summaryrefslogtreecommitdiff
path: root/arch/arm/common/rvidcc_common.c
blob: 3e21709095d0c85fef19fadc8b03574be4f5530f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
/* 
   Copyright (C) 2004-2007 ARM Limited.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License version 2
   as published by the Free Software Foundation.

   This program 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 this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

   As a special exception, if other files instantiate templates or use macros
   or inline functions from this file, or you compile this file and link it
   with other works to produce a work based on this file, this file does not
   by itself cause the resulting work to be covered by the GNU General Public
   License. However the source code for this file must still be made available
   in accordance with section (3) of the GNU General Public License.

   This exception does not invalidate any other reasons why a work based on
   this file might be covered by the GNU General Public License.
*/

/*     ================================================================
       Universal RealView ICE DCC communications driver - How to use it
       ================================================================

   This driver is split into 2 sections: a generic section (this file) that
   handles the DCC hardware access and buffering and an OS-dependent file that
   integrates the driver into the OS network and TTY layers.  The interface
   between the sections is defined in rvidcc_interface.h, which contains
   prototypes and documentation for the functions in the generic section that
   can be called from the OS-dependent section and the callback functions
   called from the generic section that must be provided by the OS-dependent
   section.

   This driver is designed to be able to work with or without interrupts, to
   use raw DCC comms (as a TTY) or virtual Ethernet.  The driver is configured
   in the rvidcc_config.h header, including options to select which cores to
   support, whether to support ethernet, buffer sizes and how to
   enable/disable interrupts.  See the example configs for details of all
   options.

   All buffers used by the driver are in the system memory space.  All
   pointer arguments for functions called by or from the generic section are
   pointers to system memory.  It is the responsibility of the OS-dependent
   section to copy to/from user memory.

   ARM Ltd. highly recommends using interrupt-driven DCC communications, and
   to use the virtual Ethernet system, even if only a TTY is required.

   Raw DCC communications transfers data in 32-bit words. Thus if virtual
   Ethernet is not used, data transfers will contain null padding for blocks
   of data that do not have a multiple of 4 bytes. For most command-line
   applications, this is unacceptable. However protocols, such as the remote
   gdb protocol, will tolerate the extra nulls, provided they occur between
   packets. This, of course, requires that data be passed to the driver as
   packets, and not as byte-by-byte as some TTY drivers are in the habit of
   doing.

   Using the virtual Ethernet system does not force you to have to support
   networking, or an IP stack, etc. The virtual Ethernet system contains a
   TTY channel and an Ethernet channel, and if only the TTY channel is
   required, the Ethernet channel and its data can be ignored. Using this
   for TTY only has the advantage that data packets do not have to be a
   multiple of 4 bytes, and so it is suitable for command-line consoles.

   The virtual Ethernet system is not true Ethernet in that it can only carry
   IP traffic. It does, however, have a mechanism for obtaining a unique MAC
   address from the ICE, and the target will receive ARP-style queries to
   determine if the target has a particular IP address. Thus DHCP can be used
   to obtain an IP address from an external DHCP server, without the need for
   any special configuration of the ICE.
*/

#include "rvidcc_config.h"
#include "rvidcc_interface.h"


#ifdef __ARMCC_VERSION
#define INLINE __inline
#else
#define INLINE inline
#endif


#ifndef RVIDCC_RAW

#define UNCOMP_TTY_START 0xa5580000
#define UNCOMP_ETH_START 0xa55a0000
#define START_MASK       0xfffc0000
#define START_SENTINEL   UNCOMP_TTY_START

#endif /* RVIDCC_RAW */

#if !defined(RVIDCC_ARM79) && !defined(RVIDCC_ARM10) && !defined(RVIDCC_ARM11)
#warning No cores are enabled.  Define at least one of RVIDCC_ARM79, RVIDCC_ARM10 or RVIDCC_ARM11
#endif


