Subversion Repositories HomeAutomation

Rev

Details | Last modification | View Log | SVN | RSS feed

Rev Author Line No. Line
363 johboh 1
/*********************************************************************
2
 *
3
 *                  TFTP Client module for Microchip TCP/IP Stack
4
 *
5
 *********************************************************************
6
 * FileName:        TFTPc.c
7
 * Dependencies:    TFTPc.h
8
 *                  UDP.h
9
 * Processor:       PIC18
10
 * Complier:        MCC18 v1.00.50 or higher
11
 *                  HITECH PICC-18 V8.10PL1 or higher
12
 * Company:         Microchip Technology, Inc.
13
 *
14
 * Software License Agreement
15
 *
16
 * The software supplied herewith by Microchip Technology Incorporated
17
 * (the “Company”) for its PICmicro® Microcontroller is intended and
18
 * supplied to you, the Company’s customer, for use solely and
19
 * exclusively on Microchip PICmicro Microcontroller products. The
20
 * software is owned by the Company and/or its supplier, and is
21
 * protected under applicable copyright laws. All rights are reserved.
22
 * Any use in violation of the foregoing restrictions may subject the
23
 * user to criminal sanctions under applicable laws, as well as to
24
 * civil liability for the breach of the terms and conditions of this
25
 * license.
26
 *
27
 * THIS SOFTWARE IS PROVIDED IN AN “AS IS” CONDITION. NO WARRANTIES,
28
 * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED
29
 * TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
30
 * PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE COMPANY SHALL NOT,
31
 * IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR
32
 * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
33
 *
34
 * Author               Date    Comment
35
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
36
 * Nilesh Rajbharti     8/5/03  Original        (Rev 1.0)
37
 ********************************************************************/
38
#if defined(WIN32)
39
    #include <stdio.h>
40
    #define TFTP_DEBUG
41
#endif
42
 
43
#include "..\Include\tftpc.h"
44
#include "..\Include\arptsk.h"
45
#include "..\Include\tick.h"
46
 
47
#if !defined(STACK_USE_TFTP_CLIENT)
48
    #error TFTP Client module is not enabled.
49
    #error If you do not want TFTP Client module, remove this file from your
50
    #error project to reduce your code size.
51
    #error If you do want TFTP Client module, make sure that STACK_USE_TFTP_CLIENT
52
    #error is defined in StackTsk.h file.
53
#endif
54
 
55
 
56
#define TFTP_CLIENT_PORT        65352L       // Some unique port on this
57
                                            // device.
58
#define TFTP_SERVER_PORT        (69L)
59
#define TFTP_BLOCK_SIZE         (0x200L)     // 512 bytes
60
#define TFTP_BLOCK_SIZE_MSB     (0x02)
61
 
62
typedef enum _TFTP_STATE
63
{
64
    SM_TFTP_WAIT = 0,
65
    SM_TFTP_READY,
66
    SM_TFTP_WAIT_FOR_DATA,
67
    SM_TFTP_WAIT_FOR_ACK,
68
    SM_TFTP_DUPLICATE_ACK,
69
    SM_TFTP_SEND_ACK,
70
    SM_TFTP_SEND_LAST_ACK
71
} TFTP_STATE;
72
 
73
typedef enum _TFTP_OPCODE
74
{
75
    TFTP_OPCODE_RRQ = 1,         // Get
76
    TFTP_OPCODE_WRQ,            // Put
77
    TFTP_OPCODE_DATA,           // Actual data
78
    TFTP_OPCODE_ACK,            // Ack for Get/Put
79
    TFTP_OPCODE_ERROR           // Error
80
} TFTP_OPCODE;
81
 
82
 
83
 
84
UDP_SOCKET _tftpSocket;         // TFTP Socket for TFTP server link
85
WORD _tftpError;
86
 
87
static union _MutExVar
88
{
89
    struct
90
    {
91
        NODE_INFO _hostInfo;
92
    } group1;
93
 
94
    struct
95
    {
96
        WORD_VAL _tftpBlockNumber;
97
        WORD_VAL _tftpDuplicateBlock;
98
        WORD_VAL _tftpBlockLength;
99
    } group2;
100
} MutExVar;     // Mutually Exclusive variable groups to conserve RAM.
101
 
102
 
