source: soft/giet_vm/sys/drivers.c @ 228

Last change on this file since 228 was 228, checked in by meunier, 11 years ago

Added support for memspaces and const.
Added an interrupt masking to the "giet_context_switch" syscall
Corrected two bugs in boot/boot_init.c (one minor and one regarding barriers initialization)
Reformatted the code in all files.

File size: 45.8 KB
Line 
1///////////////////////////////////////////////////////////////////////////////////
2// File     : drivers.c
3// Date     : 01/04/2012
4// Author   : alain greiner
5// Copyright (c) UPMC-LIP6
6///////////////////////////////////////////////////////////////////////////////////
7// The drivers.c and drivers.h files are part ot the GIET-VM nano kernel.
8// They contains the drivers for the peripherals available in the SoCLib library:
9// - vci_multi_tty
10// - vci_multi_timer
11// - vci_multi_dma
12// - vci_multi_icu
13// - vci_xicu & vci_multi_icu
14// - vci_gcd
15// - vci_frame_buffer
16// - vci_block_device
17//
18// The following global parameters must be defined in the giet_config.h file:
19// - CLUSTER_SIZE
20// - NB_CLUSTERS   
21// - NB_PROCS_MAX 
22// - NB_TIMERS_MAX   
23// - NB_DMAS_MAX     
24// - NB_TTYS   
25//
26// The following virtual base addresses must be defined in the giet_vsegs.ld file:
27// - seg_icu_base
28// - seg_tim_base
29// - seg_tty_base
30// - seg_gcd_base
31// - seg_dma_base
32// - seg_fbf_base
33// - seg_ioc_base
34// - seg_nic_base
35// As some peripherals can be replicated in the clusters (ICU, TIMER, DMA)
36// These addresses must be completed by an offset depending on the cluster index
37//    full_base_address = seg_***_base + cluster_id * CLUSTER_SIZE
38///////////////////////////////////////////////////////////////////////////////////
39
40#include <vm_handler.h>
41#include <sys_handler.h>
42#include <giet_config.h>
43#include <drivers.h>
44#include <common.h>
45#include <hwr_mapping.h>
46#include <mips32_registers.h>
47#include <ctx_handler.h>
48
49#if !defined(NB_CLUSTERS)
50# error: You must define NB_CLUSTERS in the configs file
51#endif
52
53#if !defined(NB_PROCS_MAX)
54# error: You must define NB_PROCS_MAX in the configs file
55#endif
56
57#if (NB_PROCS_MAX > 8)
58# error: NB_PROCS_MAX cannot be larger than 8!
59#endif
60
61#if !defined(CLUSTER_SIZE)
62# error: You must define CLUSTER_SIZE in the configs file
63#endif
64
65#if !defined(NB_TTYS)
66# error: You must define NB_TTYS in the configs file
67#endif
68
69#if (NB_TTYS < 1)
70# error: NB_TTYS cannot be smaller than 1!
71#endif
72
73#if !defined(NB_DMAS_MAX)
74#define NB_DMAS_MAX 0
75#endif
76
77#if !defined(NB_TIMERS_MAX)
78#define NB_TIMERS_MAX 0
79#endif
80
81#if ( (NB_TIMERS_MAX) > 32 )
82# error: NB_TIMERS_MAX + NB_PROCS_MAX cannot be larger than 32
83#endif
84
85#if !defined(NB_IOCS)
86# error: You must define NB_IOCS in the configs file
87#endif
88
89#if ( NB_IOCS > 1 )
90# error: NB_IOCS cannot be larger than 1
91#endif
92
93#if !defined( USE_XICU )
94# error: You must define USE_XICU in the configs file
95#endif
96
97#if !defined( IOMMU_ACTIVE )
98# error: You must define IOMMU_ACTIVE in the configs file
99#endif
100
101
102#define in_unckdata __attribute__((section (".unckdata")))
103
104//////////////////////////////////////////////////////////////////////////////
105//     Timers driver
106//////////////////////////////////////////////////////////////////////////////
107// The timers can be implemented in a vci_timer component or in a vci_xicu
108// component (depending on the USE_XICU parameter).
109// There is one timer (or xicu) component per cluster.
110// There is two types of timers:
111// - "system" timers : one per processor, used for context switch.
112//   local_id in [0, NB_PROCS_MAX-1],
113// - "user" timers : requested by the task in the mapping_info data structure.
114//   For each user timer, the timer_id is stored in the context of the task.
115// The global index is cluster_id * (NB_PROCS_MAX+NB_TIMERS_MAX) + local_id
116//////////////////////////////////////////////////////////////////////////////
117
118// User Timer signaling variables
119
120#if (NB_TIMERS_MAX > 0)
121in_unckdata volatile unsigned char _user_timer_event[NB_CLUSTERS * NB_TIMERS_MAX] 
122= { [0 ... ((NB_CLUSTERS * NB_TIMERS_MAX) - 1)] = 0 };
123#endif
124
125//////////////////////////////////////////////////////////////////////////////
126//     _timer_start()
127// This function activates a timer in the vci_timer (or vci_xicu) component
128// by writing in the proper register the period value.
129// It can be used by both the kernel to initialise a "system" timer,
130// or by a task (through a system call) to configure an "user" timer.
131// Returns 0 if success, > 0 if error.
132//////////////////////////////////////////////////////////////////////////////
133unsigned int _timer_start(unsigned int cluster_id, unsigned int local_id, unsigned int period) {
134    // parameters checking
135    if (cluster_id >= NB_CLUSTERS) {
136        return 1;
137    }
138    if (local_id >= NB_TIMERS_MAX) {
139        return 2;
140    }
141
142#if USE_XICU
143    unsigned int * timer_address = (unsigned int *) ((char *) &seg_icu_base + (cluster_id * CLUSTER_SIZE));
144
145    timer_address[XICU_REG(XICU_PTI_PER, local_id)] = period;
146#else
147    unsigned int* timer_address = (unsigned int *) ((char *) &seg_tim_base + (cluster_id * CLUSTER_SIZE));
148
149    timer_address[local_id * TIMER_SPAN + TIMER_PERIOD] = period;
150    timer_address[local_id * TIMER_SPAN + TIMER_MODE] = 0x3;
151#endif
152    return 0;
153}
154
155
156//////////////////////////////////////////////////////////////////////////////
157//     _timer_stop()
158// This function desactivates a timer in the vci_timer (or vci_xicu) component
159// by writing in the proper register.
160// Returns 0 if success, > 0 if error.
161//////////////////////////////////////////////////////////////////////////////
162unsigned int _timer_stop(unsigned int cluster_id, unsigned int local_id) {
163    // parameters checking
164    if (cluster_id >= NB_CLUSTERS) {
165        return 1;
166    }
167    if (local_id >= NB_TIMERS_MAX) {
168        return 2;
169    }
170
171#if USE_XICU
172    unsigned int * timer_address = (unsigned int *) ((char *) &seg_icu_base + (cluster_id * CLUSTER_SIZE));
173
174    timer_address[XICU_REG(XICU_PTI_PER, local_id)] = 0;
175#else
176    unsigned int* timer_address = (unsigned int *) ((char *) &seg_tim_base + (cluster_id * CLUSTER_SIZE));
177    timer_address[local_id * TIMER_SPAN + TIMER_MODE] = 0;
178#endif
179    return 0;
180}
181
182
183//////////////////////////////////////////////////////////////////////////////
184//     _timer_reset_irq()
185// This function acknowlegge a timer interrupt in the vci_timer (or vci_xicu)
186// component by reading/writing in the proper register.
187// It can be used by both the isr_switch() for a "system" timer,
188// or by the _isr_timer() for an "user" timer.
189// Returns 0 if success, > 0 if error.
190//////////////////////////////////////////////////////////////////////////////
191unsigned int _timer_reset_irq(unsigned int cluster_id, unsigned int local_id) {
192    // parameters checking
193    if (cluster_id >= NB_CLUSTERS) {
194        return 1;
195    }
196    if (local_id >= NB_TIMERS_MAX) {
197        return 2;
198    }
199
200#if USE_XICU
201    unsigned int * timer_address = (unsigned int *) ((char *) &seg_icu_base +
202            (cluster_id * (unsigned) CLUSTER_SIZE));
203
204    unsigned int bloup = timer_address[XICU_REG(XICU_PTI_ACK, local_id)];
205    bloup++; // to avoid a warning
206#else
207    unsigned int * timer_address = (unsigned int *)((char *) &seg_tim_base + 
208            (cluster_id * CLUSTER_SIZE));
209
210    timer_address[local_id * TIMER_SPAN + TIMER_RESETIRQ] = 0;
211#endif
212
213    return 0;
214}
215
216
217/////////////////////////////////////////////////////////////////////////////////
218//     VciMultiTty driver
219/////////////////////////////////////////////////////////////////////////////////
220// There is only one multi_tty controler in the architecture.
221// The total number of TTYs is defined by the configuration parameter NB_TTYS.
222// The "system" terminal is TTY[0].
223// The "user" TTYs are allocated to applications by the GIET in the boot phase,
224// as defined in the mapping_info data structure. The corresponding tty_id must
225// be stored in the context of the task by the boot code.
226// The TTY address is : seg_tty_base + tty_id*TTY_SPAN
227/////////////////////////////////////////////////////////////////////////////////
228
229// TTY variables
230in_unckdata volatile unsigned char _tty_get_buf[NB_TTYS];
231in_unckdata volatile unsigned char _tty_get_full[NB_TTYS] = { [0 ... NB_TTYS - 1] = 0 };
232in_unckdata unsigned int _tty_put_lock = 0;  // protect kernel TTY[0]
233
234////////////////////////////////////////////////////////////////////////////////
235//      _tty_error()
236////////////////////////////////////////////////////////////////////////////////
237void _tty_error(unsigned int tty_id, unsigned int task_id) {
238    unsigned int proc_id = _procid();
239
240    _get_lock(&_tty_put_lock);
241    if (tty_id == 0xFFFFFFFF) {
242        _puts("\n[GIET ERROR] no TTY assigned to the task ");
243    }
244    else {
245        _puts("\n[GIET ERROR] TTY index too large for task ");
246    }
247    _putd(task_id);
248    _puts(" on processor ");
249    _putd(proc_id);
250    _puts("\n");
251    _release_lock(&_tty_put_lock);
252}
253
254
255/////////////////////////////////////////////////////////////////////////////////
256//      _tty_write()
257// Write one or several characters directly from a fixed-length user buffer to
258// the TTY_WRITE register of the TTY controler.
259// It doesn't use the TTY_PUT_IRQ interrupt and the associated kernel buffer.
260// This is a non blocking call: it tests the TTY_STATUS register, and stops
261// the transfer as soon as the TTY_STATUS[WRITE] bit is set.
262// The function returns  the number of characters that have been written.
263/////////////////////////////////////////////////////////////////////////////////
264unsigned int _tty_write(const char * buffer, unsigned int length) {
265    unsigned int nwritten;
266    unsigned int task_id = _get_current_task_id();
267    unsigned int tty_id = _get_context_slot(task_id, CTX_TTY_ID);
268
269    if (tty_id >= NB_TTYS) {
270        _tty_error(tty_id , task_id);
271        return 0;
272    }
273
274    unsigned int * tty_address = (unsigned int *) &seg_tty_base;
275
276    for (nwritten = 0; nwritten < length; nwritten++) {
277        // check tty's status
278        if ((tty_address[tty_id * TTY_SPAN + TTY_STATUS] & 0x2) == 0x2) {
279            break;
280        }
281        else {
282            // write character
283            tty_address[tty_id * TTY_SPAN + TTY_WRITE] = (unsigned int) buffer[nwritten];
284        }
285    }
286    return nwritten;
287}
288
289
290//////////////////////////////////////////////////////////////////////////////
291//      _tty_read()
292// This non-blocking function uses the TTY_GET_IRQ[tty_id] interrupt and
293// the associated kernel buffer, that has been written by the ISR.
294// It get the TTY terminal index from the context of the current task.
295// It fetches one single character from the _tty_get_buf[tty_id] kernel
296// buffer, writes this character to the user buffer, and resets the
297// _tty_get_full[tty_id] buffer.
298// The length argument is not used.
299// Returns 0 if the kernel buffer is empty, 1 if the buffer is full.
300//////////////////////////////////////////////////////////////////////////////
301unsigned int _tty_read(char * buffer, unsigned int length) {
302    unsigned int task_id = _get_current_task_id();
303    unsigned int tty_id = _get_context_slot(task_id, CTX_TTY_ID);
304
305    if (tty_id >= NB_TTYS) {
306        _tty_error(tty_id, task_id);
307        return 0;
308    }
309
310    if (_tty_get_full[tty_id] == 0) {
311        return 0;
312    }
313    else {
314        *buffer = _tty_get_buf[tty_id];
315        _tty_get_full[tty_id] = 0;
316        return 1;
317    }
318}
319
320
321////////////////////////////////////////////////////////////////////////////////
322//     _tty_get_char()
323// This function is used by the _isr_tty to read a character in the TTY
324// terminal defined by the tty_id argument. The character is stored
325// in requested buffer, and the IRQ is acknowledged.
326// Returns 0 if success, 1 if tty_id too large.
327////////////////////////////////////////////////////////////////////////////////
328unsigned int _tty_get_char(unsigned int tty_id, unsigned char * buffer) {
329    // checking argument
330    if (tty_id >= NB_TTYS) {
331        return 1;
332    }
333
334    // compute terminal base address
335    unsigned int * tty_address = (unsigned int *) &seg_tty_base; 
336
337    *buffer = (unsigned char) tty_address[tty_id * TTY_SPAN + TTY_READ];
338    return 0;
339}
340
341
342////////////////////////////////////////////////////////////////////////////////
343//     VciMultiIcu and VciXicu drivers
344////////////////////////////////////////////////////////////////////////////////
345// There is one vci_multi_icu (or vci_xicu) component per cluster,
346// and the number of independant ICUs is equal to NB_PROCS_MAX,
347// because there is one private interrupr controler per processor.
348////////////////////////////////////////////////////////////////////////////////
349
350////////////////////////////////////////////////////////////////////////////////
351//     _icu_set_mask()
352// This function can be used with both the vci_xicu & vci_multi_icu components.
353// It set the mask register for the ICU channel identified by the cluster index
354// and the processor index: all '1' bits are set / all '0' bits are not modified.
355// Returns 0 if success, > 0 if error.
356////////////////////////////////////////////////////////////////////////////////
357unsigned int _icu_set_mask(
358        unsigned int cluster_id,
359        unsigned int proc_id,
360        unsigned int value,
361        unsigned int is_timer) {
362    // parameters checking
363    if (cluster_id >= NB_CLUSTERS) {
364        return 1;
365    }
366    if (proc_id >= NB_PROCS_MAX) {
367        return 1;
368    }
369
370    unsigned int * icu_address = (unsigned int *) ((char *) &seg_icu_base + 
371            (cluster_id * (unsigned) CLUSTER_SIZE));
372#if USE_XICU
373    if (is_timer) {
374        icu_address[XICU_REG(XICU_MSK_PTI_ENABLE, proc_id)] = value;
375    }
376    else {
377        icu_address[XICU_REG(XICU_MSK_HWI_ENABLE, proc_id)] = value;
378    }
379#else
380    icu_address[proc_id * ICU_SPAN + ICU_MASK_SET] = value; 
381#endif
382
383    return 0;
384}
385
386
387////////////////////////////////////////////////////////////////////////////////
388//     _icu_get_index()
389// This function can be used with both the vci_xicu & vci_multi_icu components.
390// It returns the index of the highest priority (smaller index) active HWI.
391// The ICU channel is identified by the cluster index and the processor index.
392// Returns 0 if success, > 0 if error.
393////////////////////////////////////////////////////////////////////////////////
394unsigned int _icu_get_index(unsigned int cluster_id, unsigned int proc_id, unsigned int * buffer) {
395    // parameters checking
396    if (cluster_id >= NB_CLUSTERS) {
397        return 1;
398    }
399    if (proc_id >= NB_PROCS_MAX) {
400        return 1;
401    }
402
403    unsigned int * icu_address = (unsigned int *) ((char *) &seg_icu_base + 
404            (cluster_id * (unsigned) CLUSTER_SIZE));
405#if USE_XICU
406    unsigned int prio = icu_address[XICU_REG(XICU_PRIO, proc_id)];
407    unsigned int pti_ok = (prio & 0x00000001);
408    unsigned int hwi_ok = (prio & 0x00000002);
409    unsigned int swi_ok = (prio & 0x00000004);
410    unsigned int pti_id = (prio & 0x00001F00) >> 8;
411    unsigned int hwi_id = (prio & 0x001F0000) >> 16;
412    unsigned int swi_id = (prio & 0x1F000000) >> 24;
413    if (pti_ok) {
414        *buffer = pti_id;
415    }
416    else if (hwi_ok) {
417        *buffer = hwi_id;
418    }
419    else if (swi_ok) {
420        *buffer = swi_id;
421    }
422    else {
423        *buffer = 32;
424    }
425#else
426    *buffer = icu_address[proc_id * ICU_SPAN + ICU_IT_VECTOR]; 
427#endif
428
429    return 0;
430}
431
432
433////////////////////////////////////////////////////////////////////////////////
434//     VciGcd driver
435////////////////////////////////////////////////////////////////////////////////
436// The Greater Dommon Divider is a -very- simple hardware coprocessor
437// performing the computation of the GCD of two 32 bits integers.
438// It has no DMA capability.
439////////////////////////////////////////////////////////////////////////////////
440
441////////////////////////////////////////////////////////////////////////////////
442//     _gcd_write()
443// Write a 32-bit word in a memory mapped register of the GCD coprocessor.
444// Returns 0 if success, > 0 if error.
445////////////////////////////////////////////////////////////////////////////////
446unsigned int _gcd_write(unsigned int register_index, unsigned int value) {
447    // parameters checking
448    if (register_index >= GCD_END) {
449        return 1;
450    }
451
452    unsigned int * gcd_address = (unsigned int *) &seg_gcd_base;
453
454    gcd_address[register_index] = value; // write word
455    return 0;
456}
457
458
459////////////////////////////////////////////////////////////////////////////////
460//     _gcd_read()
461// Read a 32-bit word in a memory mapped register of the GCD coprocessor.
462// Returns 0 if success, > 0 if error.
463////////////////////////////////////////////////////////////////////////////////
464unsigned int _gcd_read(unsigned int register_index, unsigned int * buffer) {
465    // parameters checking
466    if (register_index >= GCD_END) {
467        return 1;
468    }
469
470    unsigned int * gcd_address = (unsigned int *) &seg_gcd_base;
471
472    *buffer = gcd_address[register_index]; // read word
473    return 0;
474}
475
476////////////////////////////////////////////////////////////////////////////////
477// VciBlockDevice driver
478////////////////////////////////////////////////////////////////////////////////
479// The VciBlockDevice is a single channel external storage contrÃŽler.
480//
481// The IOMMU can be activated or not:
482//
483// 1) When the IOMMU is used, a fixed size 2Mbytes vseg is allocated to
484// the IOC peripheral, in the I/O virtual space, and the user buffer is
485// dynamically remapped in the IOMMU page table. The corresponding entry
486// in the IOMMU PT1 is defined by the kernel _ioc_iommu_ix1 variable.
487// The number of pages to be unmapped is stored in the _ioc_npages variable.
488// The number of PT2 entries is dynamically computed and stored in the
489// kernel _ioc_iommu_npages variable. It cannot be larger than 512.
490// The user buffer is unmapped by the _ioc_completed() function when
491// the transfer is completed.
492//
493// 2/ If the IOMMU is not used, we check that  the user buffer is mapped to a
494// contiguous physical buffer (this is generally true because the user space
495// page tables are statically constructed to use contiguous physical memory).
496//
497// Finally, the memory buffer must fulfill the following conditions:
498// - The user buffer must be word aligned,
499// - The user buffer must be mapped in user address space,
500// - The user buffer must be writable in case of (to_mem) access,
501// - The total number of physical pages occupied by the user buffer cannot
502//   be larger than 512 pages if the IOMMU is activated,
503// - All physical pages occupied by the user buffer must be contiguous
504//   if the IOMMU is not activated.
505// An error code is returned if these conditions are not verified.
506//
507// As the IOC component can be used by several programs running in parallel,
508// the _ioc_lock variable guaranties exclusive access to the device.  The
509// _ioc_read() and _ioc_write() functions use atomic LL/SC to get the lock.
510// and set _ioc_lock to a non zero value.  The _ioc_write() and _ioc_read()
511// functions are blocking, polling the _ioc_lock variable until the device is
512// available.
513// When the tranfer is completed, the ISR routine activated by the IOC IRQ
514// set the _ioc_done variable to a non-zero value. Possible address errors
515// detected by the IOC peripheral are reported by the ISR in the _ioc_status
516// variable.
517// The _ioc_completed() function is polling the _ioc_done variable, waiting for
518// transfer completion. When the completion is signaled, the _ioc_completed()
519// function reset the _ioc_done variable to zero, and releases the _ioc_lock
520// variable.
521//
522// In a multi-processing environment, this polling policy should be replaced by
523// a descheduling policy for the requesting process.
524///////////////////////////////////////////////////////////////////////////////
525
526// IOC global variables
527in_unckdata volatile unsigned int _ioc_status= 0;
528in_unckdata volatile unsigned int _ioc_done = 0;
529in_unckdata unsigned int _ioc_lock = 0;
530in_unckdata unsigned int _ioc_iommu_ix1 = 0;
531in_unckdata unsigned int _ioc_iommu_npages; 
532
533///////////////////////////////////////////////////////////////////////////////
534//      _ioc_access()
535// This function transfer data between a memory buffer and the block device.
536// The buffer lentgth is (count*block_size) bytes.
537// Arguments are:
538// - to_mem     : from external storage to memory when non 0
539// - lba        : first block index on the external storage.
540// - user_vaddr : virtual base address of the memory buffer.
541// - count      : number of blocks to be transfered.
542// Returns 0 if success, > 0 if error.
543///////////////////////////////////////////////////////////////////////////////
544unsigned int _ioc_access(
545        unsigned int to_mem,
546        unsigned int lba,
547        unsigned int user_vaddr,
548        unsigned int count) {
549    unsigned int user_vpn_min; // first virtuel page index in user space
550    unsigned int user_vpn_max; // last virtual page index in user space
551    unsigned int vpn;          // current virtual page index in user space
552    unsigned int ppn;          // physical page number
553    unsigned int flags;        // page protection flags
554    unsigned int ix2;          // page index in IOMMU PT1 page table
555    unsigned int addr;         // buffer address for IOC peripheral
556    unsigned int ppn_first;    // first physical page number for user buffer
557
558    // check buffer alignment
559    if ((unsigned int) user_vaddr & 0x3) {
560        return 1;
561    }
562
563    unsigned int * ioc_address = (unsigned int *) &seg_ioc_base ;
564
565    unsigned int block_size = ioc_address[BLOCK_DEVICE_BLOCK_SIZE];
566    unsigned int length = count * block_size;
567
568    // get user space page table virtual address
569    unsigned int task_id = _get_current_task_id();
570    unsigned int user_pt_vbase = _get_context_slot(task_id, CTX_PTAB_ID);
571
572    user_vpn_min = user_vaddr >> 12;
573    user_vpn_max = (user_vaddr + length - 1) >> 12;
574    ix2 = 0;
575
576    // loop on all virtual pages covering the user buffer
577    for (vpn = user_vpn_min; vpn <= user_vpn_max; vpn++) {
578        // get ppn and flags for each vpn
579        unsigned int ko = _v2p_translate((page_table_t *) user_pt_vbase, vpn, &ppn, &flags);
580
581        // check access rights
582        if (ko) {
583            return 2; // unmapped
584        }
585        if ((flags & PTE_U) == 0) {
586            return 3; // not in user space
587        }
588        if (((flags & PTE_W) == 0 ) && to_mem) {
589            return 4; // not writable
590        }
591
592        // save first ppn value
593        if (ix2 == 0) {
594            ppn_first = ppn;
595        }
596
597        if (IOMMU_ACTIVE) {
598            // the user buffer must be remapped in the I/0 space
599            // check buffer length < 2 Mbytes
600            if (ix2 > 511) {
601                return 2;
602            }
603
604            // map the physical page in IOMMU page table
605            _iommu_add_pte2(
606                    _ioc_iommu_ix1,    // PT1 index
607                    ix2,               // PT2 index
608                    ppn,               // Physical page number   
609                    flags);            // Protection flags
610        }
611        else {
612            // no IOMMU : check that physical pages are contiguous
613            if ((ppn - ppn_first) != ix2) {
614                return 5; // split physical buffer
615            }
616        }
617
618        // increment page index
619        ix2++;
620    } // end for vpn
621
622    // register the number of pages to be unmapped
623    _ioc_iommu_npages = (user_vpn_max - user_vpn_min) + 1;
624
625    // invalidate data cache in case of memory write
626    if (to_mem) {
627        _dcache_buf_invalidate((void *) user_vaddr, length);
628    }
629
630    // compute buffer base address for IOC depending on IOMMU activation
631    if (IOMMU_ACTIVE) {
632        addr = (_ioc_iommu_ix1) << 21 | (user_vaddr & 0xFFF);
633    }
634    else {
635        addr = (ppn_first << 12) | (user_vaddr & 0xFFF);
636    }
637
638    // get the lock on ioc device
639    _get_lock(&_ioc_lock);
640
641    // peripheral configuration 
642    ioc_address[BLOCK_DEVICE_BUFFER] = addr;
643    ioc_address[BLOCK_DEVICE_COUNT] = count;
644    ioc_address[BLOCK_DEVICE_LBA] = lba;
645    if (to_mem == 0) {
646        ioc_address[BLOCK_DEVICE_OP] = BLOCK_DEVICE_WRITE;
647    }
648    else {
649        ioc_address[BLOCK_DEVICE_OP] = BLOCK_DEVICE_READ;
650    }
651
652    return 0;
653}
654
655
656/////////////////////////////////////////////////////////////////////////////////
657// _ioc_completed()
658//
659// This function checks completion of an I/O transfer and reports errors.
660// As it is a blocking call, the processor is stalled.
661// If the virtual memory is activated, the pages mapped in the I/O virtual
662// space are unmapped, and the IOB TLB is cleared.
663// Returns 0 if success, > 0 if error.
664/////////////////////////////////////////////////////////////////////////////////
665unsigned int _ioc_completed() {
666    unsigned int ret;
667    unsigned int ix2;
668
669    // busy waiting
670    while (_ioc_done == 0) {
671        asm volatile("nop");
672    }
673
674    // unmap the buffer from IOMMU page table if IOMMU is activated
675    if (IOMMU_ACTIVE) {
676        unsigned int * iob_address = (unsigned int *) &seg_iob_base;
677
678        for (ix2 = 0; ix2 < _ioc_iommu_npages; ix2++) {
679            // unmap the page in IOMMU page table
680            _iommu_inval_pte2(
681                    _ioc_iommu_ix1, // PT1 index
682                    ix2 );          // PT2 index
683
684            // clear IOMMU TLB
685            iob_address[IOB_INVAL_PTE] = (_ioc_iommu_ix1 << 21) | (ix2 << 12); 
686        }
687    }
688
689    // test IOC status
690    if ((_ioc_status != BLOCK_DEVICE_READ_SUCCESS)
691            && (_ioc_status != BLOCK_DEVICE_WRITE_SUCCESS)) {
692        ret = 1; // error
693    }
694    else {
695        ret = 0; // success
696    }
697
698    // reset synchronization variables
699    _ioc_done = 0;
700    asm volatile("sync");
701    _ioc_lock = 0;
702
703    return ret;
704}
705
706
707///////////////////////////////////////////////////////////////////////////////
708//     _ioc_read()
709// Transfer data from the block device to a memory buffer in user space.
710// - lba    : first block index on the block device
711// - buffer : base address of the memory buffer (must be word aligned)
712// - count  : number of blocks to be transfered.
713// Returns 0 if success, > 0 if error.
714///////////////////////////////////////////////////////////////////////////////
715unsigned int _ioc_read(unsigned int lba, void * buffer, unsigned int count) {
716    return _ioc_access(
717            1,        // read access
718            lba,
719            (unsigned int) buffer,
720            count);
721}
722
723
724///////////////////////////////////////////////////////////////////////////////
725//     _ioc_write()
726// Transfer data from a memory buffer in user space to the block device.
727// - lba    : first block index on the block device
728// - buffer : base address of the memory buffer (must be word aligned)
729// - count  : number of blocks to be transfered.
730// Returns 0 if success, > 0 if error.
731///////////////////////////////////////////////////////////////////////////////
732unsigned int _ioc_write(unsigned int lba, const void * buffer, unsigned int count) {
733    return _ioc_access(
734            0, // write access
735            lba,
736            (unsigned int) buffer,
737            count);
738}
739
740
741///////////////////////////////////////////////////////////////////////////////
742//     _ioc_get_status()
743// This function returns the transfert status, and acknowledge the IRQ.
744// Returns 0 if success, > 0 if error.
745///////////////////////////////////////////////////////////////////////////////
746unsigned int _ioc_get_status(unsigned int * status) {
747    // get IOC base address
748    unsigned int * ioc_address = (unsigned int *) &seg_ioc_base;
749
750    *status = ioc_address[BLOCK_DEVICE_STATUS]; // read status & reset IRQ
751    return 0;
752}
753
754
755//////////////////////////////////////////////////////////////////////////////////
756// VciMultiDma driver
757//////////////////////////////////////////////////////////////////////////////////
758// The DMA controllers are physically distributed in the clusters.
759// There is  (NB_CLUSTERS * NB_DMAS_MAX) channels, indexed by a global index:
760//        dma_id = cluster_id * NB_DMA_MAX + loc_id
761//
762// As a DMA channel can be used by several tasks, each DMA channel is protected
763// by a specific lock: _dma_lock[dma_id]
764// The signalisation between the OS and the DMA uses the _dma_done[dma_id]
765// synchronisation variables  (set by the ISR, and reset by the OS).
766// The transfer status is copied by the ISR in the _dma_status[dma_id] variables.
767//
768// These DMA channels can be used by the FB driver, or by the NIC driver.
769//////////////////////////////////////////////////////////////////////////////////
770
771#if NB_DMAS_MAX > 0
772in_unckdata unsigned int            _dma_lock[NB_DMAS_MAX * NB_CLUSTERS] = {
773    [0 ... (NB_DMAS_MAX * NB_CLUSTERS) - 1] = 0
774};
775
776in_unckdata volatile unsigned int    _dma_done[NB_DMAS_MAX * NB_CLUSTERS] = {
777    [0 ... (NB_DMAS_MAX * NB_CLUSTERS) - 1] = 0
778};
779
780in_unckdata volatile unsigned int _dma_status[NB_DMAS_MAX * NB_CLUSTERS];
781in_unckdata unsigned int _dma_iommu_ix1 = 1;
782in_unckdata unsigned int _dma_iommu_npages[NB_DMAS_MAX * NB_CLUSTERS];
783#endif
784
785//////////////////////////////////////////////////////////////////////////////////
786// _dma_reset_irq()
787//////////////////////////////////////////////////////////////////////////////////
788unsigned int _dma_reset_irq(unsigned int cluster_id, unsigned int channel_id) {
789#if NB_DMAS_MAX > 0
790    // parameters checking
791    if (cluster_id >= NB_CLUSTERS) {
792        return 1;
793    }
794    if (channel_id >= NB_DMAS_MAX) {
795        return 1;
796    }
797
798    // compute DMA base address
799    unsigned int * dma_address = (unsigned int *) ((char *) &seg_dma_base + 
800            (cluster_id * (unsigned) CLUSTER_SIZE));
801
802    dma_address[channel_id * DMA_SPAN + DMA_RESET] = 0;           
803    return 0;
804#else
805    return -1;
806#endif
807}
808
809
810//////////////////////////////////////////////////////////////////////////////////
811// _dma_get_status()
812//////////////////////////////////////////////////////////////////////////////////
813unsigned int _dma_get_status(unsigned int cluster_id, unsigned int channel_id, unsigned int * status) {
814#if NB_DMAS_MAX > 0
815    // parameters checking
816    if (cluster_id >= NB_CLUSTERS) {
817        return 1;
818    }
819    if (channel_id >= NB_DMAS_MAX) {
820        return 1;
821    }
822
823    // compute DMA base address
824    unsigned int * dma_address = (unsigned int *) ((char *) &seg_dma_base + 
825            (cluster_id * (unsigned) CLUSTER_SIZE));
826
827    *status = dma_address[channel_id * DMA_SPAN + DMA_LEN];
828    return 0;
829#else
830    return -1;
831#endif
832}
833
834
835//////////////////////////////////////////////////////////////////////////////////
836// _dma_transfer()
837// Transfer data between a user buffer and a device buffer using DMA.
838// Two devices types are supported: Frame Buffer if dev_type == 0
839//                                  Multi-Nic if dev_type != 0
840// Arguments are:
841// - dev_type     : device type.
842// - to_user      : from  device buffer to user buffer when true.
843// - offset       : offset (in bytes) in the device buffer.
844// - user_vaddr   : virtual base address of the user buffer.
845// - length       : number of bytes to be transfered.
846//
847// The DMA channel is obtained from task context (CTX_FBDMA_ID / CTX_NIDMA_ID.
848// The user buffer must be mapped in user address space and word-aligned.
849// The user buffer length must be multiple of 4 bytes.
850// Me must compute the physical base addresses for both the device buffer
851// and the user buffer before programming the DMA transfer.
852// The GIET being fully static, we don't need to split the transfer in 4 Kbytes
853// pages, because the user buffer is contiguous in physical space.
854// Returns 0 if success, > 0 if error.
855//////////////////////////////////////////////////////////////////////////////////
856unsigned int _dma_transfer(
857        unsigned int dev_type,
858        unsigned int to_user,
859        unsigned int offset,
860        unsigned int user_vaddr,
861        unsigned int length) {
862#if NB_DMAS_MAX > 0
863    unsigned int ko;           // unsuccessfull V2P translation
864    unsigned int flags;        // protection flags
865    unsigned int ppn;          // physical page number
866    unsigned int user_pbase;   // user buffer pbase address
867    unsigned int device_pbase; // frame buffer pbase address
868    unsigned int device_vaddr; // device buffer vbase address
869
870    // check user buffer address and length alignment
871    if ((user_vaddr & 0x3) || (length & 0x3)) {
872        _get_lock(&_tty_put_lock);
873        _puts("\n[GIET ERROR] in _dma_transfer : user buffer not word aligned\n");
874        _release_lock(&_tty_put_lock);
875        return 1;
876    }
877
878    // get DMA channel and compute DMA vbase address
879    unsigned int task_id    = _get_current_task_id();
880    unsigned int dma_id     = _get_context_slot(task_id, CTX_DMA_ID);
881    unsigned int cluster_id = dma_id / NB_DMAS_MAX;
882    unsigned int loc_id     = dma_id % NB_DMAS_MAX;
883    unsigned int * dma_base = (unsigned int *) ((char *) &seg_dma_base + 
884            (cluster_id * (unsigned) CLUSTER_SIZE));
885
886    // get page table address
887    unsigned int user_ptab = _get_context_slot( task_id, CTX_PTAB_ID);
888
889    // get peripheral buffer virtual address
890    if ( dev_type) {
891        device_vaddr = (unsigned int) &seg_nic_base + offset;
892    }
893    else {
894        device_vaddr = (unsigned int) &seg_fbf_base + offset;
895    }
896
897    // get device buffer physical address
898    ko = _v2p_translate((page_table_t *) user_ptab, (device_vaddr >> 12), &ppn, &flags);
899    if (ko) {
900        _get_lock(&_tty_put_lock);
901        _puts("\n[GIET ERROR] in _dma_transfer : device buffer unmapped\n");
902        _release_lock(&_tty_put_lock);
903        return 2;
904    }
905    device_pbase = (ppn << 12) | (device_vaddr & 0x00000FFF);
906
907    // Compute user buffer physical address
908    ko = _v2p_translate( (page_table_t*)user_ptab, (user_vaddr >> 12), &ppn, &flags);
909    if (ko) {
910        _get_lock(&_tty_put_lock);
911        _puts("\n[GIET ERROR] in _dma_transfer() : user buffer unmapped\n");
912        _release_lock(&_tty_put_lock);
913        return 3;
914    } 
915    if ((flags & PTE_U) == 0) {
916        _get_lock(&_tty_put_lock);
917        _puts("[GIET ERROR] in _dma_transfer() : user buffer not in user space\n");
918        _release_lock(&_tty_put_lock);
919        return 4; 
920    }
921    if (((flags & PTE_W) == 0 ) && to_user) {
922        _get_lock(&_tty_put_lock);
923        _puts("\n[GIET ERROR] in _dma_transfer() : user buffer not writable\n");
924        _release_lock(&_tty_put_lock);
925        return 5;
926    }
927    user_pbase = (ppn << 12) | (user_vaddr & 0x00000FFF);
928
929    /*  This is a draft for IOMMU support
930
931    // loop on all virtual pages covering the user buffer
932    unsigned int user_vpn_min = user_vaddr >> 12;
933    unsigned int user_vpn_max = (user_vaddr + length - 1) >> 12;
934    unsigned int ix2          = 0;
935    unsigned int ix1          = _dma_iommu_ix1 + dma_id;
936
937    for ( vpn = user_vpn_min ; vpn <= user_vpn_max ; vpn++ )
938    {
939    // get ppn and flags for each vpn
940    unsigned int ko = _v2p_translate( (page_table_t*)user_pt_vbase,
941    vpn,
942    &ppn,
943    &flags );
944
945    // check access rights
946    if ( ko )                                 return 3;     // unmapped
947    if ( (flags & PTE_U) == 0 )               return 4;     // not in user space
948    if ( ( (flags & PTE_W) == 0 ) && to_user ) return 5;     // not writable
949
950    // save first ppn value
951    if ( ix2 == 0 ) ppn_first = ppn;
952
953    if ( IOMMU_ACTIVE )    // the user buffer must be remapped in the I/0 space
954    {
955    // check buffer length < 2 Mbytes
956    if ( ix2 > 511 ) return 2;
957
958    // map the physical page in IOMMU page table
959    _iommu_add_pte2( ix1,        // PT1 index
960    ix2,        // PT2 index
961    ppn,        // physical page number
962    flags );    // protection flags
963    }
964    else            // no IOMMU : check that physical pages are contiguous
965    {
966    if ( (ppn - ppn_first) != ix2 )       return 6;     // split physical buffer 
967    }
968
969    // increment page index
970    ix2++;
971    } // end for vpn
972
973    // register the number of pages to be unmapped if iommu activated
974    _dma_iommu_npages[dma_id] = (user_vpn_max - user_vpn_min) + 1;
975
976*/
977
978    // invalidate data cache in case of memory write
979    if (to_user) {
980        _dcache_buf_invalidate((void *) user_vaddr, length);
981    }
982
983    // get the lock
984    _get_lock(&_dma_lock[dma_id]);
985
986    // DMA configuration
987    if (to_user) {
988        dma_base[loc_id * DMA_SPAN + DMA_SRC] = (unsigned int) device_pbase;
989        dma_base[loc_id * DMA_SPAN + DMA_DST] = (unsigned int) user_pbase;
990    }
991    else {
992        dma_base[loc_id * DMA_SPAN + DMA_SRC] = (unsigned int) user_pbase;
993        dma_base[loc_id * DMA_SPAN + DMA_DST] = (unsigned int) device_pbase;
994    }
995    dma_base[loc_id * DMA_SPAN + DMA_LEN] = (unsigned int) length;
996
997    return 0;
998#else //NB_DMAS_MAX == 0
999    return -1;
1000#endif
1001}  // end _dma_transfer() 
1002
1003
1004//////////////////////////////////////////////////////////////////////////////////
1005// _dma_completed()
1006// This function checks completion of a DMA transfer to or from a peripheral
1007// device (Frame Buffer or Multi-Nic).
1008// As it is a blocking call, the processor is busy waiting.
1009// Returns 0 if success, > 0 if error
1010// (1 == read error / 2 == DMA idle error / 3 == write error)
1011//////////////////////////////////////////////////////////////////////////////////
1012unsigned int _dma_completed() {
1013#if NB_DMAS_MAX > 0
1014    unsigned int task_id = _get_current_task_id();
1015    unsigned int dma_id  = _get_context_slot(task_id, CTX_DMA_ID);
1016    unsigned int dma_ret;
1017
1018    // busy waiting with a pseudo random delay between bus access
1019    while (_dma_done[dma_id] == 0) {
1020        unsigned int delay = (( _proctime() ^ _procid() << 4) & 0x3F) + 1;
1021        asm volatile(
1022                "move  $3,   %0                 \n"
1023                "loop_nic_completed:            \n"
1024                "addi  $3,   $3, -1             \n"
1025                "bnez  $3,   loop_nic_completed \n"
1026                "nop                            \n"
1027                :
1028                : "r" (delay)
1029                : "$3"); 
1030    }
1031
1032    /* draft support for IOMMU
1033    // unmap the buffer from IOMMU page table if IOMMU is activated
1034    if ( GIET_IOMMU_ACTIVE )
1035    {
1036    unsigned int* iob_address = (unsigned int*)&seg_iob_base;
1037
1038    unsigned int  ix1         = _dma_iommu_ix1 + dma_id;
1039    unsigned int  ix2;
1040
1041    for ( ix2 = 0 ; ix2 < _dma_iommu_npages[dma_id] ; ix2++ )
1042    {
1043    // unmap the page in IOMMU page table
1044    _iommu_inval_pte2( ix1,        // PT1 index
1045    ix2 );   // PT2 index
1046
1047    // clear IOMMU TLB
1048    iob_address[IOB_INVAL_PTE] = (ix1 << 21) | (ix2 << 12);
1049    }
1050    }
1051    */
1052
1053    // reset synchronization variables
1054    _dma_done[dma_id] = 0;
1055    dma_ret = _dma_status[dma_id];
1056    asm volatile("sync\n");
1057    _dma_lock[dma_id] = 0;
1058
1059    return dma_ret;
1060
1061#else //NB_DMAS_MAX == 0
1062    return -1;
1063#endif
1064}  // end _dma_completed
1065
1066//////////////////////////////////////////////////////////////////////////////////
1067//     VciFrameBuffer driver
1068//////////////////////////////////////////////////////////////////////////////////
1069// The vci_frame_buffer device can be accessed directly by software with memcpy(),
1070// or it can be accessed through a multi-channels DMA component:
1071// 
1072// The '_fb_sync_write' and '_fb_sync_read' functions use a memcpy strategy to
1073// implement the transfer between a data buffer (user space) and the frame
1074// buffer (kernel space). They are blocking until completion of the transfer.
1075//
1076// The '_fb_write()', '_fb_read()' and '_fb_completed()' functions use the
1077// VciMultiDma components (distributed in the clusters) to transfer data
1078// between the user buffer and the frame buffer. A  FBDMA channel is
1079// allocated to each task requesting it in the mapping_info data structure.
1080//////////////////////////////////////////////////////////////////////////////////
1081
1082//////////////////////////////////////////////////////////////////////////////////
1083// _fb_sync_write()
1084// Transfer data from an memory buffer to the frame_buffer device using a memcpy.
1085// - offset : offset (in bytes) in the frame buffer.
1086// - buffer : base address of the memory buffer.
1087// - length : number of bytes to be transfered.
1088//////////////////////////////////////////////////////////////////////////////////
1089unsigned int _fb_sync_write(unsigned int offset, const void * buffer, unsigned int length) {
1090    unsigned char * fb_address = (unsigned char *) &seg_fbf_base + offset;
1091    memcpy((void *) fb_address, (void *) buffer, length);
1092    return 0;
1093}
1094
1095
1096//////////////////////////////////////////////////////////////////////////////////
1097// _fb_sync_read()
1098// Transfer data from the frame_buffer device to a memory buffer using a memcpy.
1099// - offset : offset (in bytes) in the frame buffer.
1100// - buffer : base address of the memory buffer.
1101// - length : number of bytes to be transfered.
1102//////////////////////////////////////////////////////////////////////////////////
1103unsigned int _fb_sync_read(unsigned int offset, const void * buffer, unsigned int length) {
1104    unsigned char * fb_address = (unsigned char *) &seg_fbf_base + offset;
1105    memcpy((void *) buffer, (void *) fb_address, length);
1106    return 0;
1107}
1108
1109
1110//////////////////////////////////////////////////////////////////////////////////
1111// _fb_write()
1112// Transfer data from a memory buffer to the frame_buffer device using  DMA.
1113// - offset : offset (in bytes) in the frame buffer.
1114// - buffer : base address of the memory buffer.
1115// - length : number of bytes to be transfered.
1116// Returns 0 if success, > 0 if error.
1117//////////////////////////////////////////////////////////////////////////////////
1118unsigned int _fb_write(unsigned int offset, const void * buffer, unsigned int length) {
1119    return _dma_transfer(
1120            0,             // frame buffer
1121            0,             // write
1122            offset,
1123            (unsigned int) buffer,
1124            length);
1125}
1126
1127
1128//////////////////////////////////////////////////////////////////////////////////
1129// _fb_read()
1130// Transfer data from the frame_buffer device to a memory buffer using  DMA.
1131// - offset : offset (in bytes) in the frame buffer.
1132// - buffer : base address of the memory buffer.
1133// - length : number of bytes to be transfered.
1134// Returns 0 if success, > 0 if error.
1135//////////////////////////////////////////////////////////////////////////////////
1136unsigned int _fb_read(unsigned int offset, const void * buffer, unsigned int length) {
1137    return _dma_transfer(
1138            0,    // frame buffer
1139            1,    // read
1140            offset,
1141            (unsigned int) buffer,
1142            length);
1143}
1144
1145
1146//////////////////////////////////////////////////////////////////////////////////
1147// _fb_completed()
1148// This function checks completion of a DMA transfer to or fom the frame buffer.
1149// As it is a blocking call, the processor is busy waiting.
1150// Returns 0 if success, > 0 if error
1151// (1 == read error / 2 == DMA idle error / 3 == write error)
1152//////////////////////////////////////////////////////////////////////////////////
1153unsigned int _fb_completed() {
1154    return _dma_completed();
1155}
1156
1157//////////////////////////////////////////////////////////////////////////////////
1158//     VciMultiNic driver
1159//////////////////////////////////////////////////////////////////////////////////
1160// The VciMultiNic device can be accessed directly by software with memcpy(),
1161// or it can be accessed through a multi-channels DMA component:
1162// 
1163// The '_nic_sync_write' and '_nic_sync_read' functions use a memcpy strategy to
1164// implement the transfer between a data buffer (user space) and the NIC
1165// buffer (kernel space). They are blocking until completion of the transfer.
1166//
1167// The '_nic_write()', '_nic_read()' and '_nic_completed()' functions use the
1168// VciMultiDma components (distributed in the clusters) to transfer data
1169// between the user buffer and the NIC. A  NIDMA channel is allocated to each
1170// task requesting it in the mapping_info data structure.
1171//////////////////////////////////////////////////////////////////////////////////
1172
1173//////////////////////////////////////////////////////////////////////////////////
1174// _nic_sync_write()
1175// Transfer data from an memory buffer to the NIC device using a memcpy.
1176// - offset : offset (in bytes) in the frame buffer.
1177// - buffer : base address of the memory buffer.
1178// - length : number of bytes to be transfered.
1179//////////////////////////////////////////////////////////////////////////////////
1180unsigned int _nic_sync_write(unsigned int offset, const void * buffer, unsigned int length) {
1181    unsigned char * nic_address = (unsigned char *) &seg_nic_base + offset;
1182    memcpy((void *) nic_address, (void *) buffer, length);
1183    return 0;
1184}
1185
1186
1187//////////////////////////////////////////////////////////////////////////////////
1188// _nic_sync_read()
1189// Transfer data from the NIC device to a memory buffer using a memcpy.
1190// - offset : offset (in bytes) in the frame buffer.
1191// - buffer : base address of the memory buffer.
1192// - length : number of bytes to be transfered.
1193//////////////////////////////////////////////////////////////////////////////////
1194unsigned int _nic_sync_read(unsigned int offset, const void * buffer, unsigned int length) {
1195    unsigned char *nic_address = (unsigned char *) &seg_nic_base + offset;
1196    memcpy((void *) buffer, (void *) nic_address, length);
1197    return 0;
1198}
1199
1200
1201//////////////////////////////////////////////////////////////////////////////////
1202// _nic_write()
1203// Transfer data from a memory buffer to the NIC device using  DMA.
1204// - offset : offset (in bytes) in the frame buffer.
1205// - buffer : base address of the memory buffer.
1206// - length : number of bytes to be transfered.
1207// Returns 0 if success, > 0 if error.
1208//////////////////////////////////////////////////////////////////////////////////
1209unsigned int _nic_write(unsigned int offset, const void * buffer, unsigned int length) {
1210    return _dma_transfer(
1211            1,            // NIC
1212            0,            // write
1213            offset,
1214            (unsigned int) buffer,
1215            length );   
1216}
1217
1218
1219//////////////////////////////////////////////////////////////////////////////////
1220// _nic_read()
1221// Transfer data from the NIC device to a memory buffer using  DMA.
1222// - offset : offset (in bytes) in the frame buffer.
1223// - buffer : base address of the memory buffer.
1224// - length : number of bytes to be transfered.
1225// Returns 0 if success, > 0 if error.
1226//////////////////////////////////////////////////////////////////////////////////
1227unsigned int _nic_read(unsigned int offset, const void * buffer, unsigned int length) {
1228    return _dma_transfer(
1229            1,            // NIC
1230            1,            // read
1231            offset,
1232            (unsigned int) buffer,
1233            length );   
1234}
1235
1236
1237//////////////////////////////////////////////////////////////////////////////////
1238// _nic_completed()
1239// This function checks completion of a DMA transfer to or fom a NIC channel.
1240// As it is a blocking call, the processor is busy waiting.
1241// Returns 0 if success, > 0 if error
1242// (1 == read error / 2 == DMA idle error / 3 == write error)
1243//////////////////////////////////////////////////////////////////////////////////
1244unsigned int _nic_completed() {
1245    return _dma_completed();
1246}
1247
1248// Local Variables:
1249// tab-width: 4
1250// c-basic-offset: 4
1251// c-file-offsets:((innamespace . 0)(inline-open . 0))
1252// indent-tabs-mode: nil
1253// End:
1254// vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
1255
Note: See TracBrowser for help on using the repository browser.