#define DCC_ARM9_RBIT  (1 << 0)
#define DCC_ARM9_WBIT  (1 << 1)
#define DCC_ARM10_RBIT (1 << 7)
#define DCC_ARM10_WBIT (1 << 6)
#define DCC_ARM11_RBIT (1 << 30)
#define DCC_ARM11_WBIT (1 << 29)

/* Access primitives: x must be unsigned int */

#ifdef __ARMCC_VERSION

/* RVCT versions */
#define READ_CORE_ID(x) { __asm { mrc p15, 0, x, c0, c0, 0 } \
                           x = (x >> 4) & 0xFFF; }

#ifdef RVIDCC_ARM79
#define WRITE_ARM9_DCC(x) __asm { mcr p14, 0, x, c1, c0, 0 }
#define READ_ARM9_DCC(x) __asm { mrc p14, 0, x, c1, c0, 0 }
#define STATUS_ARM9_DCC(x) __asm { mrc p14, 0, x, c0, c0, 0 }
#define CAN_READ_ARM9_DCC(x) {STATUS_ARM9_DCC(x); x &= DCC_ARM9_RBIT;}
#define CAN_WRITE_ARM9_DCC(x) {STATUS_ARM9_DCC(x); x &= DCC_ARM9_WBIT; x = (x==0);}
#endif

#ifdef RVIDCC_ARM10
#define WRITE_ARM10_DCC(x) __asm { mcr p14, 0, x, c0, c5, 0 }
#define READ_ARM10_DCC(x) __asm { mrc p14, 0, x, c0, c5, 0 }
#define STATUS_ARM10_DCC(x) __asm { mrc p14, 0, x, c0, c1, 0 }
#define CAN_READ_ARM10_DCC(x) {STATUS_ARM10_DCC(x); x &= DCC_ARM10_RBIT;}
#define CAN_WRITE_ARM10_DCC(x) {STATUS_ARM10_DCC(x); x &= DCC_ARM10_WBIT; x = (x==0);}
#endif

#ifdef RVIDCC_ARM11
#define WRITE_ARM11_DCC(x) __asm { mcr p14, 0, x, c0, c5, 0 }
#define READ_ARM11_DCC(x) __asm { mrc p14, 0, x, c0, c5, 0 }
#define STATUS_ARM11_DCC(x) __asm { mrc p14, 0, x, c0, c1, 0 }
#define CAN_READ_ARM11_DCC(x) {STATUS_ARM11_DCC(x); x &= DCC_ARM11_RBIT;}
#define CAN_WRITE_ARM11_DCC(x) {STATUS_ARM11_DCC(x); x &= DCC_ARM11_WBIT; x = (x==0);}
#endif

#else

/* GCC versions */
#define READ_CORE_ID(x) { __asm__ ("mrc p15, 0, %0, c0, c0, 0\n" : "=r" (x)); \
                           x = (x >> 4) & 0xFFF; }

#ifdef RVIDCC_ARM79
#define WRITE_ARM9_DCC(x) __asm__ volatile ("mcr p14, 0, %0, c1, c0, 0\n" : : "r" (x))
#define READ_ARM9_DCC(x) __asm__ volatile ("mrc p14, 0, %0, c1, c0, 0\n" : "=r" (x))
#define STATUS_ARM9_DCC(x) __asm__ volatile ("mrc p14, 0, %0, c0, c0, 0\n" : "=r" (x))
#define CAN_READ_ARM9_DCC(x) {STATUS_ARM9_DCC(x); x &= DCC_ARM9_RBIT;}
#define CAN_WRITE_ARM9_DCC(x) {STATUS_ARM9_DCC(x); x &= DCC_ARM9_WBIT; x = (x==0);}
#endif