103
static TFTP_STATE _tftpState;
104
static BYTE _tftpRetries;
105
static TICK _tftpStartTick;
106
static union
107
{
108
    struct
109
    {
110
        unsigned int bIsFlushed : 1;
111
        unsigned int bIsAcked : 1;
112
        unsigned int bIsClosed : 1;
113
        unsigned int bIsClosing : 1;
114
        unsigned int bIsReading : 1;
115
    } bits;
116
    BYTE Val;
117
} _tftpFlags;
118
 
119
 
120
// Private helper functions
121
static void _TFTPSendFileName(TFTP_OPCODE command, char *fileName);
122
static void _TFTPSendAck(WORD_VAL blockNumber);
123
 
124
// Blank out DEBUG statements if not enabled.
125
#if defined(TFTP_DEBUG)
126
    #define DEBUG(a)        a
127
#else
128
    #define DEBUG(a)
129
#endif
130
 
131
 
132
/*********************************************************************
133
 * Function:        void TFTPOpen(IP_ADDR *host)
134
 *
135
 * PreCondition:    UDP module is already initialized
136
 *                  and at least one UDP socket is available.
137
 *
138
 * Input:           host        - IP address of remote TFTP server
139
 *
140
 * Output:          None
141
 *
142
 * Side Effects:    None
143
 *
144
 * Overview:        Initiates ARP for given host and prepares
145
 *                  TFTP module for next sequence of function calls.
146
 *
147
 * Note:            Use TFTPIsOpened() to check if a connection was
148
 *                  successfully opened or not.
149
 *
150
 ********************************************************************/
151
void TFTPOpen(IP_ADDR *host)
152
{
153
    DEBUG(printf("Opening a connection..."));
154
 
155
    // Remember this address locally.
156
    MutExVar.group1._hostInfo.IPAddr.Val = host->Val;
157
 
158
    // Initiate ARP resolution.
159
    ARPResolve(&MutExVar.group1._hostInfo.IPAddr);
160
 
161
    // Wait for ARP to get resolved.
162
    _tftpState = SM_TFTP_WAIT;
163
 
164
    // Mark this as start tick to detect timeout condition.
165
    _tftpStartTick = TickGet();
166
 
167
    // Forget about all previous attempts.
168
    _tftpRetries = 1;
169
 
170
}
171
 
172
 
173
 
174
/*********************************************************************
175
 * Function:        TFTP_RESULT TFTPIsOpened(void)
176
 *
177
 * PreCondition:    TFTPOpen() is already called.
178
 *
179
 * Input:           None
180
 *
181
 * Output:          TFTP_OK if previous call to TFTPOpen is complete
182
 *
183
 *                  TFTP_TIMEOUT if remote host did not respond to
184
 *                          previous ARP request.
185
 *
186
 *                  TFTP_NOT_READY if remote has still not responded
187
 *                          and timeout has not expired.
188
 *
189
 * Side Effects:    None
190
 *
191
 * Overview:        Waits for ARP reply and opens a UDP socket
192
 *                  to perform further TFTP operations.
193
 *
194
 * Note:            Once opened, application may keep TFTP socket
195
 *                  open and future TFTP operations.
196
 *                  If TFTPClose() is called to close the connection
197
 *                  TFTPOpen() must be called again before performing
198
 *                  any other TFTP operations.
199
 ********************************************************************/
200
TFTP_RESULT TFTPIsOpened(void)
201
{
202
    switch(_tftpState)
203
    {
204
    default:
205
        DEBUG(printf("Resolving remote IP...\n"));
206
 
207
        // Check to see if adddress is resolved.
208
        if ( ARPIsResolved(&MutExVar.group1._hostInfo.IPAddr,
209
                           &MutExVar.group1._hostInfo.MACAddr) )
210
        {
211
            _tftpSocket = UDPOpen(TFTP_CLIENT_PORT,
212
                                  &MutExVar.group1._hostInfo,
213
                                  TFTP_SERVER_PORT);
214
            _tftpState = SM_TFTP_READY;
215
        }
216
        else
217
            break;
218
 
219
    case SM_TFTP_READY:
220
        // Wait for UDP to be ready.  Immediately after this user will
221
        // may TFTPGetFile or TFTPPutFile and we have to make sure that
222
        // UDP is read to transmit.  These functions do not check for
223
        // UDP to get ready.
224
        if ( UDPIsPutReady(_tftpSocket) )
225
            return TFTP_OK;
226
    }
227
 
228
    // Make sure that we do not do this forever.
229
    if ( TickGetDiff(TickGet(), _tftpStartTick) >= TFTP_ARP_TIMEOUT_VAL )
230
    {
231
        _tftpStartTick = TickGet();
232
 
233
        // Forget about all previous attempts.
234
        _tftpRetries = 1;
235
 
236
        return TFTP_TIMEOUT;
237
    }
238
 
239
    return TFTP_NOT_READY;
240
}
241
 
