\section{TFTP}

% Short description/overview of module functions
This module provides support for Trivial File Transfer Protocol (TFTP).
The support includes client and server implementation, both of them can be active at the same time.

Flows must be split up into TFTP blocks on the sender side, and reassembled from block len
on the receiving side. Please note that a block whose size is less than the block size indicates
the end of the transfer. 

To indicate the end of a transfer where the content is aligned with the block size, an additional  
transmission of zero bytes must follow the flow.

Function pico$\_$tftp$\_$listen must be used to start the server with a proper callback that should be provided by the user. To reject a request received by the server the server callback must call pico$\_$tftp$\_$reject$\_$request.

In order to start transmission or reception of files a session handler must be obtained with a call to pico$\_$tftp$\_$session$\_$setup. The created session may take advantage of the Extenxed Options of the TFTP protocol invoking pico$\_$tftp$\_$set$\_$option before starting using it.

Real file transaction is started using the functions pico$\_$tftp$\_$start$\_$tx and pico$\_$tftp$\_$start$\_$rx; both require a callback that must be provided by the user to handle single chunks of the transmission. The transmitter callback must use pico$\_$tftp$\_$send function to send each block of data.

In case of problem the session can be aborted (and an error message is sent to the remote side) using pico$\_$tftp$\_$abort.

When a transfer is complete the session became invalid and must not be used any more.

\subsection*{Application driven interface}

In some use case is preferable to have an application driven behaviour. The API provide 5 specific functions to use TFTP in this scenario.

The way to obtain a session handler suited for this purpose is an invocation to the function pico$\_$tftp$\_$app$\_$setup. The synchro variable passed to this function will play a key role during the management of the transfer.

As usual the section can be instructed to use Extended Options using pico$\_$tftp$\_$set$\_$option before starting the file transfer.

Once the session is created, the application can start receiving a file with a call to the function pico$\_$tftp$\_$app$\_$start$\_$rx or, if needs to transmit, invoking  pico$\_$tftp$\_$app$\_$start$\_$tx.

After the file transfer is started the user is allowed to perform data handling only when the synchro variable associated with the session is not 0. It is set to 0 after calling pico$\_$tftp$\_$app$\_$setup. A value that differ to 0 means that a single chunk is ready to be handled.

Single chunk of data are received using pico$\_$tftp$\_$get and transmitted with the use of the function pico$\_$tftp$\_$put.

Once the file transfer ends, both for completion or in case of error, the session is no more valid.


\subsection{pico\_tftp\_listen}

\subsubsection*{Description}
Start up a TFTP server listening for GET/PUT requests on the given port.
The function pointer passed as callback in the \texttt{cb} argument will be invoked upon a new
transfer request received from the network, and the call will pass the information about:
\begin{itemize}[noitemsep]
\item The address of the remote peer asking for a transfer
\item The remote port of the peer
\item The type of transfer requested, via the \texttt{opcode} parameter being either \texttt{PICO$\_$TFTP$\_$RRQ} or \texttt{PICO$\_$TFTP$\_$WRQ}, for get or put requests respectively.
\end{itemize}

\subsubsection*{Function prototype}
\begin{verbatim}
int pico_tftp_listen(uint16_t family, void (*cb)(union pico_address *addr,
                uint16_t port, uint16_t opcode, char *filename, int32_t len));
\end{verbatim}

\subsubsection*{Parameters}
\begin{itemize}[noitemsep]
\item \texttt{family} - The chosen socket family. Accepted values are \texttt{PICO$\_$PROTO$\_$IPV4} for IPv4 and \texttt{PICO$\_$PROTO$\_$IPV6} for IPv6.
\item \texttt{cb} - a pointer to the callback function, defined by the user, that will be called upon a new transfer request.
\end{itemize}

\subsubsection*{Return value}
This function returns 0 if succeeds or -1 in case of errors (pico$\_$err is set accordingly).

\subsubsection*{Example}
\begin{verbatim}
/* Example of a TFTP listening service callback */

void tftp_listen_cb(union pico_address *addr, uint16_t port,
                    uint16_t opcode, char *filename, int32_t len)
{
    struct note_t *note;
    struct pico_tftp_session *session;

    printf("TFTP listen callback (BASIC) from remote port %" PRIu16 ".\n",
           short_be(port));
    if (opcode == PICO_TFTP_RRQ) {
        printf("Received TFTP get request for %s\n", filename);
        note = transfer_prepare(&session, 't', filename, addr, family);
        start_tx(session, filename, port, cb_tftp_tx, note);
    } else if (opcode == PICO_TFTP_WRQ) {
        printf("Received TFTP put request for %s\n", filename);
        note = transfer_prepare(&session, 'r', filename, addr, family);
        start_rx(session, filename, port, cb_tftp_rx, note);
    }
}

