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

Last change on this file since 235 was 232, checked in by meunier, 12 years ago

Ajout du malloc dans le Giet.

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