242
 
243
 
244
/*********************************************************************
245
 * Function:        void TFTPOpenFile(char *fileName,
246
 *                                    TFTP_FILE_MODE mode)
247
 *
248
 * PreCondition:    TFPTIsFileOpenReady() = TRUE
249
 *
250
 * Input:           fileName        - File name that is to be opened.
251
 *                  mode            - Mode of file access
252
 *                                    Must be
253
 *                                      TFTP_FILE_MODE_READ for read
254
 *                                      TFTP_FILE_MODE_WRITE for write
255
 *
256
 * Output:          None
257
 *
258
 * Side Effects:    None
259
 *
260
 * Overview:        Prepares and sends TFTP file name and mode packet.
261
 *
262
 * Note:            By default, this funciton uses "octet" or binary
263
 *                  mode of file transfer.
264
 *                  Use TFTPIsFileOpened() to check if file is
265
 *                  ready to be read or written.
266
 ********************************************************************/
267
void TFTPOpenFile(char *fileName, TFTP_FILE_MODE mode)
268
{
269
    DEBUG(printf("Opening file...\n"));
270
 
271
    // Set TFTP Server port.  If this is the first call, remotePort
272
    // must have been set by TFTPOpen().  But if caller does not do
273
    // TFTPOpen for every transfer, we must reset remote port.
274
    // Most TFTP servers accept connection TFTP port. but once
275
    // connection is established, they use other temp. port,
276
    UDPSocketInfo[_tftpSocket].remotePort = TFTP_SERVER_PORT;
277
 
278
    // Tell remote server about our intention.
279
    _TFTPSendFileName(mode, fileName);
280
 
281
    // Clear all flags.
282
    _tftpFlags.Val = 0;
283
 
284
    // Remember start tick for this operation.
285
    _tftpStartTick = TickGet();
286
 
287
    // Depending on mode of operation, remote server will respond with
288
    // specific block number.
289
    if ( mode == TFTP_FILE_MODE_READ )
290
    {
291
        // Remember that we are reading a file.
292
        _tftpFlags.bits.bIsReading = TRUE;
293
 
294
 
295
        // For read operation, server would respond with data block of 1.
296
        MutExVar.group2._tftpBlockNumber.Val = 1;
297
 
298
        // Next packet would be the data packet.
299
        _tftpState = SM_TFTP_WAIT_FOR_DATA;
300
    }
301
 
302
    else
303
    {
304
        // Remember that we are writing a file.
305
        _tftpFlags.bits.bIsReading = FALSE;
306
 
307
        // For write operation, server would respond with data block of 0.
308
        MutExVar.group2._tftpBlockNumber.Val = 0;
309
 
310
        // Next packet would be the ACK packet.
311
        _tftpState = SM_TFTP_WAIT_FOR_ACK;
312
    }
313
 
314
 
315
}
316
 
317
 
318
 
319
 
320
/*********************************************************************
321
 * Function:        TFTP_RESULT TFTPIsFileOpened(void)
322
 *
323
 * PreCondition:    TFTPOpenFile() is called.
324
 *
325
 * Input:           None
326
 *
327
 * Output:          TFTP_OK if file is ready to be read or written
328
 *
329
 *                  TFTP_RETRY if previous attempt was timed out
330
 *                  needs to be retried.
331
 *
332
 *                  TFTP_TIMEOUT if all attempts were exhausted.
333
 *
334
 *                  TFTP_NOT_ERROR if remote server responded with
335
 *                  error
336
 *
337
 *                  TFTP_NOT_READY if file is not yet opened.
338
 *
339
 * Side Effects:    None
340
 *
341
 * Overview:        Waits for remote server response regarding
342
 *                  previous attempt to open file.
343
 *                  If no response is received within specified
344
 *                  timeout, fnction returns with TFTP_RETRY
345
 *                  and application logic must issue another
346
 *                  TFTPFileOpen().
347
 *
348
 * Note:            None
349
 ********************************************************************/
350
TFTP_RESULT TFTPIsFileOpened(void)
351
{
352
    if ( _tftpFlags.bits.bIsReading )
353
        return TFTPIsGetReady();
354
 
355
    else
356
        return TFTPIsPutReady();
357
}
358
 
359
 
360
 
361
 