// Code fragment to demostrate the use of pico_tftp_listen:
if (!is_server_enabled) {
    pico_tftp_listen(PICO_PROTO_IPV4, (commands->operation == 'S') ?
                     tftp_listen_cb_opt : tftp_listen_cb);
    is_server_enabled = 1;
}
\end{verbatim}


\subsection{pico\_tftp\_reject\_request}

\subsection*{Description}
This message is used in listen callback to reject a request with an error message.
\subsection*{Function prototype}
\begin{verbatim}
int pico_tftp_reject_request(union pico_address *addr, uint16_t port,
                             uint16_t error_code, const char *error_message);
\end{verbatim}

\subsection*{Parameters}
\begin{itemize}[noitemsep]
\item \texttt{addr} - The address of the remote peer; it must match the address where the request came from.
\item \texttt{port} - The port on the remote peer; it must match the address where the request came from.
\item \texttt{error$\_$code} - Error reason, possible values are:

\begin{tabular}{ll}
TFTP$\_$ERR$\_$UNDEF & Not defined, see error message (if any) \\
TFTP$\_$ERR$\_$ENOENT & File not found \\
TFTP$\_$ERR$\_$EACC & Access violation \\
TFTP$\_$ERR$\_$EXCEEDED & Disk full or allocation exceeded \\
TFTP$\_$ERR$\_$EILL & Illegal TFTP operation \\
TFTP$\_$ERR$\_$ETID & Unknown transfer ID \\
TFTP$\_$ERR$\_$EEXIST & File already exists \\
TFTP$\_$ERR$\_$EUSR & No such user \\
TFTP$\_$ERR$\_$EOPT & Option negotiation \\
\end{tabular}
\item \texttt{message} - Text message to attach.
\end{itemize}

\subsubsection*{Return value}
This function returns 0 if succeeds or -1 in case of errors (pico$\_$err is set accordingly).

\subsection*{Example}
\begin{verbatim}
void tftp_listen_cb_opt(union pico_address *addr, uint16_t port,
                        uint16_t opcode, char *filename, int32_t len)
{
    struct note_t *note;
    struct pico_tftp_session *session;
    int options;
    uint8_t timeout;
    int32_t filesize;
    int ret;

    printf("TFTP listen callback (OPTIONS) from remote port %" PRIu16 ".\n",
           short_be(port));
    /* declare the options we want to support */
    ret = pico_tftp_parse_request_args(filename, len, &options,
                                       &timeout, &filesize);
    if (ret)
        pico_tftp_reject_request(addr, port, TFTP_ERR_EOPT,
                                 "Malformed request");

    if (opcode == PICO_TFTP_RRQ) {
        printf("Received TFTP get request for %s\n", filename);
        note = transfer_prepare(&session, 'T', filename, addr, family);

        if (options & PICO_TFTP_OPTION_TIME)
            pico_tftp_set_option(session, PICO_TFTP_OPTION_TIME, timeout);
        if (options & PICO_TFTP_OPTION_FILE) {
            ret = get_filesize(filename);
            if (ret < 0) {
                pico_tftp_reject_request(addr, port, TFTP_ERR_ENOENT,
                                         "File not found");
                return;
            }
            pico_tftp_set_option(session, PICO_TFTP_OPTION_FILE, ret);
        }

        start_tx(session, filename, port, cb_tftp_tx_opt, note);
    } else { /* opcode == PICO_TFTP_WRQ */
        printf("Received TFTP put request for %s\n", filename);

        note = transfer_prepare(&session, 'R', filename, addr, family);
        if (options & PICO_TFTP_OPTION_TIME)
            pico_tftp_set_option(session, PICO_TFTP_OPTION_TIME, timeout);
        if (options & PICO_TFTP_OPTION_FILE)
            pico_tftp_set_option(session, PICO_TFTP_OPTION_FILE, filesize);

        start_rx(session, filename, port, cb_tftp_rx_opt, note);
    }
}
\end{verbatim}


\subsection{pico\_tftp\_session\_setup}

\subsubsection*{Description}
Obtain a session handler to use for the next file transfer with a remote location.

\subsubsection*{Function prototype}
\begin{verbatim}
struct pico_tftp_session * pico_tftp_session_setup(union pico_address *a,
                                                   uint16_t family);
\end{verbatim}

\subsubsection*{Parameters}
\begin{itemize}[noitemsep]
\item \texttt{a} - The address of the peer to be contacted. In case of a solicited transfer, it must match the address where the request came from.
\item \texttt{family} - The chosen socket family. Accepted values are \texttt{PICO$\_$PROTO$\_$IPV4} for IPv4 and \texttt{PICO$\_$PROTO$\_$IPV6} for IPv6.
\end{itemize}

\subsubsection*{Return value}
In case of success a session handler is returned. In case of failure, NULL is returned and pico$\_$err is set accordingly.

