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
RevLine 
[158]1///////////////////////////////////////////////////////////////////////////////////
2// File     : drivers.c
3// Date     : 01/04/2012
4// Author   : alain greiner
5// Copyright (c) UPMC-LIP6
6///////////////////////////////////////////////////////////////////////////////////
[189]7// The drivers.c and drivers.h files are part ot the GIET-VM nano kernel.
[158]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
[203]13// - vci_xicu & vci_multi_icu
[158]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:
[204]19// - CLUSTER_SIZE
[189]20// - NB_CLUSTERS   
21// - NB_PROCS_MAX 
22// - NB_TIMERS_MAX   
23// - NB_DMAS_MAX     
24// - NB_TTYS   
[158]25//
[218]26// The following virtual base addresses must be defined in the giet_vsegs.ld file:
[158]27// - seg_icu_base
[203]28// - seg_tim_base
[158]29// - seg_tty_base
30// - seg_gcd_base
31// - seg_dma_base
[203]32// - seg_fbf_base
[158]33// - seg_ioc_base
[218]34// - seg_nic_base
[204]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
[158]38///////////////////////////////////////////////////////////////////////////////////
39
[166]40#include <vm_handler.h>
[158]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)
[215]50# error: You must define NB_CLUSTERS in the configs file
[158]51#endif
[189]52
53#if !defined(NB_PROCS_MAX)
[215]54# error: You must define NB_PROCS_MAX in the configs file
[189]55#endif
56
57#if (NB_PROCS_MAX > 8)
58# error: NB_PROCS_MAX cannot be larger than 8!
59#endif
60
[204]61#if !defined(CLUSTER_SIZE)
[215]62# error: You must define CLUSTER_SIZE in the configs file
[158]63#endif
[189]64
[158]65#if !defined(NB_TTYS)
[215]66# error: You must define NB_TTYS in the configs file
[158]67#endif
68
[165]69#if (NB_TTYS < 1)
70# error: NB_TTYS cannot be smaller than 1!
71#endif
72
[189]73#if !defined(NB_DMAS_MAX)
[205]74#define NB_DMAS_MAX 0
[165]75#endif
76
[189]77#if !defined(NB_TIMERS_MAX)
[205]78#define NB_TIMERS_MAX 0
[165]79#endif
80
[216]81#if ( (NB_TIMERS_MAX) > 32 )
[189]82# error: NB_TIMERS_MAX + NB_PROCS_MAX cannot be larger than 32
83#endif
[165]84
[189]85#if !defined(NB_IOCS)
[215]86# error: You must define NB_IOCS in the configs file
[189]87#endif
[158]88
[189]89#if ( NB_IOCS > 1 )
90# error: NB_IOCS cannot be larger than 1
91#endif
[158]92
[215]93#if !defined( USE_XICU )
94# error: You must define USE_XICU in the configs file
95#endif
[158]96
[215]97#if !defined( IOMMU_ACTIVE )
98# error: You must define IOMMU_ACTIVE in the configs file
99#endif
100
101
[189]102#define in_unckdata __attribute__((section (".unckdata")))
[169]103
[158]104//////////////////////////////////////////////////////////////////////////////
[228]105//     Timers driver
[158]106//////////////////////////////////////////////////////////////////////////////
[203]107// The timers can be implemented in a vci_timer component or in a vci_xicu
[215]108// component (depending on the USE_XICU parameter).
[203]109// There is one timer (or xicu) component per cluster.
[189]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.
[203]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
[158]116//////////////////////////////////////////////////////////////////////////////
117
[189]118// User Timer signaling variables
119
120#if (NB_TIMERS_MAX > 0)
[228]121in_unckdata volatile unsigned char _user_timer_event[NB_CLUSTERS * NB_TIMERS_MAX] 
122= { [0 ... ((NB_CLUSTERS * NB_TIMERS_MAX) - 1)] = 0 };
[189]123#endif
124
[158]125//////////////////////////////////////////////////////////////////////////////
[203]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,
[189]130// or by a task (through a system call) to configure an "user" timer.
[158]131// Returns 0 if success, > 0 if error.
132//////////////////////////////////////////////////////////////////////////////
[228]133unsigned int _timer_start(unsigned int cluster_id, unsigned int local_id, unsigned int period) {
[165]134    // parameters checking
[228]135    if (cluster_id >= NB_CLUSTERS) {
136        return 1;
137    }
138    if (local_id >= NB_TIMERS_MAX) {
139        return 2;
140    }
[158]141
[215]142#if USE_XICU
[228]143    unsigned int * timer_address = (unsigned int *) ((char *) &seg_icu_base + (cluster_id * CLUSTER_SIZE));
[158]144
[203]145    timer_address[XICU_REG(XICU_PTI_PER, local_id)] = period;
[189]146#else
[228]147    unsigned int* timer_address = (unsigned int *) ((char *) &seg_tim_base + (cluster_id * CLUSTER_SIZE));
[189]148
[203]149    timer_address[local_id * TIMER_SPAN + TIMER_PERIOD] = period;
[228]150    timer_address[local_id * TIMER_SPAN + TIMER_MODE] = 0x3;
[189]151#endif
[158]152    return 0;
153}
[228]154
155
[189]156//////////////////////////////////////////////////////////////////////////////
[203]157//     _timer_stop()
158// This function desactivates a timer in the vci_timer (or vci_xicu) component
159// by writing in the proper register.
[189]160// Returns 0 if success, > 0 if error.
161//////////////////////////////////////////////////////////////////////////////
[228]162unsigned int _timer_stop(unsigned int cluster_id, unsigned int local_id) {
[203]163    // parameters checking
[228]164    if (cluster_id >= NB_CLUSTERS) {
165        return 1;
166    }
167    if (local_id >= NB_TIMERS_MAX) {
168        return 2;
169    }
[158]170
[215]171#if USE_XICU
[228]172    unsigned int * timer_address = (unsigned int *) ((char *) &seg_icu_base + (cluster_id * CLUSTER_SIZE));
[203]173
174    timer_address[XICU_REG(XICU_PTI_PER, local_id)] = 0;
175#else
[228]176    unsigned int* timer_address = (unsigned int *) ((char *) &seg_tim_base + (cluster_id * CLUSTER_SIZE));
[203]177    timer_address[local_id * TIMER_SPAN + TIMER_MODE] = 0;
178#endif
179    return 0;
[189]180}
[228]181
182
[158]183//////////////////////////////////////////////////////////////////////////////
[203]184//     _timer_reset_irq()
185// This function acknowlegge a timer interrupt in the vci_timer (or vci_xicu)
[204]186// component by reading/writing in the proper register.
[203]187// It can be used by both the isr_switch() for a "system" timer,
188// or by the _isr_timer() for an "user" timer.
[158]189// Returns 0 if success, > 0 if error.
190//////////////////////////////////////////////////////////////////////////////
[228]191unsigned int _timer_reset_irq(unsigned int cluster_id, unsigned int local_id) {
[203]192    // parameters checking
[228]193    if (cluster_id >= NB_CLUSTERS) {
194        return 1;
195    }
196    if (local_id >= NB_TIMERS_MAX) {
197        return 2;
198    }
[158]199
[215]200#if USE_XICU
[228]201    unsigned int * timer_address = (unsigned int *) ((char *) &seg_icu_base +
202            (cluster_id * (unsigned) CLUSTER_SIZE));
[203]203
204    unsigned int bloup = timer_address[XICU_REG(XICU_PTI_ACK, local_id)];
[228]205    bloup++; // to avoid a warning
[203]206#else
[228]207    unsigned int * timer_address = (unsigned int *)((char *) &seg_tim_base + 
208            (cluster_id * CLUSTER_SIZE));
[203]209
210    timer_address[local_id * TIMER_SPAN + TIMER_RESETIRQ] = 0;
211#endif
212
213    return 0;
[158]214}
215
[228]216
[158]217/////////////////////////////////////////////////////////////////////////////////
[228]218//     VciMultiTty driver
[158]219/////////////////////////////////////////////////////////////////////////////////
[189]220// There is only one multi_tty controler in the architecture.
[158]221// The total number of TTYs is defined by the configuration parameter NB_TTYS.
[189]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/////////////////////////////////////////////////////////////////////////////////
[158]228
[189]229// TTY variables
230in_unckdata volatile unsigned char _tty_get_buf[NB_TTYS];
[228]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]
[189]233
234////////////////////////////////////////////////////////////////////////////////
235//      _tty_error()
236////////////////////////////////////////////////////////////////////////////////
[228]237void _tty_error(unsigned int tty_id, unsigned int task_id) {
[189]238    unsigned int proc_id = _procid();
239
240    _get_lock(&_tty_put_lock);
[228]241    if (tty_id == 0xFFFFFFFF) {
[226]242        _puts("\n[GIET ERROR] no TTY assigned to the task ");
[228]243    }
244    else {
[226]245        _puts("\n[GIET ERROR] TTY index too large for task ");
[228]246    }
247    _putd(task_id);
[189]248    _puts(" on processor ");
[228]249    _putd(proc_id);
[189]250    _puts("\n");
251    _release_lock(&_tty_put_lock);
252}
[228]253
254
[189]255/////////////////////////////////////////////////////////////////////////////////
256//      _tty_write()
[158]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.
[189]263/////////////////////////////////////////////////////////////////////////////////
[228]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);
[158]268
[228]269    if (tty_id >= NB_TTYS) {
270        _tty_error(tty_id , task_id);
[189]271        return 0;
272    }
[158]273
[228]274    unsigned int * tty_address = (unsigned int *) &seg_tty_base;
[158]275
[228]276    for (nwritten = 0; nwritten < length; nwritten++) {
[165]277        // check tty's status
[228]278        if ((tty_address[tty_id * TTY_SPAN + TTY_STATUS] & 0x2) == 0x2) {
[158]279            break;
[228]280        }
281        else {
[165]282            // write character
[228]283            tty_address[tty_id * TTY_SPAN + TTY_WRITE] = (unsigned int) buffer[nwritten];
284        }
[158]285    }
286    return nwritten;
287}
[228]288
289
[158]290//////////////////////////////////////////////////////////////////////////////
[204]291//      _tty_read()
[158]292// This non-blocking function uses the TTY_GET_IRQ[tty_id] interrupt and
[165]293// the associated kernel buffer, that has been written by the ISR.
[204]294// It get the TTY terminal index from the context of the current task.
[158]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.
[204]298// The length argument is not used.
[158]299// Returns 0 if the kernel buffer is empty, 1 if the buffer is full.
300//////////////////////////////////////////////////////////////////////////////
[228]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);
[158]304
[228]305    if (tty_id >= NB_TTYS) {
306        _tty_error(tty_id, task_id);
[189]307        return 0;
308    }
[158]309
[228]310    if (_tty_get_full[tty_id] == 0) {
[189]311        return 0;
[158]312    }
[228]313    else {
[158]314        *buffer = _tty_get_buf[tty_id];
315        _tty_get_full[tty_id] = 0;
[189]316        return 1;
[158]317    }
[228]318}
319
320
[158]321////////////////////////////////////////////////////////////////////////////////
[204]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.
[158]327////////////////////////////////////////////////////////////////////////////////
[228]328unsigned int _tty_get_char(unsigned int tty_id, unsigned char * buffer) {
[204]329    // checking argument
[228]330    if (tty_id >= NB_TTYS) {
331        return 1;
332    }
[199]333
[204]334    // compute terminal base address
[228]335    unsigned int * tty_address = (unsigned int *) &seg_tty_base; 
[158]336
[228]337    *buffer = (unsigned char) tty_address[tty_id * TTY_SPAN + TTY_READ];
[204]338    return 0;
[158]339}
340
[228]341
[158]342////////////////////////////////////////////////////////////////////////////////
[228]343//     VciMultiIcu and VciXicu drivers
[158]344////////////////////////////////////////////////////////////////////////////////
[203]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.
[158]348////////////////////////////////////////////////////////////////////////////////
349
350////////////////////////////////////////////////////////////////////////////////
[203]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.
[158]355// Returns 0 if success, > 0 if error.
356////////////////////////////////////////////////////////////////////////////////
[228]357unsigned int _icu_set_mask(
358        unsigned int cluster_id,
359        unsigned int proc_id,
360        unsigned int value,
361        unsigned int is_timer) {
[203]362    // parameters checking
[228]363    if (cluster_id >= NB_CLUSTERS) {
364        return 1;
365    }
366    if (proc_id >= NB_PROCS_MAX) {
367        return 1;
368    }
[203]369
[228]370    unsigned int * icu_address = (unsigned int *) ((char *) &seg_icu_base + 
371            (cluster_id * (unsigned) CLUSTER_SIZE));
[215]372#if USE_XICU
[228]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    }
[189]379#else
[203]380    icu_address[proc_id * ICU_SPAN + ICU_MASK_SET] = value; 
381#endif
[189]382
[158]383    return 0;
384}
[228]385
386
[158]387////////////////////////////////////////////////////////////////////////////////
[203]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.
[158]392// Returns 0 if success, > 0 if error.
393////////////////////////////////////////////////////////////////////////////////
[228]394unsigned int _icu_get_index(unsigned int cluster_id, unsigned int proc_id, unsigned int * buffer) {
[203]395    // parameters checking
[228]396    if (cluster_id >= NB_CLUSTERS) {
397        return 1;
398    }
399    if (proc_id >= NB_PROCS_MAX) {
400        return 1;
401    }
[203]402
[228]403    unsigned int * icu_address = (unsigned int *) ((char *) &seg_icu_base + 
404            (cluster_id * (unsigned) CLUSTER_SIZE));
[215]405#if USE_XICU
[228]406    unsigned int prio = icu_address[XICU_REG(XICU_PRIO, proc_id)];
[203]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;
[228]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    }
[189]425#else
[203]426    *buffer = icu_address[proc_id * ICU_SPAN + ICU_IT_VECTOR]; 
427#endif
[189]428
[158]429    return 0;
430}
431
[228]432
[158]433////////////////////////////////////////////////////////////////////////////////
[228]434//     VciGcd driver
[158]435////////////////////////////////////////////////////////////////////////////////
436// The Greater Dommon Divider is a -very- simple hardware coprocessor
[165]437// performing the computation of the GCD of two 32 bits integers.
[158]438// It has no DMA capability.
439////////////////////////////////////////////////////////////////////////////////
440
441////////////////////////////////////////////////////////////////////////////////
[189]442//     _gcd_write()
[158]443// Write a 32-bit word in a memory mapped register of the GCD coprocessor.
444// Returns 0 if success, > 0 if error.
445////////////////////////////////////////////////////////////////////////////////
[228]446unsigned int _gcd_write(unsigned int register_index, unsigned int value) {
[165]447    // parameters checking
[228]448    if (register_index >= GCD_END) {
[158]449        return 1;
[228]450    }
[158]451
[228]452    unsigned int * gcd_address = (unsigned int *) &seg_gcd_base;
[165]453
454    gcd_address[register_index] = value; // write word
[158]455    return 0;
456}
[228]457
458
[158]459////////////////////////////////////////////////////////////////////////////////
[189]460//     _gcd_read()
[158]461// Read a 32-bit word in a memory mapped register of the GCD coprocessor.
462// Returns 0 if success, > 0 if error.
463////////////////////////////////////////////////////////////////////////////////
[228]464unsigned int _gcd_read(unsigned int register_index, unsigned int * buffer) {
[165]465    // parameters checking
[228]466    if (register_index >= GCD_END) {
[158]467        return 1;
[228]468    }
[158]469
[228]470    unsigned int * gcd_address = (unsigned int *) &seg_gcd_base;
[165]471
472    *buffer = gcd_address[register_index]; // read word
[158]473    return 0;
474}
475
476////////////////////////////////////////////////////////////////////////////////
477// VciBlockDevice driver
478////////////////////////////////////////////////////////////////////////////////
[165]479// The VciBlockDevice is a single channel external storage contrÃŽler.
[166]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//
[158]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
[166]518// transfer completion. When the completion is signaled, the _ioc_completed()
[158]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
[189]526// IOC global variables
[228]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; 
[158]532
533///////////////////////////////////////////////////////////////////////////////
[189]534//      _ioc_access()
[166]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.
[158]542// Returns 0 if success, > 0 if error.
543///////////////////////////////////////////////////////////////////////////////
[228]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
[166]558    // check buffer alignment
[228]559    if ((unsigned int) user_vaddr & 0x3) {
560        return 1;
561    }
[158]562
[228]563    unsigned int * ioc_address = (unsigned int *) &seg_ioc_base ;
[204]564
[228]565    unsigned int block_size = ioc_address[BLOCK_DEVICE_BLOCK_SIZE];
566    unsigned int length = count * block_size;
[158]567
[167]568    // get user space page table virtual address
[228]569    unsigned int task_id = _get_current_task_id();
570    unsigned int user_pt_vbase = _get_context_slot(task_id, CTX_PTAB_ID);
571
[166]572    user_vpn_min = user_vaddr >> 12;
573    user_vpn_max = (user_vaddr + length - 1) >> 12;
[228]574    ix2 = 0;
[158]575
[166]576    // loop on all virtual pages covering the user buffer
[228]577    for (vpn = user_vpn_min; vpn <= user_vpn_max; vpn++) {
[166]578        // get ppn and flags for each vpn
[228]579        unsigned int ko = _v2p_translate((page_table_t *) user_pt_vbase, vpn, &ppn, &flags);
[158]580
[166]581        // check access rights
[228]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        }
[158]591
[166]592        // save first ppn value
[228]593        if (ix2 == 0) {
594            ppn_first = ppn;
595        }
[158]596
[228]597        if (IOMMU_ACTIVE) {
598            // the user buffer must be remapped in the I/0 space
[166]599            // check buffer length < 2 Mbytes
[228]600            if (ix2 > 511) {
601                return 2;
602            }
[158]603
[166]604            // map the physical page in IOMMU page table
[228]605            _iommu_add_pte2(
606                    _ioc_iommu_ix1,    // PT1 index
607                    ix2,               // PT2 index
608                    ppn,               // Physical page number   
609                    flags);            // Protection flags
[166]610        }
[228]611        else {
612            // no IOMMU : check that physical pages are contiguous
613            if ((ppn - ppn_first) != ix2) {
614                return 5; // split physical buffer
615            }
[166]616        }
[228]617
[166]618        // increment page index
619        ix2++;
620    } // end for vpn
[158]621
[166]622    // register the number of pages to be unmapped
623    _ioc_iommu_npages = (user_vpn_max - user_vpn_min) + 1;
[158]624
[166]625    // invalidate data cache in case of memory write
[228]626    if (to_mem) {
627        _dcache_buf_invalidate((void *) user_vaddr, length);
628    }
[158]629
[166]630    // compute buffer base address for IOC depending on IOMMU activation
[228]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    }
[166]637
638    // get the lock on ioc device
[228]639    _get_lock(&_ioc_lock);
[158]640
[166]641    // peripheral configuration 
[228]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    }
[158]651
652    return 0;
653}
[228]654
655
[158]656/////////////////////////////////////////////////////////////////////////////////
657// _ioc_completed()
658//
659// This function checks completion of an I/O transfer and reports errors.
[166]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.
[158]663// Returns 0 if success, > 0 if error.
664/////////////////////////////////////////////////////////////////////////////////
[228]665unsigned int _ioc_completed() {
666    unsigned int ret;
667    unsigned int ix2;
[158]668
[166]669    // busy waiting
[228]670    while (_ioc_done == 0) {
[158]671        asm volatile("nop");
[228]672    }
[158]673
[166]674    // unmap the buffer from IOMMU page table if IOMMU is activated
[228]675    if (IOMMU_ACTIVE) {
676        unsigned int * iob_address = (unsigned int *) &seg_iob_base;
[166]677
[228]678        for (ix2 = 0; ix2 < _ioc_iommu_npages; ix2++) {
[166]679            // unmap the page in IOMMU page table
[228]680            _iommu_inval_pte2(
681                    _ioc_iommu_ix1, // PT1 index
682                    ix2 );          // PT2 index
[166]683
684            // clear IOMMU TLB
[169]685            iob_address[IOB_INVAL_PTE] = (_ioc_iommu_ix1 << 21) | (ix2 << 12); 
[166]686        }
687    }
688
689    // test IOC status
[158]690    if ((_ioc_status != BLOCK_DEVICE_READ_SUCCESS)
[228]691            && (_ioc_status != BLOCK_DEVICE_WRITE_SUCCESS)) {
692        ret = 1; // error
693    }
694    else {
695        ret = 0; // success
696    }
[158]697
[166]698    // reset synchronization variables
[223]699    _ioc_done = 0;
[228]700    asm volatile("sync");
[223]701    _ioc_lock = 0;
[158]702
703    return ret;
704}
[228]705
706
[166]707///////////////////////////////////////////////////////////////////////////////
[189]708//     _ioc_read()
[166]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///////////////////////////////////////////////////////////////////////////////
[228]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);
[166]721}
[228]722
723
[166]724///////////////////////////////////////////////////////////////////////////////
[189]725//     _ioc_write()
[166]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///////////////////////////////////////////////////////////////////////////////
[228]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);
[166]738}
[228]739
740
[204]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///////////////////////////////////////////////////////////////////////////////
[228]746unsigned int _ioc_get_status(unsigned int * status) {
[204]747    // get IOC base address
[228]748    unsigned int * ioc_address = (unsigned int *) &seg_ioc_base;
[166]749
[204]750    *status = ioc_address[BLOCK_DEVICE_STATUS]; // read status & reset IRQ
751    return 0;
752}
753
[228]754
[158]755//////////////////////////////////////////////////////////////////////////////////
[189]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
[213]771#if NB_DMAS_MAX > 0
[228]772in_unckdata unsigned int            _dma_lock[NB_DMAS_MAX * NB_CLUSTERS] = {
773    [0 ... (NB_DMAS_MAX * NB_CLUSTERS) - 1] = 0
774};
[189]775
[228]776in_unckdata volatile unsigned int    _dma_done[NB_DMAS_MAX * NB_CLUSTERS] = {
777    [0 ... (NB_DMAS_MAX * NB_CLUSTERS) - 1] = 0
778};
[189]779
[228]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];
[213]783#endif
[189]784
785//////////////////////////////////////////////////////////////////////////////////
[204]786// _dma_reset_irq()
787//////////////////////////////////////////////////////////////////////////////////
[228]788unsigned int _dma_reset_irq(unsigned int cluster_id, unsigned int channel_id) {
[213]789#if NB_DMAS_MAX > 0
[204]790    // parameters checking
[228]791    if (cluster_id >= NB_CLUSTERS) {
792        return 1;
793    }
794    if (channel_id >= NB_DMAS_MAX) {
795        return 1;
796    }
[204]797
798    // compute DMA base address
[228]799    unsigned int * dma_address = (unsigned int *) ((char *) &seg_dma_base + 
800            (cluster_id * (unsigned) CLUSTER_SIZE));
[204]801
[228]802    dma_address[channel_id * DMA_SPAN + DMA_RESET] = 0;           
[204]803    return 0;
[213]804#else
805    return -1;
806#endif
[204]807}
[218]808
[228]809
[204]810//////////////////////////////////////////////////////////////////////////////////
811// _dma_get_status()
812//////////////////////////////////////////////////////////////////////////////////
[228]813unsigned int _dma_get_status(unsigned int cluster_id, unsigned int channel_id, unsigned int * status) {
[213]814#if NB_DMAS_MAX > 0
[204]815    // parameters checking
[228]816    if (cluster_id >= NB_CLUSTERS) {
817        return 1;
818    }
819    if (channel_id >= NB_DMAS_MAX) {
820        return 1;
821    }
[204]822
823    // compute DMA base address
[228]824    unsigned int * dma_address = (unsigned int *) ((char *) &seg_dma_base + 
825            (cluster_id * (unsigned) CLUSTER_SIZE));
[207]826
[228]827    *status = dma_address[channel_id * DMA_SPAN + DMA_LEN];
[204]828    return 0;
[213]829#else
830    return -1;
831#endif
[204]832}
833
[228]834
[204]835//////////////////////////////////////////////////////////////////////////////////
[218]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.
[207]848// The user buffer must be mapped in user address space and word-aligned.
[169]849// The user buffer length must be multiple of 4 bytes.
[218]850// Me must compute the physical base addresses for both the device buffer
[189]851// and the user buffer before programming the DMA transfer.
[207]852// The GIET being fully static, we don't need to split the transfer in 4 Kbytes
[189]853// pages, because the user buffer is contiguous in physical space.
[158]854// Returns 0 if success, > 0 if error.
855//////////////////////////////////////////////////////////////////////////////////
[228]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) {
[213]862#if NB_DMAS_MAX > 0
[228]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
[158]869
[189]870    // check user buffer address and length alignment
[228]871    if ((user_vaddr & 0x3) || (length & 0x3)) {
[203]872        _get_lock(&_tty_put_lock);
[218]873        _puts("\n[GIET ERROR] in _dma_transfer : user buffer not word aligned\n");
[203]874        _release_lock(&_tty_put_lock);
[189]875        return 1;
876    }
[169]877
[218]878    // get DMA channel and compute DMA vbase address
[228]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));
[218]885
886    // get page table address
[228]887    unsigned int user_ptab = _get_context_slot( task_id, CTX_PTAB_ID);
[169]888
[218]889    // get peripheral buffer virtual address
[228]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    }
[189]896
[218]897    // get device buffer physical address
[228]898    ko = _v2p_translate((page_table_t *) user_ptab, (device_vaddr >> 12), &ppn, &flags);
899    if (ko) {
[203]900        _get_lock(&_tty_put_lock);
[218]901        _puts("\n[GIET ERROR] in _dma_transfer : device buffer unmapped\n");
[203]902        _release_lock(&_tty_put_lock);
[189]903        return 2;
904    }
[218]905    device_pbase = (ppn << 12) | (device_vaddr & 0x00000FFF);
[189]906
[218]907    // Compute user buffer physical address
[228]908    ko = _v2p_translate( (page_table_t*)user_ptab, (user_vaddr >> 12), &ppn, &flags);
909    if (ko) {
[203]910        _get_lock(&_tty_put_lock);
[218]911        _puts("\n[GIET ERROR] in _dma_transfer() : user buffer unmapped\n");
[203]912        _release_lock(&_tty_put_lock);
[189]913        return 3;
914    } 
[228]915    if ((flags & PTE_U) == 0) {
[203]916        _get_lock(&_tty_put_lock);
[218]917        _puts("[GIET ERROR] in _dma_transfer() : user buffer not in user space\n");
[203]918        _release_lock(&_tty_put_lock);
[189]919        return 4; 
920    }
[228]921    if (((flags & PTE_W) == 0 ) && to_user) {
[203]922        _get_lock(&_tty_put_lock);
[218]923        _puts("\n[GIET ERROR] in _dma_transfer() : user buffer not writable\n");
[203]924        _release_lock(&_tty_put_lock);
[189]925        return 5;
926    }
[218]927    user_pbase = (ppn << 12) | (user_vaddr & 0x00000FFF);
[189]928
[228]929    /*  This is a draft for IOMMU support
930
[189]931    // loop on all virtual pages covering the user buffer
[169]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;
[158]936
[169]937    for ( vpn = user_vpn_min ; vpn <= user_vpn_max ; vpn++ )
938    {
[228]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 );
[158]944
[228]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
[158]949
[228]950    // save first ppn value
951    if ( ix2 == 0 ) ppn_first = ppn;
[169]952
[228]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;
[169]957
[228]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    }
[169]968
[228]969    // increment page index
970    ix2++;
[169]971    } // end for vpn
972
[189]973    // register the number of pages to be unmapped if iommu activated
[169]974    _dma_iommu_npages[dma_id] = (user_vpn_max - user_vpn_min) + 1;
975
[189]976*/
[204]977
[169]978    // invalidate data cache in case of memory write
[228]979    if (to_user) {
980        _dcache_buf_invalidate((void *) user_vaddr, length);
981    }
982
[189]983    // get the lock
[228]984    _get_lock(&_dma_lock[dma_id]);
[169]985
986    // DMA configuration
[228]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;
[169]990    }
[228]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;
[169]994    }
[228]995    dma_base[loc_id * DMA_SPAN + DMA_LEN] = (unsigned int) length;
996
[158]997    return 0;
[213]998#else //NB_DMAS_MAX == 0
999    return -1;
1000#endif
[218]1001}  // end _dma_transfer() 
1002
[228]1003
[169]1004//////////////////////////////////////////////////////////////////////////////////
[218]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//////////////////////////////////////////////////////////////////////////////////
[228]1012unsigned int _dma_completed() {
[218]1013#if NB_DMAS_MAX > 0
[228]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;
[218]1017
1018    // busy waiting with a pseudo random delay between bus access
[228]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"); 
[218]1030    }
[228]1031
1032    /* draft support for IOMMU
[218]1033    // unmap the buffer from IOMMU page table if IOMMU is activated
1034    if ( GIET_IOMMU_ACTIVE )
1035    {
[228]1036    unsigned int* iob_address = (unsigned int*)&seg_iob_base;
[218]1037
[228]1038    unsigned int  ix1         = _dma_iommu_ix1 + dma_id;
1039    unsigned int  ix2;
[218]1040
[228]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
[218]1046
[228]1047    // clear IOMMU TLB
1048    iob_address[IOB_INVAL_PTE] = (ix1 << 21) | (ix2 << 12);
[218]1049    }
[228]1050    }
1051    */
[218]1052
1053    // reset synchronization variables
[223]1054    _dma_done[dma_id] = 0;
[225]1055    dma_ret = _dma_status[dma_id];
1056    asm volatile("sync\n");
[218]1057    _dma_lock[dma_id] = 0;
1058
[223]1059    return dma_ret;
[218]1060
1061#else //NB_DMAS_MAX == 0
1062    return -1;
1063#endif
1064}  // end _dma_completed
1065
1066//////////////////////////////////////////////////////////////////////////////////
[228]1067//     VciFrameBuffer driver
[218]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//////////////////////////////////////////////////////////////////////////////////
[228]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);
[218]1092    return 0;
1093}
1094
[228]1095
[218]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//////////////////////////////////////////////////////////////////////////////////
[228]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);
[218]1106    return 0;
1107}
1108
[228]1109
[218]1110//////////////////////////////////////////////////////////////////////////////////
[169]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//////////////////////////////////////////////////////////////////////////////////
[228]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);
[158]1125}
1126
[228]1127
[158]1128//////////////////////////////////////////////////////////////////////////////////
1129// _fb_read()
[169]1130// Transfer data from the frame_buffer device to a memory buffer using  DMA.
[158]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//////////////////////////////////////////////////////////////////////////////////
[228]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);
[158]1143}
1144
[228]1145
[158]1146//////////////////////////////////////////////////////////////////////////////////
1147// _fb_completed()
1148// This function checks completion of a DMA transfer to or fom the frame buffer.
[169]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)
[158]1152//////////////////////////////////////////////////////////////////////////////////
[228]1153unsigned int _fb_completed() {
[218]1154    return _dma_completed();
1155}
[158]1156
[218]1157//////////////////////////////////////////////////////////////////////////////////
[228]1158//     VciMultiNic driver
[218]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//////////////////////////////////////////////////////////////////////////////////
[204]1172
[218]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//////////////////////////////////////////////////////////////////////////////////
[228]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);
[218]1183    return 0;
1184}
[158]1185
[228]1186
[218]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//////////////////////////////////////////////////////////////////////////////////
[228]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);
[218]1197    return 0;
1198}
[158]1199
[228]1200
[218]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//////////////////////////////////////////////////////////////////////////////////
[228]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 );   
[218]1216}
[169]1217
[228]1218
[218]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//////////////////////////////////////////////////////////////////////////////////
[228]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 );   
[218]1234}
[189]1235
[228]1236
[218]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//////////////////////////////////////////////////////////////////////////////////
[228]1244unsigned int _nic_completed() {
[218]1245    return _dma_completed();
[158]1246}
1247
[228]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.