362
/*********************************************************************
363
 * Function:        TFTP_RESULT TFTPIsGetReady(void)
364
 *
365
 * PreCondition:    TFTPOpenFile() is called with TFTP_FILE_MODE_READ
366
 *                  and TFTPIsFileOpened() returned with TRUE.
367
 *
368
 * Input:           None
369
 *
370
 * Output:          TFTP_OK if it there is more data byte available
371
 *                  to read
372
 *
373
 *                  TFTP_TIMEOUT if timeout occurred waiting for
374
 *                  new data.
375
 *
376
 *                  TFTP_END_OF_FILE if end of file has reached.
377
 *
378
 *                  TFTP_ERROR if remote server returned ERROR.
379
 *                      Actual error code may be read by calling
380
 *                      TFTPGetError()
381
 *
382
 *                  TFTP_NOT_READY if still waiting for new data.
383
 *
384
 * Side Effects:    None
385
 *
386
 * Overview:        Waits for data block.  If data block does not
387
 *                  arrive within specified timeout, it automatically
388
 *                  sends out ack for previous block to remind
389
 *                  server to send next data block.
390
 *                  If all attempts are exhausted, it returns with
391
 *                  TFTP_TIMEOUT.
392
 *
393
 * Note:            By default, this funciton uses "octet" or binary
394
 *                  mode of file transfer.
395
 ********************************************************************/
396
TFTP_RESULT TFTPIsGetReady(void)
397
{
398
    WORD_VAL opCode;
399
    WORD_VAL blockNumber;
400
    BOOL bTimeOut;
401
 
402
 
403
    // Check to see if timeout has occurred.
404
    bTimeOut = FALSE;
405
    if ( TickGetDiff(TickGet(), _tftpStartTick) >= TFTP_GET_TIMEOUT_VAL )
406
    {
407
        bTimeOut = TRUE;
408
        _tftpStartTick = TickGet();
409
    }
410
 
411
 
412
    switch(_tftpState)
413
    {
414
    case SM_TFTP_WAIT_FOR_DATA:
415
        // If timeout occurs in this state, it may be because, we have not
416
        // even received very first data block or some in between block.
417
        if ( bTimeOut == TRUE )
418
        {
419
            bTimeOut = FALSE;
420
 
421
            if ( _tftpRetries++ > (TFTP_MAX_RETRIES-1) )
422
            {
423
                DEBUG(printf("TFTPIsGetReady(): Timeout.\n"));
424
 
425
                // Forget about all previous attempts.
426
                _tftpRetries = 1;
427
 
428
                return TFTP_TIMEOUT;
429
            }
430
 
431
            // If we have not even received first block, ask application
432
            // retry.
433
            if ( MutExVar.group2._tftpBlockNumber.Val == 1 )
434
            {
435
                DEBUG(printf("TFTPIsGetReady(): TFTPOpen Retry.\n"));
436
                return TFTP_RETRY;
437
            }
438
            else
439
            {
440
                DEBUG(printf("TFTPIsGetReady(): ACK Retry #%d...,\n", _tftpRetries));
441
 
442
                // Block number was already incremented in last ACK attempt,
443
                // so decrement it.
444
                MutExVar.group2._tftpBlockNumber.Val--;
445
 
446
                // Do it.
447
                _tftpState = SM_TFTP_SEND_ACK;
448
                break;
449
            }
450
        }
451
 
452
        // For Read operation, server will respond with data block.
453
        if ( !UDPIsGetReady(_tftpSocket) )
454
            break;
455
 
456
        // Get opCode
457
        UDPGet(&opCode.byte.MSB);
458
        UDPGet(&opCode.byte.LSB);
459
 
460
        // Get block number.
461
        UDPGet(&blockNumber.byte.MSB);
462
        UDPGet(&blockNumber.byte.LSB);
463
 
464
        // In order to read file, this must be data with block number of 0.
465
        if ( opCode.Val == TFTP_OPCODE_DATA )
466
        {
467
            // Make sure that this is not a duplicate block.
468
            if ( MutExVar.group2._tftpBlockNumber.Val == blockNumber.Val )
469
            {
470
                // Mark that we have not acked this block.
471
                _tftpFlags.bits.bIsAcked = FALSE;
472
 
473
                // Since we have a packet, forget about previous retry count.
474
                _tftpRetries = 1;
475
 
476
                _tftpState = SM_TFTP_READY;
477
                return TFTP_OK;
478
            }
479
 
480
            // If received block has already been received, simply ack it
481
            // so that Server can "get over" it and send next block.
482
            else if ( MutExVar.group2._tftpBlockNumber.Val > blockNumber.Val )
483
            {
484
                DEBUG(printf("TFTPIsGetReady(): "\
485
                            "Duplicate block %d received - droping it...\n", \
486
                            blockNumber.Val));
487
                MutExVar.group2._tftpDuplicateBlock.Val = blockNumber.Val;
488
                _tftpState = SM_TFTP_DUPLICATE_ACK;
489
            }
490
#if defined(TFTP_DEBUG)
491
            else
492
            {
493
                DEBUG(printf("TFTPIsGetReady(): "\
494
                             "Unexpected block %d received - droping it...\n", \
495
                             blockNumber.Val));
496
            }
497
#endif
498
        }
499
        // Discard all unexpected and error blocks.
500
        UDPDiscard();
501
 
502
        // If this was an error, remember error code for later delivery.
503
        if ( opCode.Val == TFTP_OPCODE_ERROR )
504
        {
505
            _tftpError = blockNumber.Val;
506
            return TFTP_ERROR;
507
        }
508
 
509
        break;
510
 
511
    case SM_TFTP_DUPLICATE_ACK:
512
        if ( UDPIsPutReady(_tftpSocket) )
513
        {
514
            _TFTPSendAck(MutExVar.group2._tftpDuplicateBlock);
515
            _tftpState = SM_TFTP_WAIT_FOR_DATA;
516
        }
517
        break;
518
 
519
    case SM_TFTP_READY:
520
        if ( UDPIsGetReady(_tftpSocket) )
521
        {
522
            _tftpStartTick = TickGet();
523
            return TFTP_OK;
524
        }
525
 
526
        // End of file is reached when data block is less than 512 bytes long.
527
        // To reduce code, only MSB compared against 0x02 (of 0x200 = 512) to
528
        // determine if block is less than 512 bytes long or not.
529
        else if ( MutExVar.group2._tftpBlockLength.Val == 0 ||
530
                  MutExVar.group2._tftpBlockLength.byte.MSB < TFTP_BLOCK_SIZE_MSB )
531
            _tftpState = SM_TFTP_SEND_LAST_ACK;
532
        else
533
            break;
534
 
535
 
536
    case SM_TFTP_SEND_LAST_ACK:
537
    case SM_TFTP_SEND_ACK:
538
        if ( UDPIsPutReady(_tftpSocket) )
539
        {
540
            _TFTPSendAck(MutExVar.group2._tftpBlockNumber);
541
 
542
            // This is the next block we are expecting.
543
            MutExVar.group2._tftpBlockNumber.Val++;
544
 
545
            // Remember that we have already acked current block.
546
            _tftpFlags.bits.bIsAcked = TRUE;
547
 
548
            if ( _tftpState == SM_TFTP_SEND_LAST_ACK )
549
                return TFTP_END_OF_FILE;
550
 
551
            _tftpState = SM_TFTP_WAIT_FOR_DATA;
552
        }
553
        break;
554
    }
555
 
556
 
557
 
558
    return TFTP_NOT_READY;
559
}
560
 