\subsubsection*{Example}
\begin{verbatim}
struct pico_tftp_session * make_session_or_die(union pico_address *addr,
                                               uint16_t family)
{
    struct pico_tftp_session * session;

    session = pico_tftp_session_setup(addr, family);
    if (!session) {
        fprintf(stderr, "TFTP: Error in session setup\n");
        exit(3);
    }
    return session;
}
\end{verbatim}


\subsection{pico\_tftp\_set\_option}

\subsection*{Description}
This function is used to require the use of Extended Options for TFTP transfer associate to a session according to RFC 2347 and RFC 2349. It should be used before the invocation of pico$\_$tftp$\_$start$\_$rx or pico$\_$tftp$\_$start$\_$tx unless the setting is related to the timeout.
In order to require Transfer size Option PICO$\_$TFTP$\_$OPTION$\_$FILE must be used and its value set to the file size in case of a Write Request or to 0 in case of a Read Request.
To require to adopt a specific fixed value for the timeout PICO$\_$TFTP$\_$OPTION$\_$TIME must be used with a value ranging between 1 and 255. If this option is set to a value of 0 (or not used at all) an adaptive timeout algorithm will take care of the retransmissions.

\subsection*{Function prototype}
\begin{verbatim}
int pico_tftp_set_option(struct pico_tftp_session *session,
                         uint8_t type, int32_t value);
\end{verbatim}

\subsection*{Parameters}
\begin{itemize}[noitemsep]
\item \texttt{session} - Section handler to use for the file transfer.
\item \texttt{type} - Option to set; accepted values are PICO$\_$TFTP$\_$OPTION$\_$FILE for Transfer size Option or PICO$\_$TFTP$\_$OPTION$\_$TIME for Timeout interval Option.
\item \texttt{value} - Option value to send.
\end{itemize}

\subsubsection*{Return value}
This function returns 0 if succeeds or -1 in case of errors (pico$\_$err is set accordingly).

\subsection*{Example}
\begin{verbatim}
filesize = get_filesize(commands->filename);
if (filesize < 0) {
    fprintf(stderr, "TFTP: unable to read size of file %s\n",
            commands->filename);
    exit(3);
}
pico_tftp_set_option(session, PICO_TFTP_OPTION_FILE, filesize);
start_tx(session, commands->filename, short_be(PICO_TFTP_PORT),
         cb_tftp_tx_opt, note);
\end{verbatim}


\subsection{pico\_tftp\_get\_option}

\subsection*{Description}
This function is used to retrieve the values of Extended Options that has been set to a session according to RFC 2347 and RFC 2349.
In order to ask Transfer size Option value PICO$\_$TFTP$\_$OPTION$\_$FILE must be used; it may be used for example for example in receiver callback for calculation of remaining bytes to be received to complete the current transfer.
To query the timeout PICO$\_$TFTP$\_$OPTION$\_$TIME must be used; a value ranging between 1 and 255 will be returned in the value parameter if the fixed interval is in place. If the call return -1 and pico$\_$err is set to PICO$\_$ERR$\_$ENOENT the adaptive timeout algorithm is running.

\subsection*{Function prototype}
\begin{verbatim}
int pico_tftp_set_option(struct pico_tftp_session *session,
                         uint8_t type, int32_t *value);
\end{verbatim}

\subsection*{Parameters}
\begin{itemize}[noitemsep]
\item \texttt{session} - Section handler to use for the file transfer.
\item \texttt{type} - Option to query; accepted values are PICO$\_$TFTP$\_$OPTION$\_$FILE for Transfer size Option or PICO$\_$TFTP$\_$OPTION$\_$TIME for Timeout interval Option.
\item \texttt{value} - Pointer to an integer variable where to store the value.
\end{itemize}

\subsubsection*{Return value}
This function returns 0 if succeeds or -1 in case of errors (pico$\_$err is set accordingly).

\subsection*{Example}
\begin{verbatim}
int cb_tftp_tx_opt(struct pico_tftp_session *session, uint16_t event,
                   uint8_t *block, int32_t len, void *arg)
{
    int ret;
    int32_t filesize;

    if (event == PICO_TFTP_EV_OPT) {
        ret = pico_tftp_get_option(session, PICO_TFTP_OPTION_FILE, &filesize);
        if (ret)
            printf("TFTP: Option filesize is not used\n");
        else
            printf("TFTP: We expect to transmit %" PRId32 " bytes\n",
                   filesize);
        event = PICO_TFTP_EV_OK;
    }

    return cb_tftp_tx(session, event, block, len, arg);
}
\end{verbatim}


\subsection{pico\_tftp\_parse\_request\_args}