#ifdef RVIDCC_ARM10
#define WRITE_ARM10_DCC(x) __asm__ volatile ("mcr p14, 0, %0, c0, c5, 0\n" : : "r" (x))
#define READ_ARM10_DCC(x) __asm__ volatile ("mrc p14, 0, %0, c0, c5, 0\n" : "=r" (x))
#define STATUS_ARM10_DCC(x) __asm__ volatile ("mrc p14, 0, %0, c0, c1, 0\n" : "=r" (x))
#define CAN_READ_ARM10_DCC(x) {STATUS_ARM10_DCC(x); x &= DCC_ARM10_RBIT;}
#define CAN_WRITE_ARM10_DCC(x) {STATUS_ARM10_DCC(x); x &= DCC_ARM10_WBIT; x = (x==0);}
#endif

#ifdef RVIDCC_ARM11
#define WRITE_ARM11_DCC(x) __asm__ volatile ("mcr p14, 0, %0, c0, c5, 0\n" : : "r" (x))
#define READ_ARM11_DCC(x) __asm__ volatile ("mrc p14, 0, %0, c0, c5, 0\n" : "=r" (x))
#define STATUS_ARM11_DCC(x) __asm__ volatile ("mrc p14, 0, %0, c0, c1, 0\n" : "=r" (x))
#define CAN_READ_ARM11_DCC(x) {STATUS_ARM11_DCC(x); x &= DCC_ARM11_RBIT;}
#define CAN_WRITE_ARM11_DCC(x) {STATUS_ARM11_DCC(x); x &= DCC_ARM11_WBIT; x = (x==0);}
#endif

#endif

static enum {arm9_and_earlier, arm10, arm11_and_later} arm_type = arm9_and_earlier;

/* Prototypes */
static void rvidcc_write_lowlevel(const void *ptr, size_t len);

/* Queues */

struct ringbuffer
{
    unsigned char *buf;
    size_t size;
    unsigned char* readPtr;
    unsigned char* writePtr;
};

static unsigned char dcc_inbuffer[RVIDCC_BUFSIZE];
static struct ringbuffer dcc_inbuf;
static unsigned char dcc_outbuffer[RVIDCC_BUFSIZE];
static struct ringbuffer dcc_outbuf;

#ifndef RVIDCC_RAW
#define DCC_TTY_INBUF_SIZE (RVIDCC_BUFSIZE/2)
static unsigned char dcc_temp_source[DCC_TTY_INBUF_SIZE];
static unsigned char dcc_tty_inbuffer[DCC_TTY_INBUF_SIZE];
static struct ringbuffer dcc_tty_inbuf;
#define DCC_TTY_INBUF      dcc_tty_inbuf

#else

#define DCC_TTY_INBUF      dcc_inbuf

#endif


/* This ringbuffer has no locks as it assumes only 1 reader and 1 writer and
 * assumes a 32-bit access is atomic */
INLINE static void ringbuf_advance_read(struct ringbuffer* ring_buf, size_t amount)
{
    unsigned char* temptr = ring_buf->readPtr + amount;
    while (temptr >= ring_buf->buf + ring_buf->size)
        temptr -= ring_buf->size;
    ring_buf->readPtr = temptr;
}


INLINE static void ringbuf_advance_write(struct ringbuffer* ring_buf, size_t amount)
{
    unsigned char* temptr = ring_buf->writePtr + amount;
    while (temptr >= ring_buf->buf + ring_buf->size)
        temptr -= ring_buf->size;
    ring_buf->writePtr = temptr;
}


INLINE static void ringbuf_get(struct ringbuffer* ring_buf,
                        unsigned char* dst, size_t amount)
{
    unsigned char* source = ring_buf->readPtr;
    size_t len_to_end = ring_buf->buf + ring_buf->size - source;
    if (amount > len_to_end)
    {
        memcpy(dst, source, len_to_end);
        memcpy(dst + len_to_end, ring_buf->buf, (amount - len_to_end));
    }
    else
        memcpy(dst, source, amount);
}


INLINE static void ringbuf_put(struct ringbuffer* ring_buf,
                        unsigned char* src, size_t amount)
{
    unsigned char* dest = ring_buf->writePtr;
    unsigned int len_to_end = ring_buf->buf + ring_buf->size - dest;
    if (amount > len_to_end)
    {
        memcpy(dest, src, len_to_end);
        memcpy(ring_buf->buf, src + len_to_end, amount - len_to_end);
    }
    else
        memcpy(dest, src, amount);
}