561
 
562
 
563
/*********************************************************************
564
 * Function:        BYTE TFTPGet(void)
565
 *
566
 * PreCondition:    TFTPOpenFile() is called with TFTP_FILE_MODE_READ
567
 *                  and TFTPIsGetReady() = TRUE
568
 *
569
 * Input:           None
570
 *
571
 * Output:          data byte as received from remote server.
572
 *
573
 * Side Effects:    None
574
 *
575
 * Overview:        Fetches next data byte from TFTP socket.
576
 *                  If end of data block is reached, it issues
577
 *                  ack to server so that next data block can be
578
 *                  received.
579
 *
580
 * Note:            Use this function to read file from server.
581
 ********************************************************************/
582
BYTE TFTPGet(void)
583
{
584
    BYTE v;
585
 
586
    // Read byte from UDP
587
    UDPGet(&v);
588
 
589
    // Update block length
590
    MutExVar.group2._tftpBlockLength.Val++;
591
 
592
    // Check to see if entire data block is fetched.
593
    // To reduce code, MSB is compared for 0x02 (of 0x200 = 512).
594
    if ( MutExVar.group2._tftpBlockLength.byte.MSB == TFTP_BLOCK_SIZE_MSB )
595
    {
596
        // Entire block was fetched.  Discard everything else.
597
        UDPDiscard();
598
 
599
        // Remember that we have flushed this block.
600
        _tftpFlags.bits.bIsFlushed = TRUE;
601
 
602
        // Reset block length.
603
        MutExVar.group2._tftpBlockLength.Val = 0;
604
 
605
        // Must send ACK to receive next block.
606
        _tftpState = SM_TFTP_SEND_ACK;
607
    }
608
 
609
    return v;
610
}
611
 