\subsection*{Description}
This function is used to extract Extension Options eventually present in Read or Write request (in the listen callback) or in Option ACKnowledge messages (in transmitter or receiver callback when event is equal to PICO$\_$TFTP$\_$EV$\_$OPT).
Note that timeout and filesize are modified only if the corresponding option is found in the received message.

\subsection*{Function prototype}
\begin{verbatim}
int pico_tftp_parse_request_args(char *args, int32_t len, int *options,
                                 uint8_t *timeout, int32_t *filesize);
\end{verbatim}

\subsection*{Parameters}
\begin{itemize}[noitemsep]
\item \texttt{args} - Pointer to the buffer containing the arguments: filename for listen callback and block for rx or tx callback.
\item \texttt{len} - Length of the buffer containing the arguments; same value of the len parameter in callbacks.
\item \texttt{options} - Pointer to the variable that will contain the set of options found. Presence of single options can be then verified anding it with PICO$\_$TFTP$\_$OPTION$\_$FILE or PICO$\_$TFTP$\_$OPTION$\_$TIME.
\item \texttt{timeout} - Pointer to the variable that will contain the timeout value (if present in the options).
\item \texttt{filesize} - Pointer to the variable that will contain the filesize value (if present in the options)..
\end{itemize}

\subsubsection*{Return value}
This function returns 0 if succeeds or -1 in case of errors (pico$\_$err is set accordingly).

\subsection*{Example}
\begin{verbatim}
void tftp_listen_cb_opt(union pico_address *addr, uint16_t port,
                        uint16_t opcode, char *filename, int32_t len)
{
    struct note_t *note;
    struct pico_tftp_session *session;
    int options;
    uint8_t timeout;
    int32_t filesize;
    int ret;

    printf("TFTP listen callback (OPTIONS) from remote port %" PRIu16 ".\n",
            short_be(port));
    /* declare the options we want to support */
    ret = pico_tftp_parse_request_args(filename, len, &options,
                                       &timeout, &filesize);
    if (ret)
        pico_tftp_reject_request(addr, port, TFTP_ERR_EOPT,
                                 "Malformed request");

    if (opcode == PICO_TFTP_RRQ) {
        printf("Received TFTP get request for %s\n", filename);
        note = transfer_prepare(&session, 'T', filename, addr, family);

        if (options & PICO_TFTP_OPTION_TIME)
            pico_tftp_set_option(session, PICO_TFTP_OPTION_TIME, timeout);
        if (options & PICO_TFTP_OPTION_FILE) {
            ret = get_filesize(filename);
            if (ret < 0) {
                pico_tftp_reject_request(addr, port, TFTP_ERR_ENOENT,
                "File not found");
                return;
            }
            pico_tftp_set_option(session, PICO_TFTP_OPTION_FILE, ret);
        }

        start_tx(session, filename, port, cb_tftp_tx_opt, note);
    } else { /* opcode == PICO_TFTP_WRQ */
        printf("Received TFTP put request for %s\n", filename);

        note = transfer_prepare(&session, 'R', filename, addr, family);
        if (options & PICO_TFTP_OPTION_TIME)
            pico_tftp_set_option(session, PICO_TFTP_OPTION_TIME, timeout);
        if (options & PICO_TFTP_OPTION_FILE)
            pico_tftp_set_option(session, PICO_TFTP_OPTION_FILE, filesize);

        start_rx(session, filename, port, cb_tftp_rx_opt, note);
    }
}
\end{verbatim}


\subsection{pico\_tftp\_start\_tx}

\subsubsection*{Description}
Start a TFTP transfer. The action can be unsolicited (client PUT operation) or solicited (server responding to a GET request).
In either case, the transfer will happen one block at a time, and the callback provided by the user will be called to notify the acknowledgement for the successful of each transfer, transfer of the last block, reception of an option acknowledge message (client mode) or whenever an error occurs. Any error during the TFTP transfer will cancel the transfer itself.
The possible values for the \texttt{event} variable in callback are:
\begin{itemize}[noitemsep]
\item \texttt{PICO$\_$TFTP$\_$EV$\_$OK} Time to send another chunk of data.
\item \texttt{PICO$\_$TFTP$\_$EV$\_$OPT} Option acknowledge has been received.
\item \texttt{PICO$\_$TFTP$\_$EV$\_$ERR$\_$PEER} An error has occurred remotely.
\item \texttt{PICO$\_$TFTP$\_$EV$\_$ERR$\_$LOCAL} An internal error has occurred.
\end{itemize}

\subsubsection*{Function prototype}
\begin{verbatim}
int pico_tftp_start_tx(struct pico_tftp_session *session, uint16_t port,
        const char *filename,
        int (*user_cb)(struct pico_tftp_session *session, uint16_t event,
                       uint8_t *block, int32_t len, void *arg),
        void *arg);
\end{verbatim}