INLINE static size_t ringbuf_available_data(struct ringbuffer* ring_buf)
{
    int avail = ring_buf->writePtr - ring_buf->readPtr;

    if (avail < 0)
        /* handle wrap around */
        avail += ring_buf->size;

    return (size_t)avail;
}


INLINE static size_t ringbuf_available_space(struct ringbuffer* ring_buf)
{
    size_t avail;

    if (ring_buf->writePtr >= ring_buf->readPtr)
        avail = ring_buf->size - (ring_buf->writePtr - ring_buf->readPtr) - 1;
    else
        avail = ring_buf->readPtr - ring_buf->writePtr - 1;

    return avail;
}


// conditionally swap endian of a 32bit word
#ifdef RVIDCC_BIG_ENDIAN
#define to_little_endian(x) ((((x)&0xff) << 24) | (((x)&0xff00) << 8) | (((x)&0xff0000) >> 8) | (((x)&0xff000000) >> 24))
#else
#define to_little_endian(x) (x)
#endif


int rvidcc_init(void)
{
    register unsigned int id;
    int err = 0;
#ifndef RVIDCC_RAW
    unsigned int macRequest = to_little_endian(UNCOMP_ETH_START);
#endif

    RVIDCC_DISABLE_INTERRUPTS;

    /* check core type, raising error if core not supported */
    READ_CORE_ID(id);
    if ((id & 0xF00) == 0xA00)
    {
        arm_type = arm10;
#ifndef RVIDCC_ARM10
        err = RVIDCC_ERR_CORE_NOT_SUPPORTED;
#endif
    }
    else if (id >= 0xb00)
    {
        arm_type = arm11_and_later;
#ifndef RVIDCC_ARM11
        err = RVIDCC_ERR_CORE_NOT_SUPPORTED;
#endif
    }
    else
    {
        arm_type = arm9_and_earlier;
#ifndef RVIDCC_ARM79
        err = RVIDCC_ERR_CORE_NOT_SUPPORTED;
#endif
    }

    if (!err)
    {
        dcc_inbuf.buf = dcc_inbuffer;
        dcc_inbuf.size = RVIDCC_BUFSIZE;
        dcc_inbuf.readPtr  = dcc_inbuf.buf;
        dcc_inbuf.writePtr = dcc_inbuf.buf;

        dcc_outbuf.buf = dcc_outbuffer;
        dcc_outbuf.size = RVIDCC_BUFSIZE;
        dcc_outbuf.readPtr  = dcc_outbuf.buf;
        dcc_outbuf.writePtr = dcc_outbuf.buf;

#ifndef RVIDCC_RAW
        dcc_tty_inbuf.buf = dcc_tty_inbuffer;
        dcc_tty_inbuf.size = DCC_TTY_INBUF_SIZE;
        dcc_tty_inbuf.readPtr  = dcc_tty_inbuf.buf;
        dcc_tty_inbuf.writePtr = dcc_tty_inbuf.buf;

        /* request a MAC address */
        rvidcc_write_lowlevel(&macRequest, sizeof(unsigned int));
#endif

        /* Enable interrupts (write interrupt will be enabled when something is written) */
        RVIDCC_ENABLE_READ_INTERRUPT;
        RVIDCC_ENABLE_INTERRUPTS;
    }

    return err;
}