612
 
613
 
614
 
615
/*********************************************************************
616
 * Function:        void TFTPCloseFile(void)
617
 *
618
 * PreCondition:    TFTPOpenFile() was called and TFTPIsFileOpened()
619
 *                  had returned with TFTP_OK.
620
 *
621
 * Input:           None
622
 *
623
 * Output:          None
624
 *
625
 * Side Effects:    None
626
 *
627
 * Overview:        If file is opened in read mode, it makes sure
628
 *                  that last ACK is sent to server
629
 *                  If file is opened in write mode, it makes sure
630
 *                  that last block is sent out to server and
631
 *                  waits for server to respond with ACK.
632
 *
633
 * Note:            TFTPIsFileClosed() must be called to confirm
634
 *                  if file was really closed.
635
 ********************************************************************/
636
void TFTPCloseFile(void)
637
{
638
    // If a file was opened for read, we can close it immediately.
639
    if ( _tftpFlags.bits.bIsReading )
640
    {
641
        // If this was premature close, make sure that we discard
642
        // current block.
643
        if ( !_tftpFlags.bits.bIsFlushed )
644
        {
645
            _tftpFlags.bits.bIsFlushed = TRUE;
646
            UDPDiscard();
647
        }
648
 
649
        if ( _tftpFlags.bits.bIsAcked )
650
        {
651
            _tftpFlags.bits.bIsClosed = TRUE;
652
            _tftpFlags.bits.bIsClosing = FALSE;
653
            return;
654
        }
655
 
656
        else
657
        {
658
            _tftpState = SM_TFTP_SEND_LAST_ACK;
659
            _tftpFlags.bits.bIsClosing = TRUE;
660
        }
661
        return;
662
    }
663
 
664
    // For write mode, if we have not flushed last block, do it now.
665
    if ( !_tftpFlags.bits.bIsFlushed )
666
    {
667
        _tftpFlags.bits.bIsFlushed = TRUE;
668
        UDPFlush();
669
    }
670
 
671
    // For write mode, if our last block was ack'ed by remote server,
672
    // file is said to be closed.
673
    if ( _tftpFlags.bits.bIsAcked )
674
    {
675
        _tftpFlags.bits.bIsClosed = TRUE;
676
        _tftpFlags.bits.bIsClosing = FALSE;
677
        return;
678
    }
679
 
680
    _tftpState = SM_TFTP_WAIT_FOR_ACK;
681
    _tftpFlags.bits.bIsClosing =  TRUE;
682
 
683
}
684
 
685
 
686
 
687
/*********************************************************************
688
 * Function:        TFTP_RESULT TFPTIsFileClosed(void)
689
 *
690
 * PreCondition:    TFTPCloseFile() is already called.
691
 *
692
 * Input:           None
693
 *
694
 * Output:          TFTP_OK if file was successfully closdd
695
 *
696
 *                  TFTP_RETRY if file mode was Write and remote
697
 *                  server did not receive last packet.
698
 *                  Application must retry with last block.
699
 *
700
 *                  TFTP_TIMEOUT if all attempts were exhausted
701
 *                  in closing file.
702
 *
703
 *                  TFTP_ERROR if remote server sent an error
704
 *                  in response to last block.
705
 *                  Actual error code may be read by calling
706
 *                  TFTPGetError()
707
 *
708
 *                  TFTP_NOT_READY if file is not closed yet.
709
 *
710
 * Side Effects:    None
711
 *
712
 * Overview:        If file mode is Read, it simply makes that
713
 *                  last block is acknowledged.
714
 *                  If file mode is Write, it waits for server ack.
715
 *                  If no ack was received within specified timeout
716
 *                  instructs appliaction to resend last block.
717
 *                  It keeps track of retries and declares timeout
718
 *                  all attempts were exhausted.
719
 *
720
 * Note:            None
721
 ********************************************************************/
722
TFTP_RESULT TFTPIsFileClosed(void)
723
{
724
    if ( _tftpFlags.bits.bIsReading )
725
        return TFTPIsGetReady();
726
 
727
    else
728
        return TFTPIsPutReady();
729
}
730
 
731
 
732
 
733
 