\subsubsection*{Parameters}
\begin{itemize}[noitemsep]
\item \texttt{session} - Session handler to use for the file transfer.
\item \texttt{port} - The port on the remote peer.
\item \texttt{filename} - The name of the file to be transferred. In case of solicited transfer, it must match the filename provided during the request.
\item \texttt{user$\_$cb} - The callback provided by the user to be called upon each block transfer, option acknowledge or in case of error.
\item \texttt{arg} - The pointer is sent as argument to the callback.
\end{itemize}

\subsubsection*{Return value}
This function returns 0 if succeeds or -1 in case of errors (pico$\_$err is set accordingly).

\subsubsection*{Example}
\begin{verbatim}
void start_tx(struct pico_tftp_session *session,
    const char *filename, uint16_t port,
    int (*tx_callback)(struct pico_tftp_session *session, uint16_t err,
                       uint8_t *block, int32_t len, void *arg),
    struct note_t *note)
{
    if (pico_tftp_start_tx(session, port, filename, tx_callback, note)) {
        fprintf(stderr, "TFTP: Error in initialization\n");
        exit(1);
    }
}
\end{verbatim}


\subsection{pico\_tftp\_send}
\subsubsection*{Description}
Send the next block during an active TFTP transfer. This is ideally called every time the user callback is triggered by the protocol, indicating that the transfer of the last block has been acknowledged. The user should not call this function unless it's solicited by the protocol during an active transmit session.

\subsubsection*{Function prototype}
\begin{verbatim}
int32_t pico_tftp_send(struct pico_tftp_session *session,
                       const uint8_t *data, int32_t len);
\end{verbatim}

\subsubsection*{Parameters}
\begin{itemize}[noitemsep]
\item \texttt{session} - the session handler to use for the file transfer.
\item \texttt{data} - the content of the block to be transferred.
\item \texttt{len} - the size of the buffer being transmitted. If $<$ \texttt{BLOCKSIZE}, the transfer is concluded. In order to terminate a transfer where the content is aligned to \texttt{BLOCKSIZE}, a zero-sized \texttt{pico$\_$tftp$\_$send} must be called at the end of the transfer.
\end{itemize}

\subsubsection*{Return value}
In case of success, the number of bytes transmitted is returned. In case of failure, -1 is returned and pico$\_$err is set accordingly.

\subsubsection*{Example}
\begin{verbatim}
int cb_tftp_tx(struct pico_tftp_session *session, uint16_t event,
               uint8_t *block, int32_t len, void *arg)
{
    struct note_t *note = (struct note_t *) arg;

    if (event != PICO_TFTP_EV_OK) {
        fprintf(stderr, "TFTP: Error %" PRIu16 ": %s\n", event, block);
        exit(1);
    }

    len = read(note->fd, tftp_txbuf, PICO_TFTP_PAYLOAD_SIZE);

    if (len >= 0) {
        note->filesize += len;
        pico_tftp_send(session, tftp_txbuf, len);
        if (len < PICO_TFTP_PAYLOAD_SIZE) {
            printf("TFTP: file %s (%" PRId32
                   " bytes) TX transfer complete!\n",
                   note->filename, note->filesize);
            close(note->fd);
            del_note(note);
        }
    } else {
        perror("read");
        fprintf(stderr,
                "Filesystem error reading file %s,"
                " cancelling current transfer\n", note->filename);
        pico_tftp_abort(session, TFTP_ERR_EACC, "Error on read");
        del_note(note);
    }

    if (!clipboard)
        pico_timer_add(3000, deferred_exit, NULL);

    return len;
}
\end{verbatim}


\subsection{pico\_tftp\_start\_rx}

\subsubsection*{Description}
Start a TFTP transfer. The action can be unsolicited (client GET operation) or solicited (server responding to a PUT request).
In either case, the transfer will happen one block at a time, and the callback provided by the user will be called upon successful transfer of a block, whose content can be directly accessed via the \texttt{block} field, reception of an option acknowledge messagge (client mode) or whenever an error occurs.
The possible values for the \texttt{event} variable in callback are:
\begin{itemize}[noitemsep]
\item \texttt{PICO$\_$TFTP$\_$EV$\_$OK} Previously sent block has been acknowledge.
\item \texttt{PICO$\_$TFTP$\_$EV$\_$OPT} Option acknowledge has been received.
\item \texttt{PICO$\_$TFTP$\_$EV$\_$ERR$\_$PEER} An error has occurrend remotely.
\item \texttt{PICO$\_$TFTP$\_$EV$\_$ERR$\_$LOCAL} An internal error has occurred.
\end{itemize}

\subsubsection*{Function prototype}
\begin{verbatim}
int pico_tftp_start_rx(struct pico_tftp_session *session, uint16_t port,
        const char *filename,
        int (*user_cb)(struct pico_tftp_session *session, uint16_t event,
                       uint8_t *block, int32_t len, void *arg),
        void *arg);