void rvidcc_write(void)
{
    register unsigned int reg;
    int pollcount = -1;
    int some_transfer = 0;

    switch (arm_type)
    {
#ifdef RVIDCC_ARM79
    case arm9_and_earlier:
    {
        /* Try to write everything available */
        while (ringbuf_available_data(&dcc_outbuf) >= sizeof(unsigned int))
        {
            /* On the first word, the write is aborted immediately if the DCC write
             * register is full.  On subsequent words, the driver waits for a
             * bit (RVIDCC_MAX_INLINE_POLLS times) as the RVI will probably
             * read the DCC register soon because it knows data is being sent. */
            do
            {
                CAN_WRITE_ARM9_DCC(reg);
            } while (!reg && pollcount != -1 && --pollcount > 0);

            if (!reg)
                break;

            reg = to_little_endian(*(unsigned int *)dcc_outbuf.readPtr);
            WRITE_ARM9_DCC(reg);
            ringbuf_advance_read(&dcc_outbuf, sizeof(unsigned int));
            pollcount = RVIDCC_MAX_INLINE_POLLS;
            some_transfer = 1;
        }

        pollcount = -1;
        break;
    }
#endif

#ifdef RVIDCC_ARM10
    case arm10:
    {
        /* Try to write everything available */
        while (ringbuf_available_data(&dcc_outbuf) >= sizeof(unsigned int))
        {
            do
            {
                CAN_WRITE_ARM10_DCC(reg);
            } while (!reg && pollcount != -1 && --pollcount > 0);

            if (!reg)
                break;

            reg = to_little_endian(*(unsigned int *)dcc_outbuf.readPtr);
            WRITE_ARM10_DCC(reg);
            ringbuf_advance_read(&dcc_outbuf, sizeof(unsigned int));
            pollcount = RVIDCC_MAX_INLINE_POLLS;
            some_transfer = 1;
        }

        pollcount = -1;
        break;
    }
#endif

#ifdef RVIDCC_ARM11
    case arm11_and_later:
    {
        /* Try to write everything available */
        while (ringbuf_available_data(&dcc_outbuf) >= sizeof(unsigned int))
        {
            do
            {
                CAN_WRITE_ARM11_DCC(reg);
            } while (!reg && pollcount != -1 && --pollcount > 0);

            if (!reg)
                break;

            reg = to_little_endian(*(unsigned int *)dcc_outbuf.readPtr);
            WRITE_ARM11_DCC(reg);
            ringbuf_advance_read(&dcc_outbuf, sizeof(unsigned int));
            pollcount = RVIDCC_MAX_INLINE_POLLS;
            some_transfer = 1;
        }

        pollcount = -1;
        break;
    }
#endif

    default:
        break;
    }

    if (some_transfer)
        rvidcc_cb_notify();

    /* Disable interrupt if nothing left to write */
    if (ringbuf_available_data(&dcc_outbuf) == 0)
        RVIDCC_DISABLE_WRITE_INTERRUPT;
}


/* this routine assumes it is non-interruptible by others that access the queue pointers */
/* this is written out longhand because it may be called from an interrupt routine */
void rvidcc_read(void)
{
    register unsigned int reg;
    int pollcount = -1;
    int some_transfer = 0;

    switch (arm_type)
    {
#ifdef RVIDCC_ARM79
    case arm9_and_earlier:
    {
        /* Try to read everything available */
        while (ringbuf_available_space(&dcc_inbuf) >= sizeof(unsigned int))
        {
            do
            {
                CAN_READ_ARM9_DCC(reg);
            } while (!reg && pollcount != -1 && --pollcount > 0);

            if (!reg)
                break;

            READ_ARM9_DCC(reg);
            *(unsigned int *)dcc_inbuf.writePtr = to_little_endian(reg);
            ringbuf_advance_write(&dcc_inbuf, sizeof(unsigned int));
            pollcount = RVIDCC_MAX_INLINE_POLLS;
            some_transfer = 1;
        }
        break;
    }
#endif

#ifdef RVIDCC_ARM10
    case arm10:
    {
        /* Try to read everything available */
        while (ringbuf_available_space(&dcc_inbuf) >= sizeof(unsigned int))
        {
            do
            {
                CAN_READ_ARM10_DCC(reg);
            } while (!reg && pollcount != -1 && --pollcount > 0);

            if (!reg)
                break;

            READ_ARM10_DCC(reg);
            *(unsigned int *)dcc_inbuf.writePtr = to_little_endian(reg);
            ringbuf_advance_write(&dcc_inbuf, sizeof(unsigned int));
            pollcount = RVIDCC_MAX_INLINE_POLLS;
            some_transfer = 1;
        }
        break;
    }
#endif

#ifdef RVIDCC_ARM11
    case arm11_and_later:
    {
        /* Try to read everything available */
        while (ringbuf_available_space(&dcc_inbuf) >= sizeof(unsigned int))
        {
            do
            {
                CAN_READ_ARM11_DCC(reg);
            } while (!reg && pollcount != -1 && --pollcount > 0);

            if (!reg)
                break;

            READ_ARM11_DCC(reg);
            *(unsigned int *)dcc_inbuf.writePtr = to_little_endian(reg);
            ringbuf_advance_write(&dcc_inbuf, sizeof(unsigned int));
            pollcount = RVIDCC_MAX_INLINE_POLLS;
            some_transfer = 1;
        }
        break;
    }
#endif

    default:
        break;
    }

    if (ringbuf_available_space(&dcc_inbuf) == 0)
        RVIDCC_DISABLE_READ_INTERRUPT;

    if (some_transfer)
        rvidcc_cb_notify();
}