734
/*********************************************************************
735
 * Function:        TFTP_RESULT TFTPIsPutReady(void)
736
 *
737
 * PreCondition:    TFTPOpenFile() is called with TFTP_FILE_MODE_WRITE
738
 *                  and TFTPIsFileOpened() returned with TRUE.
739
 *
740
 * Input:           None
741
 *
742
 * Output:          TFTP_OK if it is okay to write more data byte.
743
 *
744
 *                  TFTP_TIMEOUT if timeout occurred waiting for
745
 *                  ack from server
746
 *
747
 *                  TFTP_RETRY if all server did not send ack
748
 *                  on time and application needs to resend
749
 *                  last block.
750
 *
751
 *                  TFTP_ERROR if remote server returned ERROR.
752
 *                  Actual error code may be read by calling
753
 *                  TFTPGetError()
754
 *
755
 *                  TFTP_NOT_READY if still waiting...
756
 *
757
 * Side Effects:    None
758
 *
759
 * Overview:        Waits for ack from server.  If ack does not
760
 *                  arrive within specified timeout, it it instructs
761
 *                  application to retry last block by returning
762
 *                  TFTP_RETRY.
763
 *
764
 *                  If all attempts are exhausted, it returns with
765
 *                  TFTP_TIMEOUT.
766
 *
767
 * Note:            None
768
 ********************************************************************/
769
TFTP_RESULT TFTPIsPutReady(void)
770
{
771
    WORD_VAL opCode;
772
    WORD_VAL blockNumber;
773
    BOOL bTimeOut;
774
 
775
    // Check to see if timeout has occurred.
776
    bTimeOut = FALSE;
777
    if ( TickGetDiff(TickGet(), _tftpStartTick) >= TFTP_GET_TIMEOUT_VAL )
778
    {
779
        bTimeOut = TRUE;
780
        _tftpStartTick = TickGet();
781
    }
782
 
783
    switch(_tftpState)
784
    {
785
    case SM_TFTP_WAIT_FOR_ACK:
786
        // When timeout occurs in this state, application must retry.
787
        if ( bTimeOut )
788
        {
789
            if ( _tftpRetries++ > (TFTP_MAX_RETRIES-1) )
790
            {
791
                DEBUG(printf("TFTPIsPutReady(): Timeout.\n"));
792
 
793
                // Forget about all previous attempts.
794
                _tftpRetries = 1;
795
 
796
                return TFTP_TIMEOUT;
797
            }
798
 
799
            else
800
            {
801
                DEBUG(printf("TFTPIsPutReady(): Retry.\n"));
802
                return TFTP_RETRY;
803
            }
804
        }
805
 
806
        // Must wait for ACK from server before we transmit next block.
807
        if ( !UDPIsGetReady(_tftpSocket) )
808
            break;
809
 
810
        // Get opCode.
811
        UDPGet(&opCode.byte.MSB);
812
        UDPGet(&opCode.byte.LSB);
813
 
814
        // Get block number.
815
        UDPGet(&blockNumber.byte.MSB);
816
        UDPGet(&blockNumber.byte.LSB);
817
 
818
        // Discard everything else.
819
        UDPDiscard();
820
 
821
        // This must be ACK or else there is a problem.
822
        if ( opCode.Val == TFTP_OPCODE_ACK )
823
        {
824
            // Also the block number must match with what we are expecting.
825
            if ( MutExVar.group2._tftpBlockNumber.Val == blockNumber.Val )
826
            {
827
                // Mark that block we sent previously has been ack'ed.
828
                _tftpFlags.bits.bIsAcked = TRUE;
829
 
830
                // Since we have ack, forget about previous retry count.
831
                _tftpRetries = 1;
832
 
833
                // If this file is being closed, this must be last ack.
834
                // Declare it as closed.
835
                if ( _tftpFlags.bits.bIsClosing )
836
                {
837
                    _tftpFlags.bits.bIsClosed = TRUE;
838
                    return TFTP_OK;
839
                }
840
 
841
                // Or else, wait for put to become ready so that caller
842
                // can transfer more data blocks.
843
                _tftpState = SM_TFTP_WAIT;
844
            }
845
 
846
            else
847
            {
848
                DEBUG(printf("TFTPIsPutReady(): "\
849
                    "Unexpected block %d received - droping it...\n", \
850
                    blockNumber.Val));
851
                return TFTP_NOT_READY;
852
            }
853
        }
854
 
855
        else if ( opCode.Val == TFTP_OPCODE_ERROR )
856
        {
857
            // For error opCode, remember error code so that application
858
            // can read it later.
859
            _tftpError = blockNumber.Val;
860
 
861
            // Declare error.
862
            return TFTP_ERROR;
863
        }
864
        else
865
            break;
866
 
867
 
868
    case SM_TFTP_WAIT:
869
        // Wait for UDP is to be ready to transmit.
870
        if ( UDPIsPutReady(_tftpSocket) )
871
        {
872
            // Put next block of data.
873
            MutExVar.group2._tftpBlockNumber.Val++;
874
            UDPPut(0);
875
            UDPPut(TFTP_OPCODE_DATA);
876
 
877
            UDPPut(MutExVar.group2._tftpBlockNumber.byte.MSB);
878
            UDPPut(MutExVar.group2._tftpBlockNumber.byte.LSB);
879
 
880
            // Remember that this block is not yet flushed.
881
            _tftpFlags.bits.bIsFlushed = FALSE;
882
 
883
            // Remember that this block is not acknowledged.
884
            _tftpFlags.bits.bIsAcked = FALSE;
885
 
886
            // Now, TFTP module is ready to put more data.
887
            _tftpState = SM_TFTP_READY;
888
 
889
            return TFTP_OK;
890
        }
891
        break;
892
 
893
    case SM_TFTP_READY:
894
        // TFTP module is said to be ready only when underlying UDP
895
        // is ready to transmit.
896
        if ( UDPIsPutReady(_tftpSocket) )
897
            return TFTP_OK;
898
    }
899
 
900
    return TFTP_NOT_READY;
901
}
902
 