\end{verbatim}

\subsubsection*{Parameters}
\begin{itemize}[noitemsep]
\item \texttt{session} - the session handler to use for the file transfer.
\item \texttt{port} - The port on the remote peer.
\item \texttt{filename} - The name of the file to be transfered. In case of solicited transfer, it must match the filename provided during the request.
\item \texttt{user$\_$cb} - The callback provided by the user to be called upon each block transfer, option acknowledge or in case of error. This is the callback where the incoming data is processed. When len is less than the block size, the transfer is over.
\item \texttt{arg} - The pointer sent as argument to the callback.
\end{itemize}

\subsubsection*{Return value}
This function returns 0 if succeeds or -1 in case of errors (pico$\_$err is set accordingly).

\subsubsection*{Example}
\begin{verbatim}
void start_rx(struct pico_tftp_session *session,
          const char *filename, uint16_t port,
          int (*rx_callback)(struct pico_tftp_session *session, uint16_t err,
                             uint8_t *block, int32_t len, void *arg),
          struct note_t *note)
{
    if (pico_tftp_start_rx(session, port, filename, rx_callback, note)) {
        fprintf(stderr, "TFTP: Error in initialization\n");
        exit(1);
    }
}
\end{verbatim}


\subsection{pico\_tftp\_get\_file\_size}

\subsection*{Description}
This function is used to retrieve the file size (if transmitted by the remote or set as session option). It is equivalent to a call to pico$\_$tftp$\_$get$\_$option(session, PICO$\_$TFTP$\_$OPTION$\_$FILE, $\&$file$\_$size);

\subsection*{Function prototype}
\begin{verbatim}
int pico_tftp_get_file_size(struct pico_tftp_session *session,
                            int32_t *file_size);
\end{verbatim}

\subsection*{Parameters}
\begin{itemize}[noitemsep]
\item \texttt{session} - Section handler to use for the file transfer.
\item \texttt{file$\_$size} - Pointer to an integer variable where to store the value.
\end{itemize}

\subsubsection*{Return value}
This function returns 0 if succeeds or -1 in case of errors (pico$\_$err is set accordingly).

\subsection*{Example}
\begin{verbatim}
ret = pico_tftp_get_file_size(session, &file_size);
if (ret)
    printf("Information about file size has not been received"\n);
\end{verbatim}


\subsection{pico\_tftp\_abort}
\subsubsection*{Description}
When called this function aborts associated ongoing transmission and notifying the other endpoint with a proper error message. After a call to this function the session is closed automatically.

\subsubsection*{Function prototype}
\begin{verbatim}
int pico_tftp_abort(struct pico_tftp_session *session,
                    uint16_t error, const char *reason);
\end{verbatim}

\subsubsection*{Parameters}
\begin{itemize}[noitemsep]
\item \texttt{session} - the session handler related to the session to abort.
\item \texttt{error} - Error reason code, possible values are:

\begin{tabular}{ll}
TFTP$\_$ERR$\_$UNDEF & Not defined, see error message (if any) \\
TFTP$\_$ERR$\_$ENOENT & File not found \\
TFTP$\_$ERR$\_$EACC & Access violation \\
TFTP$\_$ERR$\_$EXCEEDED & Disk full or allocation exceeded \\
TFTP$\_$ERR$\_$EILL & Illegal TFTP operation \\
TFTP$\_$ERR$\_$ETID & Unknown transfer ID \\
TFTP$\_$ERR$\_$EEXIST & File already exists \\
TFTP$\_$ERR$\_$EUSR & No such user \\
TFTP$\_$ERR$\_$EOPT & Option negotiation \\
\end{tabular}
\item \texttt{reason} - Text message to attach.
\end{itemize}

\subsubsection*{Return value}
This function returns 0 if succeeds or -1 in case of errors (pico$\_$err is set accordingly).

\subsection*{Example}
\begin{verbatim}
int cb_tftp_rx(struct pico_tftp_session *session, uint16_t event,
               uint8_t *block, int32_t len, void *arg)
{
    struct note_t *note = (struct note_t *) arg;
    int ret;

    if (event != PICO_TFTP_EV_OK) {
        fprintf(stderr, "TFTP: Error %" PRIu16 ": %s\n", event, block);
        exit(1);
    }

    note->filesize += len;
    if (write(note->fd, block, len) < 0) {
        perror("write");
        fprintf(stderr, "Filesystem error writing file %s,"
                " cancelling current transfer\n", note->filename);
        pico_tftp_abort(session, TFTP_ERR_EACC, "Error on write");
        del_note(note);
    } else {
        if (len != PICO_TFTP_PAYLOAD_SIZE) {
            printf("TFTP: file %s (%" PRId32
                   " bytes) RX transfer complete!\n",
                   note->filename, note->filesize);
            close(note->fd);
            del_note(note);
        }
    }

    if (!clipboard)
        pico_timer_add(3000, deferred_exit, NULL);

    return len;
}
\end{verbatim}