/* this routine assumes it is not interrupted by others that access the queue pointers */
void rvidcc_poll(void)
{
    rvidcc_write();
    rvidcc_read();
}


#ifndef RVIDCC_RAW
void rvidcc_process(void)
{
    unsigned int sentinel = 0;
    int compsize = 0;

    /* Process received data */
    while (ringbuf_available_data(&dcc_inbuf) >= sizeof(unsigned int))
    {
        int bytes;
        unsigned char *buf;

        /* Search for start sentinel */
        sentinel = to_little_endian(*(unsigned int *)dcc_inbuf.readPtr);
        compsize = sentinel & 0xffff;

        /* Discard if not a start sentinel */
        if ((sentinel & START_MASK) != START_SENTINEL || 
            compsize > sizeof(dcc_temp_source) || compsize <= 0)
        {
            ringbuf_advance_read(&dcc_inbuf, sizeof(unsigned int));
            continue;
        }

        /* Sentinel found, come back later if not enough data */
        bytes = ringbuf_available_data(&dcc_inbuf);
        if (bytes < compsize + sizeof(unsigned int))
            break;

        /* Copy to a contiguous buffer */
        ringbuf_get(&dcc_inbuf,
                    dcc_temp_source, compsize + sizeof(unsigned int));

        sentinel &= 0xffff0000;
        buf = dcc_temp_source + sizeof(unsigned int);

        bytes = compsize;

        /* Round up to nearest whole word */
        if (compsize & 0x3)
            compsize = (compsize + 4) & ~0x3;

        /* advance by packet size + sentinel */
        ringbuf_advance_read(&dcc_inbuf, compsize + sizeof(unsigned int));

        /* allow more data to be received */
        RVIDCC_ENABLE_READ_INTERRUPT;

        /* Process TTY or IP packet accordingly */
        if (sentinel == UNCOMP_TTY_START)
        {
            ringbuf_put(&dcc_tty_inbuf, buf, bytes);
            ringbuf_advance_write(&dcc_tty_inbuf, bytes);
        }
        else
        {
            if (bytes == 4) /* ARP 'is this IP address you?' */
            {
                if (rvidcc_cb_has_addr(buf))
                    rvidcc_transmit_ip_packet(buf, 4);
            }
            else if (bytes == 6) /* set MAC address */
                rvidcc_cb_set_mac_address(buf);
            else
                rvidcc_cb_ip_packet_received(buf, (size_t)bytes);
        }
    }
}
#endif