903
 
904
 
905
/*********************************************************************
906
 * Function:        void TFTPPut(BYTE c)
907
 *
908
 * PreCondition:    TFTPOpenFile() is called with TFTP_FILE_MODE_WRITE
909
 *                  and TFTPIsPutReady() = TRUE
910
 *
911
 * Input:           c       - Data byte that is to be written
912
 *
913
 * Output:          None
914
 *
915
 * Side Effects:    None
916
 *
917
 * Overview:        Puts given data byte into TFTP socket.
918
 *                  If end of data block is reached, it
919
 *                  transmits entire block.
920
 *
921
 * Note:            Use this function to write file to server.
922
 ********************************************************************/
923
void TFTPPut(BYTE c)
924
{
925
    // Put given byte directly to UDP
926
    UDPPut(c);
927
 
928
    // One more byte in data block.
929
    ++MutExVar.group2._tftpBlockLength.Val;
930
 
931
    // Check to see if data block is full.
932
    if ( MutExVar.group2._tftpBlockLength.byte.MSB == TFTP_BLOCK_SIZE_MSB )
933
    {
934
        // If it is, then transmit this block.
935
        UDPFlush();
936
 
937
        // Remember that current block is already flushed.
938
        _tftpFlags.bits.bIsFlushed = TRUE;
939
 
940
        // Prepare for next block.
941
        MutExVar.group2._tftpBlockLength.Val = 0;
942
 
943
        // Need to wait for ACK from server before putting
944
        // next block of data.
945
        _tftpState = SM_TFTP_WAIT_FOR_ACK;
946
    }
947
}
948
 
949
 
950
 
951
 
952
static void _TFTPSendFileName(TFTP_OPCODE opcode, char *fileName)
953
{
954
    BYTE c;
955
 
956
    // Write opCode
957
    UDPPut(0);
958
    UDPPut(opcode);
959
 
960
    // write file name, including NULL.
961
    do
962
    {
963
        c = *fileName++;
964
        UDPPut(c);
965
    } while ( c != '\0' );
966
 
967
    // Write mode - always use octet or binay mode to transmit files.
968
    UDPPut('o');
969
    UDPPut('c');
970
    UDPPut('t');
971
    UDPPut('e');
972
    UDPPut('t');
973
    UDPPut(0);
974
 
975
    // Transmit it.
976
    UDPFlush();
977
 
978
    // Reset data block length.
979
    MutExVar.group2._tftpBlockLength.Val = 0;
980
 
981
}
982
 
983
static void _TFTPSendAck(WORD_VAL blockNumber)
984
{
985
    // Write opCode.
986
    UDPPut(0);
987
    UDPPut(TFTP_OPCODE_ACK);
988
 
989
    // Write block number for this ack.
990
    UDPPut(blockNumber.byte.MSB);
991
    UDPPut(blockNumber.byte.LSB);
992
 
993
    // Transmit it.
994
    UDPFlush();
995
}