\subsection{pico\_tftp\_close\_server}

\subsection*{Description}
This function is used to shutdown the TFTP server.

\subsection*{Function prototype}
\begin{verbatim}
int pico_tftp_close_server(void);
\end{verbatim}

\subsubsection*{Return value}
This function returns 0 if succeeds or -1 in case of errors (pico$\_$err is set accordingly).

\subsection*{Example}
\begin{verbatim}
ret = pico_tftp_close_server();
if (ret)
    printf(stderr, "Failure shutting down the server\n");
\end{verbatim}


\subsection{pico\_tftp\_app\_setup}

\subsection*{Description}
Obtain a session handler to use for the next file transfer with a remote location in application driven mode.

\subsection*{Function prototype}
\begin{verbatim}
struct pico_tftp_session * pico_tftp_app_setup(union pico_address *a,
        uint16_t port, uint16_t family, int *synchro);
\end{verbatim}

\subsection*{Parameters}
\begin{itemize}[noitemsep]
\item \texttt{a} - The address of the peer to be contacted. In case of a solicited transfer, it must match the address where the request came from.
\item \texttt{port} - The port on the remote peer.
\item \texttt{family} - The chosen socket family. Accepted values are \texttt{PICO$\_$PROTO$\_$IPV4} for IPv4 and \texttt{PICO$\_$PROTO$\_$IPV6} for IPv6.
\item \texttt{synchro} - Variable to handle the synchronization.
\end{itemize}

\subsubsection*{Return value}
In case of success a session handler is returned. In case of failure, NULL is returned and pico$\_$err is set accordingly.

\subsection*{Example}
\begin{verbatim}
session = pico_tftp_app_setup(&server_address, short_be(PICO_TFTP_PORT),
                              PICO_PROTO_IPV4, &synchro);
if (!session) {
    fprintf(stderr, "Error in pico_tftp_app_setup\n");
    exit(1);
}
\end{verbatim}


\subsection{pico\_tftp\_app\_start\_rx}

\subsection*{Description}
Application driven function used to request to read a remote file. The transfer will happen one block at a time using pico$\_$tftp$\_$app$\_$get.

\subsection*{Function prototype}
\begin{verbatim}
int pico_tftp_app_start_rx(struct pico_tftp_session *session,
                           const char *filename);
\end{verbatim}

\subsection*{Parameters}
\begin{itemize}[noitemsep]
\item \texttt{session} - Session handler to use for the file transfer.
\item \texttt{filename} - The name of the file to be received.
\end{itemize}

\subsubsection*{Return value}
This function returns 0 if succeeds or -1 in case of errors (pico$\_$err is set accordingly).

\subsection*{Example}
\begin{verbatim}
printf("Start receiving file %s with options set to %d\n", filename, options);

if (options) {
    ret = pico_tftp_set_option(session, PICO_TFTP_OPTION_FILE, 0);
    if (ret) {
        fprintf(stderr, "Error in pico_tftp_set_option\n");
        exit(1);
    }
}

ret = pico_tftp_app_start_rx(session, filename);
if (ret) {
    fprintf(stderr, "Error in pico_tftp_app_start_rx\n");
    exit(1);
}
\end{verbatim}


\subsection{pico\_tftp\_app\_start\_tx}

\subsection*{Description}
Application driven function used to request to write a remote file. The transfer will happen one block at a time using pico$\_$tftp$\_$app$\_$put.

\subsection*{Function prototype}
\begin{verbatim}
int pico_tftp_app_start_tx(struct pico_tftp_session *session,
                           const char *filename);
\end{verbatim}

\subsection*{Parameters}
\begin{itemize}[noitemsep]
\item \texttt{session} - Session handler to use for the file transfer.
\item \texttt{filename} - The name of the file to be sent.
\end{itemize}

\subsubsection*{Return value}
This function returns 0 if succeeds or -1 in case of errors (pico$\_$err is set accordingly).

\subsection*{Example}
\begin{verbatim}
printf("Start sending file %s with options set to %d\n", filename, options);

if (options) {
    ret = get_filesize(filename);
    if (ret < 0) {
        fprintf(stderr, "Error in get_filesize\n");
        exit(1);
    }

    ret = pico_tftp_set_option(session, PICO_TFTP_OPTION_FILE, ret);
    if (ret) {
        fprintf(stderr, "Error in pico_tftp_set_option\n");
        exit(1);
    }
}

ret = pico_tftp_app_start_tx(session, filename);
if (ret) {
    fprintf(stderr, "Error in pico_tftp_app_start_rx\n");
    exit(1);
}
\end{verbatim}


