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

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

Adding a syscall to allow a user to know the block size with which the block device has been configured.

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