#ifdef RVIDCC_SCAN_INPUT_FOR
int rvidcc_scan_input_for(char c, size_t span)
{
    int ret = 0;

    size_t bytesAvail = ringbuf_available_data(&DCC_TTY_INBUF);
    if (bytesAvail && span)
    {
        unsigned char *p = DCC_TTY_INBUF.readPtr;

        if (span > bytesAvail)
            span = bytesAvail;

        while (span--)
        {
            if (*p++ == c)
            {
                ret = 1;
                break;
            }

            if (p >= DCC_TTY_INBUF.buf + DCC_TTY_INBUF.size)
                p = DCC_TTY_INBUF.buf;
        }
    }   
    return ret;
}
#endif


#ifdef RVIDCC_OUTBUF_DATA
size_t rvidcc_outbuf_data(void)
{
    return ringbuf_available_data(&dcc_outbuf);
}
#endif


size_t rvidcc_serial_can_read(void)
{
    return ringbuf_available_data(&DCC_TTY_INBUF);
}


size_t rvidcc_serial_can_write(void)
{
    size_t space = ringbuf_available_space(&dcc_outbuf);

    // protocol only has 16 bits for packet size, so limit packets to this size
    if (space > (65536 - 4))
      space = (65536 - 4);

    return space;
}


size_t rvidcc_read_serial(void *ptr, size_t len)
{
    size_t bytes = ringbuf_available_data(&DCC_TTY_INBUF);
    if (bytes && len)
    {
        if (bytes > len)
            bytes = len;

        ringbuf_get(&DCC_TTY_INBUF, (unsigned char*)ptr, bytes);
#ifdef RVIDCC_RAW
        /* round up to 4 bytes */
        if (bytes & 0x3)
            ringbuf_advance_read(&DCC_TTY_INBUF, ((bytes + 4) & ~0x3));
        else
            ringbuf_advance_read(&DCC_TTY_INBUF, bytes);

        RVIDCC_ENABLE_READ_INTERRUPT;
#else
        ringbuf_advance_read(&DCC_TTY_INBUF, bytes);
#endif
    }
    else
        bytes = 0;

    return bytes;
}


static void rvidcc_write_lowlevel(const void *ptr, size_t len)
{
    size_t rem;

    ringbuf_put(&dcc_outbuf, (unsigned char*)ptr, len);

    ringbuf_advance_write(&dcc_outbuf, len & ~0x3); // advance whole words

    /* ensure excess bytes up to word boundary are null */
    rem = (len & 0x3);
    if (rem)
    {
        for (; rem < 4; rem++)
            *(dcc_outbuf.writePtr + rem) = '\0';

        ringbuf_advance_write(&dcc_outbuf, sizeof(unsigned long)); // advance remainder word
    }


    RVIDCC_ENABLE_WRITE_INTERRUPT;
}


size_t rvidcc_write_serial(const void *ptr, size_t len)
{
    size_t bytes;
#ifndef RVIDCC_RAW
    unsigned long sentinel;
#endif

    bytes = rvidcc_serial_can_write();

#ifndef RVIDCC_RAW
    if (bytes > sizeof(unsigned long))
    {
        /* Size limit the amount of data to be written, allowing for sentinel */
        if (len > (bytes - sizeof(unsigned long)))
            len = (bytes - sizeof(unsigned long));

        sentinel = to_little_endian(UNCOMP_TTY_START | (unsigned short)len);
        rvidcc_write_lowlevel(&sentinel, sizeof(unsigned long));
        rvidcc_write_lowlevel(ptr, len);
    }
#else
    if (bytes > 0)
    {
        /* Size limit the amount of data to be written */
        if (len > bytes)
            len = bytes;

        rvidcc_write_lowlevel(ptr, len);
    }
#endif
    else
        len = 0;

    return len;
}


#ifndef RVIDCC_RAW
int rvidcc_transmit_ip_packet(void *packet, size_t length)
{
    if (rvidcc_serial_can_write() >= (length + 4))
    {
        unsigned long sentinel = to_little_endian(UNCOMP_ETH_START | (unsigned short)length);
        rvidcc_write_lowlevel(&sentinel, 4);
        rvidcc_write_lowlevel(packet, length);
    }
    else
        length = 0;

    return length;
}
#endif

/* End of file */