\subsection{pico\_tftp\_get}

\subsection*{Description}
Read the next block during an active TFTP transfer. The len field must always be equal to PICO$\_$TFTP$\_$PAYLOAD$\_$SIZE. Once the file has been sent or after an error the session is no more valid.

\subsection*{Function prototype}
\begin{verbatim}
int32_t pico_tftp_get(struct pico_tftp_session *session,
                      uint8_t *data, int32_t len);
\end{verbatim}

\subsection*{Parameters}
\begin{itemize}[noitemsep]
\item \texttt{session} - Session handler to use for the file transfer.
\item \texttt{data} - Buffer where to store the acquired payload.
\item \texttt{len} - Length of the buffer size to receive; it is equal to the fixed chunk size.
\end{itemize}

\subsubsection*{Return value}
This function returns the number of received bytes of payload (0 included) if succeeds. In case of error a negative number is returned.

\subsubsection*{Errors}
\begin{itemize}[noitemsep]
\item \texttt{-1} At least one of the passed arguments are invalid.
\item \texttt{-PICO$\_$TFTP$\_$EV$\_$ERR$\_$PEER} Remote failure.
\item \texttt{-PICO$\_$TFTP$\_$EV$\_$ERR$\_$LOCAL} Local failure.
\end{itemize}

\subsection*{Example}
\begin{verbatim}
for(;left; left -= countdown) {
    usleep(2000); //PICO_IDLE();
    pico_stack_tick();
    if (countdown)
        continue;

    if (*synchro) {
        len = pico_tftp_get(session, buf, PICO_TFTP_PAYLOAD_SIZE);
        if (len < 0) {
            fprintf(stderr, "Failure in pico_tftp_get\n");
            close(fd);
            countdown = 1;
            continue;
        }
        ret = write(fd, buf, len);
        if (ret < 0) {
            fprintf(stderr, "Error in write\n");
            pico_tftp_abort(session, TFTP_ERR_EXCEEDED, "File write error");
            close(fd);
            countdown = 1;
            continue;
        }
        printf("Written %" PRId32 " bytes to file (synchro=%d)\n",
               len, *synchro);

        if (len != PICO_TFTP_PAYLOAD_SIZE) {
            close(fd);
            printf("Transfer complete!\n");
            countdown = 1;
        }
    }
}
\end{verbatim}


\subsection{pico\_tftp\_put}

\subsection*{Description}
Send the next block during an active TFTP transfer. The len field, with the exception of last invocation must always be equal to PICO$\_$TFTP$\_$PAYLOAD$\_$SIZE. Once the file has been sent or after an error the session is no more valid.

\subsection*{Function prototype}
\begin{verbatim}
int32_t pico_tftp_put(struct pico_tftp_session *session,
                      uint8_t *data, int32_t len);
\end{verbatim}

\subsection*{Parameters}
\begin{itemize}[noitemsep]
\item \texttt{session} - Session handler to use for the file transfer.
\item \texttt{data} - Pointer to the data to be transmitted.
\item \texttt{len} - Length of the buffer size to transmit; last chunk must be $<$ of the maximum buffer size (0 if file size was a multiple of maximum buffer size).

\end{itemize}

\subsubsection*{Return value}
This function returns the number of transmitted payload data (len) if succeeds. In case of error a negative number is returned.

\subsubsection*{Errors}
\begin{itemize}[noitemsep]
\item \texttt{-1} At least one of the passed arguments are invalid.
\item \texttt{-PICO$\_$TFTP$\_$EV$\_$ERR$\_$PEER} Remote failure.
\item \texttt{-PICO$\_$TFTP$\_$EV$\_$ERR$\_$LOCAL} Local failure.
\end{itemize}

\subsection*{Example}
\begin{verbatim}
for(;left; left -= countdown) {
    usleep(2000); //PICO_IDLE();
    pico_stack_tick();
    if (countdown)
        continue;

    if (*synchro) {
        ret = read(fd, buf, PICO_TFTP_PAYLOAD_SIZE);
        if (ret < 0) {
            fprintf(stderr, "Error in read\n");
            pico_tftp_abort(session, TFTP_ERR_EACC, "File read error");
            close(fd);
            countdown = 1;
            continue;
        }
        printf("Read %" PRId32 " bytes from file (synchro=%d)\n",
               len, *synchro);

        len = pico_tftp_put(session, buf, ret);
        if (len < 0) {
            fprintf(stderr, "Failure in pico_tftp_put\n");
            close(fd);
            countdown = 1;
            continue;
        }

        if (len != PICO_TFTP_PAYLOAD_SIZE) {
            close(fd);
            printf("Transfer complete!\n");
            countdown = 1;
        }
    }
}
\end{verbatim}
