Forum Linux.embarqué problème avec dma_map_single

Posté par  . Licence CC By‑SA.
Étiquettes :
0
5
avr.
2021

Bonjour,
Je travaille sur un imx7d de NxP. Je travaille sur 2 cartes l'eval board de Nxp (la carte sabre) et une board personnelle. J'ai développé un driver memcpy qui utilise le sdma de l'imx7d. Le but étant de copier les données reçu du pci express dans une mémoire vers une autre mémoire. ​
Ce driver fonctionne de la façon suivante l'utilisateur alloue de la mémoire via un ioctl cette mémoire étant la zone de réception. Puis quand il souhaite faire une memcpy il utilise le read du driver en mettant en paramètre la quantité de lecture qu'il veut copié. C'est la qu'intervient le sdma il va faire une copie de la quantité de données indiqué dans le read de l'adresse physique qui a été préalablement indiqué par l'utilisateur vers la zone mémoire allouée.
J'ai fait des teste de ce driver au début sur la carte Sabre. Et la il fonctionnait tres bien.
​J'ai ensuite testé ce driver sur ma board personnel et la problème.
Le problème est sur la fonction dma_map_single. Plus exactement sur dma_cache_maint_page et exactement sur
l'appelle de PageHighMem(page) dans dma_cache_maint_page.
Les deux seule différencew entre ma board personnel et l'eval board sont:
- la taille de la ddr sur ma board j'ai 512M alors que sur la sabre il y a 1G.
- l'adresse physique source 0x90000000 pour ma board et 0xB0000000 pour la sabre.
Les mémoire allouées sont assé grosse de l'odre de 1M et la copie peut être entre 300K et 1M.
J'ai déjà essayé beaucoup de choses. J'ai essayé de reduire la taille copiée à 8K mais pas mieux.
Donc j'avoue que la je suis un peu perdu je pense que j'ai du mal à maitriser ces histoire de dma et d'allocation.
Donc j'ai besoin d'aide merci d'avance pour vos réponses.

Yodalesage
Ci-dessous je mets le log linux
Je vous join aussi mon driver il n'est certainement pas parfait il manque plein de securité donc vos remarque sont le bienvenu pour m'améliorer

Unable to handle kernel paging request at virtual address 9ee4f240
pgd = 99e50000
[9ee4f240] *pgd=00000000
Internal error: Oops: 5 [#1] PREEMPT SMP ARM
Modules linked in: usb_f_acm u_serial g_serial libcomposite configfs imx_rpmsg_tty cdc_acm goodix evbug
CPU: 1 PID: 566 Comm: testTactile Not tainted 4.9.11-1.0.0+gc27010d #52
Hardware name: Freescale i.MX7 Dual (Device Tree)
task: 9931d280 task.stack: 9ad10000
PC is at dma_cache_maint_page+0x50/0x128
LR is at 0x81000
pc : [<80113480>]    lr : [<00081000>]    psr: 600d0013
sp : 9ad11e20  ip : 9ea19000  fp : 80f030d4
r10: 00000000  r9 : 8011362c  r8 : 80117bf8
r7 : 00000002  r6 : 000a2b12  r5 : 00000000  r4 : 0004b05c
r3 : 00021b12  r2 : 0004b05c  r1 : 00081000  r0 : 00436240
Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
Control: 10c53c7d  Table: 9ae5006a  DAC: 00000051
Process testTactile (pid: 566, stack limit = 0x9ad10210)
Stack: (0x9ad11e20 to 0x9ad12000)
1e20: 80f06384 80fd7644 8016fbfc 00000000 9ee4f240 0004b05c 00000002 00000002
1e40: 8011362c 00000000 6c017e54 80113580 80117bf8 801b8d6c 1b6b0b00 9ee4f240
1e60: 990c8210 00000000 0004b05c 80113690 99ec2c00 9934c11c 00000000 80512440
1e80: 00000002 00000000 00000000 00000000 9919a998 99bc9d38 99b6e240 00000004
1ea0: 00000013 80e6a900 9ad11f04 802085cc 00000000 99ec2c00 80512514 0004b05c
1ec0: 9ad11f80 00000000 0004b05c 80512540 00000000 00000013 9ad11f80 00000000
1ee0: 00000000 00000000 6c017e54 99b7b000 80512514 801ffc30 00000000 01074ee0
1f00: 00000013 00000001 00000000 00000000 9ad11f04 00000000 99b6e240 00000000
1f20: 00000000 00000000 00000000 00000000 00000000 99b6e240 00000000 9b344408
1f40: 00000002 99b7b000 00000000 0004b05c 9ad11f80 00000000 0004b05c 80200a08
1f60: 7efff612 00000003 99b7b001 99b7b000 00000000 00000000 00000000 802017f4
1f80: 00000000 00000000 00000000 0013f6a0 734ee858 7efff612 00000003 80107804
1fa0: 9ad10000 80107640 0013f6a0 734ee858 00000023 00000000 0004b05c 0004b05c
1fc0: 0013f6a0 734ee858 7efff612 00000003 7efff9f8 6c017f90 00000000 6c017e54
1fe0: 00000000 6c017cd0 00000000 76154ed8 800d0010 00000023 00000000 00000000
[<80113480>] (dma_cache_maint_page) from [<80113580>] (__dma_page_cpu_to_dev+0x28/0x90)
[<80113580>] (__dma_page_cpu_to_dev) from [<80113690>] (arm_dma_map_page+0x64/0x68)
[<80113690>] (arm_dma_map_page) from [<80512440>] (memcpydevSrc+0xfc/0x1d0)
[<80512440>] (memcpydevSrc) from [<80512540>] (memcpydev_read+0x2c/0xa0)
[<80512540>] (memcpydev_read) from [<801ffc30>] (__vfs_read+0x1c/0x10c)
[<801ffc30>] (__vfs_read) from [<80200a08>] (vfs_read+0x8c/0x118)
[<80200a08>] (vfs_read) from [<802017f4>] (SyS_read+0x3c/0x90)
[<802017f4>] (SyS_read) from [<80107640>] (ret_fast_syscall+0x0/0x3c)
Code: e08e1621 e1a07003 e08162c0 e046300e (e79c2283)
---[ end trace a2d887416e214d33 ]---
/* Kernel Programming */
/**=======================================================
 * @file    memcpydev.c
 * @brief   Manage memcopy to or from device.
 * If use the function read then the memcopy is mem from dev
 * if use the function write then the memcopy is mem to dev.
 * Use ioctl function to manage the memory (size ngb memory in fifo)
 * Use mmap to manipulate the memory.
 *
 * $Author: frdl $
 * $Date: 2017-12-16 04:38:16 +0100 (sam. 16 déc. 2017) $
 * $Revision: 559 $
 *
 * $LastChangedBy: frdl $
 * $LastChangedDate: 2017-12-16 04:38:16 +0100 (sam. 16 déc. 2017) $
 * $LastChangedRevision: 559 $
========================================================*/



static int memcpydev_probe(struct platform_device* device);
static int memcpydev_remove(struct platform_device* device);

static bool dma_mem_filter(struct dma_chan* chan, void* param)
{
    if (!imx_dma_is_general_purpose(chan))
    {
        return false;
    }
    chan->private = param;
    return true;
}


void dma_mem_deinit(stMemCpy_t* memcpyDev)
{
    kfree(memcpyDev->dma_mem_ok);
}

int dma_mem_init(stMemCpy_t* memcpyDev)
{
    dma_cap_mask_t dma_mem_mask;
    struct imx_dma_data mem_dma_data = {0};

    memcpyDev->dma_mem_ok = kmalloc(sizeof(struct completion), GFP_KERNEL);
    init_completion(memcpyDev->dma_mem_ok);

    dma_cap_zero(dma_mem_mask);
    dma_cap_set(DMA_SLAVE, dma_mem_mask);
    mem_dma_data.peripheral_type = IMX_DMATYPE_MEMORY;
    mem_dma_data.priority = DMA_PRIO_HIGH;

    memcpyDev->dma_mem_chan = dma_request_channel(dma_mem_mask, dma_mem_filter, &mem_dma_data);
    if (!memcpyDev->dma_mem_chan)
    {
        printk("Error opening the SDMA memory to memory channel\n");
        return -EINVAL;
    }

    return 0;
}

static void dma_mem_callbackSrc(void* data)
{
    stMemCpy_t* memcpyDev;

    memcpyDev = (stMemCpy_t*)data;
    memcpyDev->readStatus = MEMCPY_OK;
    wake_up_interruptible(&(memcpyDev->rwq));
}


static void dma_mem_callbackDst(void* data)
{
    stMemCpy_t* memcpyDev;

    memcpyDev = (stMemCpy_t*)data;
    memcpyDev->writeStatus = MEMCPY_OK;
    wake_up_interruptible(&(memcpyDev->wwq));
}


int memcpydevSrc(stMemCpy_t* memcpyDev,unsigned long size)
{
    struct dma_async_tx_descriptor* dma_m2m_desc;
    struct dma_chan* dma_chan = memcpyDev->dma_mem_chan;
    dma_addr_t                      dma_dst;
    struct dma_slave_config dma_m2m_config;
    int status = 0;

    /*struct timeval tv1s, tv1e, tv2s, tv2e;
    u32 tv_count1, tv_count2;*/
    // do_gettimeofday(&tv1s);
    dma_m2m_config.direction = DMA_MEM_TO_MEM;
    dma_m2m_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
    dmaengine_slave_config(dma_chan, &dma_m2m_config);

    if (size % 4 ==  0)
    {



        dma_dst = dma_map_single(dma_chan->device->dev,
                                 memcpyDev->mem.memAllocate,
                                 size,
                                 DMA_FROM_DEVICE);


        dma_m2m_desc = dma_chan->device->device_prep_dma_memcpy(dma_chan,
                       dma_dst,
                       (unsigned int)(memcpyDev->phyDevSrc),
                       size,
                       DMA_CTRL_ACK );

        if (dma_m2m_desc)
        {

            dma_m2m_desc->callback = dma_mem_callbackSrc;
            dma_m2m_desc->callback_param = memcpyDev;
            //   do_gettimeofday(&tv2s);
            dma_cookie_t dmasend = dmaengine_submit(dma_m2m_desc);

            dma_async_issue_pending(dma_chan);

            //  wait_for_completion(memcpyDev->dma_mem_ok);



            printk(KERN_INFO "the transfet phy src dma ok\n\r");

            /*    enum dma_status statusDma = dma_async_is_tx_complete(dma_chan,
                                       dmasend, NULL, NULL);

                while (statusDma == DMA_IN_PROGRESS || statusDma == DMA_PAUSED)
                {
                    statusDma = dma_async_is_tx_complete(dma_chan, dmasend, NULL, NULL);
                }*/


            /*   do_gettimeofday(&tv2e);


               do_gettimeofday(&tv1e);
               //  wake_up_interruptible(&(memcpyDev->rwq));
               printk(KERN_INFO "le wake interruptible est transmit pour phy src\n\r");


               tv_count1 = (tv1e.tv_sec - tv1s.tv_sec)
                           * USEC_PER_SEC
                           + tv1e.tv_usec - tv1s.tv_usec;
               tv_count2 = (tv2e.tv_sec - tv2s.tv_sec)
                           * USEC_PER_SEC
                           + tv2e.tv_usec - tv2s.tv_usec;

               pr_info("pcie ep: Data transfer is successful."
                       " tv_count1 %dus,"
                       " tv_count2 %dus.\n",
                       tv_count1, tv_count2);
               pr_info("pcie ep: Data write speed:%ldMB/s.\n",
                       ((memcpyDev->mem.size / 1024)
                        * MSEC_PER_SEC)
                       / (tv_count1));
               pr_info("pcie ep: Data read speed:%ldMB/s.\n",
                       ((memcpyDev->mem.size / 1024)
                        * MSEC_PER_SEC)
                       / (tv_count2));*/
        }
        else
        {
            printk(KERN_ERR "the transfet dma KOKOKOKO\n\r");
            status = -1;
            memcpyDev->readStatus = MEMCPY_ERROR;
            wake_up_interruptible(&(memcpyDev->rwq));
        }

    }
    else
    {
        status = -1;
        printk(KERN_ERR "error size sdma\n\r");
        status = -1;
        memcpyDev->readStatus = MEMCPY_ERROR;
        wake_up_interruptible(&(memcpyDev->rwq));
    }


    return status;
}


int memcpydevDest(stMemCpy_t* memcpyDev,unsigned long size)
{
    struct dma_async_tx_descriptor* dma_m2m_desc;
    struct dma_chan* dma_chan = memcpyDev->dma_mem_chan;
    dma_addr_t                      dma_dst;
    struct dma_slave_config dma_m2m_config;
    int status = 0;


    dma_m2m_config.direction = DMA_MEM_TO_MEM;
    dma_m2m_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
    dmaengine_slave_config(dma_chan, &dma_m2m_config);

    if (size % 4 ==  0)
    {



        dma_dst = dma_map_single(dma_chan->device->dev,
                                 memcpyDev->mem.memAllocate,
                                 size,
                                 DMA_TO_DEVICE);



        dma_m2m_desc = dma_chan->device->device_prep_dma_memcpy(dma_chan,
                       (unsigned int)memcpyDev->phyDevDst,
                       dma_dst,
                       size,
                       DMA_CTRL_ACK);

        if (dma_m2m_desc)
        {
            dma_m2m_desc->callback = dma_mem_callbackDst;
            dma_m2m_desc->callback_param = memcpyDev->dma_mem_ok;
            dmaengine_submit(dma_m2m_desc);

            dma_async_issue_pending(dma_chan);

            wait_for_completion(memcpyDev->dma_mem_ok);
            printk(KERN_INFO "the transfet dma ok\n\r");
            //  wake_up_interruptible(&(memcpyDev->wwq));
            printk(KERN_INFO "le wake interruptible est transmit\n\r");
        }
        else
        {
            printk(KERN_ERR  "the transfet dma KOKOKOKO\n\r");
            status = -1;
            memcpyDev->writeStatus = MEMCPY_ERROR;
            wake_up_interruptible(&(memcpyDev->wwq));
        }

    }
    else
    {
        printk(KERN_ERR "error size sdma\n\r");
        status = -1;
        memcpyDev->writeStatus = MEMCPY_ERROR;
        wake_up_interruptible(&(memcpyDev->wwq));
    }


    return status;
}

/**
Cette fonction va allouer la taille mémoire demandée et
stocker les différents pointeurs dans la structure stAllocateMem_t.
 */
static int allocate_cbuffer(stAllocateMem_t* sharedMem, unsigned long size)
{
    size_t total_size;
    dma_addr_t handle;
    //Around to PAGE_SIZE (for mmap)
    total_size = (( size / PAGE_SIZE) + 1) * PAGE_SIZE;
    sharedMem->memAllocate = dma_alloc_coherent(NULL, total_size, &handle, GFP_KERNEL);
    if ( sharedMem->memAllocate == NULL)
    {
        printk(KERN_ERR "sharedMem: fail dma_alloc_coherent mem of size :%lud and total size:%lud", size, total_size);
        return -ENOMEM;
    }

    sharedMem->physMemAllocate = virt_to_phys(sharedMem->memAllocate);
    sharedMem->handle = handle;
    sharedMem->size = size;
    sharedMem->totalSize = total_size;
    return 0;
}

/**
Cette fonction va désallouer la mémoire.
 */
static int deallocate_cbuffer(stAllocateMem_t* sharedMem)
{
    dma_free_coherent(NULL, sharedMem->totalSize , sharedMem->memAllocate,
                      sharedMem->handle);

    sharedMem->memAllocate = NULL;
    sharedMem->physMemAllocate = NULL;
    sharedMem->totalSize = 0;
    sharedMem->size = 0;

    return 0;
}


/**
Cette fonction retourne le pointeur de l’adresse physique dans l’espace user
 */
static long allocateMem_ioctl_get_phys_add( stAllocateMem_t*  sharedMem,
        unsigned long __user*  phys)
{
    return copy_to_user( phys, sharedMem->physMemAllocate, sizeof(off_t)) ? -EFAULT : 0;
}
/**
Cette fonction va appeler allocate_cbuffer pour allouer de la mémoire
avec le paramètre size spécifié par la structure.
Puis renvoyer dans la structure de retour, l’adresse physique.
Remarque __user signifie que le paramètre vient de l’espace user
et donc on ne peut pas le manipuler comme ça.
 */
static long allocateMem_ioctl_alloc_buffer( stAllocateMem_t*  sharedMem, sharedMem_ioctl_t __user*  u_alloc)
{
    sharedMem_ioctl_t           k_alloc;
    if (copy_from_user(&k_alloc, u_alloc, sizeof(k_alloc)))
    {
        return -EFAULT;
    }

    if (allocate_cbuffer(sharedMem, k_alloc.size) < 0)
    {
        printk(KERN_ERR "sharedMem: fail to allocate mem of size %lud\n", k_alloc.size);
        return -EFAULT;
    }
    if (copy_to_user(&(u_alloc->totalSize), &(sharedMem->totalSize), sizeof(off_t)))
    {
        return -EFAULT;
    }
    return copy_to_user(&(u_alloc->physAddr), &(sharedMem->physMemAllocate), sizeof(off_t)) ? -EFAULT : 0;
}



/**
Cette fonction va désallouer la mémoire en appelant la fct deallocate_cbuffer.
 */
static long allocateMem_ioctl_dealloc_buffer( stAllocateMem_t*  sharedMem)
{

    if (deallocate_cbuffer(sharedMem) < 0)
    {
        printk(KERN_ERR "sharedMem: fail to deallocate mem\n");
        return -EFAULT;
    }

    return 0;
}



/**
Cette fonction alloue et initialise à NULL une structure stAllocateMem_t.
Cette structure sera la base. Le pointeur alloué sera stocké dans
le private data du file ouvert.
Il sera donc possible d’ouvrir autant de driver que l’on souhaite.
 */
static int memcpydev_open(struct inode* inode, struct file* file)
{
    stMemCpy_t* memCpy;
    //prepare the context
    memCpy = kmalloc(sizeof(stMemCpy_t), GFP_KERNEL);
    if (!memCpy)
    {
        printk(KERN_ERR "memcpydev: fail to allocate stMemCpy_t structure\n");
        return -ENOMEM;
    }
    memCpy->phyDevSrc = NULL;
    memCpy->phyDevDst = NULL;
    memCpy->mem.memAllocate = NULL;
    memCpy->mem.physMemAllocate = NULL;
    memCpy->mem.size = 0;
    memCpy->mem.totalSize = 0;
    memCpy->mem.vma = NULL;
    memCpy->readStatus = MEMCPY_NO_TRANSFERT;
    memCpy->writeStatus = MEMCPY_NO_TRANSFERT;

    init_waitqueue_head(&(memCpy->wwq));
    init_waitqueue_head(&(memCpy->rwq));

    dma_mem_init(memCpy);

    //store context in private data
    file->private_data = memCpy;
    return 0;
}

/**
La fonction libère la mémoire partagé si
ce n’est pas déjà fait. ET libère l’espace alloué par le contexte.
 */
static int memcpydev_release(struct inode* inode, struct file* file)
{
    stMemCpy_t* memCpy = (stMemCpy_t*)file->private_data;
    //appelle d’une fct de des-allocation du buffer de partage
    deallocate_cbuffer(&memCpy->mem);

    dma_mem_deinit(memCpy);

    kfree(memCpy);
    return 0;
}


/**
fonction ioctl:
IOCTL_GET_PHYS_ADD_MEM : retourne l'adresse physique de la mémoire
IOCTL_ALLOCATE_BUFFER : alloue de la mémoire et retourne la structure avec les info mem physique et taille de memoire reellemenet alloue
IOCTL_DEALLOCATE_BUFFER : desalloue la mémoire
 */
static long memcpydev_ioctl(struct file* file, unsigned int cmd, unsigned long arg)
{
    long status = -1;
    stMemCpy_t* memCpy = (stMemCpy_t*)file->private_data;
    void __user* ptr = (void __user*)arg;

    switch (cmd)
    {
        case IOCTL_GET_PHYS_ADD_MEM:
            return allocateMem_ioctl_get_phys_add(&memCpy->mem, ptr);

        case IOCTL_ALLOCATE_BUFFER:
            return allocateMem_ioctl_alloc_buffer(&memCpy->mem, ptr);

        case IOCTL_DEALLOCATE_BUFFER:
            return allocateMem_ioctl_dealloc_buffer(&memCpy->mem);
        case IOCTL_SET_PHY_ADDR_SRC:
        {
            unsigned long           phyAddSrc;
            if (copy_from_user(&phyAddSrc, ptr, sizeof(unsigned long)))
            {
                printk(KERN_ERR "error no copy from user physical src  \n\t");
                status = -EFAULT;
            }
            else
            {
                printk(KERN_INFO "OK copy from user physical src 0X%x \n\t",phyAddSrc);
                memCpy->phyDevSrc = (void*)phyAddSrc;
                status = 0;
            }
        }
        break;
        case IOCTL_GET_PHY_ADDR_SRC:
        {
            unsigned long           phyAddSrc = (unsigned long) memCpy->phyDevSrc;
            if (copy_to_user(ptr, &phyAddSrc, sizeof(unsigned long)))
            {
                printk(KERN_ERR "error no copy from kernel physical src  \n\t");
                status = -EFAULT;
            }
            else
            {
                printk(KERN_INFO "OK copy from kernel physical src \n\t");
                status = 0;
            }
        }
        break;
        case IOCTL_SET_PHY_ADDR_DST:
        {
            unsigned long           phyAddDst;
            if (copy_from_user(&phyAddDst, ptr, sizeof(unsigned long)))
            {
                printk(KERN_ERR "error no copy from user physical dest  \n\t");
                status = -EFAULT;
            }
            else
            {
                printk(KERN_INFO "OK copy from user physical dest \n\t");
                memCpy->phyDevDst = (void*)phyAddDst;
                status = 0;
            }


        }
        break;
        case IOCTL_GET_PHY_ADDR_DST:
        {
            unsigned long           phyAddDst = (unsigned long) memCpy->phyDevDst;
            if (copy_to_user(ptr, &phyAddDst, sizeof(unsigned long)))
            {
                printk(KERN_ERR "error no copy from kernel physical dest \n\t");
                status = -EFAULT;
            }
            else
            {
                printk(KERN_INFO "OK copy from kernel physical dest \n\t");
                status = 0;
            }
        }
        break;
        default:
            break;
    }

    return status;
}

/**
Cette fonction donne accès au user space à l’emplacement mémoire allouer par le driver.
Du coup depuis le user space il est possible d’accéder directement à la mémoire.
Il faudra s’il y a des problèmes de cache. Rendre cette mémoire non cachable au
moment de l’allocation.
 */
static int memcpydev_mmap(struct file* file, struct vm_area_struct* vma)
{
    int ret;
    stMemCpy_t* memCpy = (stMemCpy_t*)file->private_data;
    stAllocateMem_t* sharedMem = &memCpy->mem;
    if ( sharedMem->physMemAllocate != NULL && sharedMem->size != 0 && sharedMem->memAllocate != NULL && sharedMem->vma == NULL)
    {

        long decphysMemLong = (((long)(sharedMem->physMemAllocate)) >> PAGE_SHIFT);
        unsigned long decphysMemULong = (((unsigned long)(sharedMem->physMemAllocate)) >> PAGE_SHIFT);
        printk(KERN_INFO "sharedMemDrv: dec long decphysMemLong==0x%lx and dec ulong decphysMemULong=0x%ulx and vma->vm_pgoff==0x%ulx with PAGE_SHIFT==%d \n", decphysMemLong, decphysMemULong, vma->vm_pgoff, PAGE_SHIFT);
        if (decphysMemULong == vma->vm_pgoff)
        {
#ifdef MEM_NO_CACHE
            vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
#endif

            ret = remap_pfn_range(vma, vma->vm_start,
                                  vma->vm_pgoff,
                                  vma->vm_end - vma->vm_start,
                                  vma->vm_page_prot);
            if (ret < 0)
            {
                printk(KERN_ERR "sharedMemDrv: error function remap_pfn_range\n");
            }
            else
            {
                sharedMem->vma = vma;
            }


            return ret;
        }
        else
        {
            printk(KERN_ERR "sharedMemDrv: failed mmap phys Mem Allocate si not around PAGE_SHIFT because physMemAllocate==0x%lx and vma->vm_pgoff==0x%lx with PAGE_SHIFT==%d \n", (long)(sharedMem->physMemAllocate), vma->vm_pgoff, PAGE_SHIFT);
            return -EINVAL;
        }
    }
    printk(KERN_ERR "sharedMemDrv: failed because physMemAllocate == 0x%lx or sharedMem->size == %uld or sharedMem->memAllocate=0x%lx\n", (long)(sharedMem->physMemAllocate), sharedMem->size, (long)(sharedMem->memAllocate));
    return -EINVAL;
}


static int memcpydev_fsync(struct file* file, loff_t start, loff_t end,
                           int datasync)
{
#ifndef MEM_NO_CACHE
    printk("###### start memcpydev_fsync ########\n");
    int ret;
    stMemCpy_t* memCpy = (stMemCpy_t*)file->private_data;
    stAllocateMem_t* sharedMem = &memCpy->mem;
    if (sharedMem->vma != NULL)
    {
        printk("{{{{{{memcpydevDrv: datasync no MS_SYNC because datasync: %uld}}}}}}}}\n", datasync);

        ret = flush_cache_user_range(start, end);
    }
    else
    {
        printk(KERN_ERR "memcpydevDrv: failed fsync because no mmap the device\n");
        return -ENOMEM;
    }
    return 0;
#else
    return -EINVAL;
#endif
}


ssize_t memcpydev_write(struct file* file, const char __user* buf, size_t size, loff_t* offset)
{
    ssize_t                             ret_val = 0;
    stMemCpy_t* memCpy = (stMemCpy_t*)file->private_data;


    if(size<=memCpy->mem.size)
    {
        memCpy->writeStatus = MEMCPY_WAIT;
        //launch a memcpy mem from dev
        ret_val = memcpydevDest(memCpy,size);
        if(!ret_val)
            ret_val = wait_event_interruptible(memCpy->wwq, memCpy->writeStatus != MEMCPY_WAIT);
    }
    else
        ret_val=-1;
    return ret_val;
}

ssize_t memcpydev_read(struct file* file, char __user* buf, size_t size, loff_t* offset)
{
    int                             ret_val = 0;
    stMemCpy_t* memCpy = (stMemCpy_t*)file->private_data;

    if(size<=memCpy->mem.size)
    {
        memCpy->readStatus = MEMCPY_WAIT;
        //launch a memcpy mem from dev
        ret_val = memcpydevSrc(memCpy,size);
        if(!ret_val)
            ret_val = wait_event_interruptible(memCpy->rwq, memCpy->readStatus != MEMCPY_WAIT);
    }
    else
        ret_val=-1;
    return ret_val;

}





/**
this struct give the different pointer
 */
static struct file_operations fops =
{
    .owner          = THIS_MODULE, /// indique que c’est ce driver qui est concerné
    .mmap           = memcpydev_mmap,
    .fsync          = memcpydev_fsync,
    .open           = memcpydev_open,
    .release        = memcpydev_release,
    .read           = memcpydev_read,
    .write          = memcpydev_write,
    .unlocked_ioctl = memcpydev_ioctl,
};

/**
this struct allow to identify the device in device-tree
 */
static const struct of_device_id imx_memcpydev_dt_ids[] =
{
    { .compatible = "cam,imx7-memcpydev" },
    { /* sentinel */ }
};

/**
this struct allow that the device be recognized in platform device
 */
static struct platform_driver memcpydev_driver =
{
    .driver = {
        .name = DRIVER_NAME, //nom du driver qui sera initialisé en haut dans le fichier
        .owner = THIS_MODULE,/// indique que c’est ce driver qui est concerné
        .of_match_table = imx_memcpydev_dt_ids,
    },
    .probe = memcpydev_probe,
    .remove = memcpydev_remove,

};


/**
La fonction Probe est la fonction d’initialisation du driver. Elle est appelée, au chargement du noyaux ,
si le driver est en dur ou, au chargement du module, si ce dernier est en module.
Cette fonction alloue de la mémoire pour la structure générale du driver, enregistrer le drivers
comme driver caractères.
 */
static int memcpydev_probe(struct platform_device* device)
{
    stMemCpyDev_t* memcpydev;
    int     ret;
    //alocate memory to struct stSharedMem_t 
    memcpydev = devm_kzalloc(&device->dev, sizeof(stMemCpyDev_t),
                             GFP_KERNEL);

    //initialize major and minor
    ret = alloc_chrdev_region(&memcpydev->first, 0, 1, DEVICE_NAME);
    if (ret < 0)
    {
        printk(KERN_ERR "memcpydevDrv: failed to alloc region\n");
        return -1;
    }

    //record driver char with struct  fops
    cdev_init(&(memcpydev->memCpy_cdev), &fops);
    if ((ret = cdev_add(&(memcpydev->memCpy_cdev), memcpydev->first, 1)) < 0)
    {
        printk(KERN_ERR "memcpydevDrv: failed to add device to the system\n");
        unregister_chrdev_region(memcpydev->first, 1);
        kfree(memcpydev);
        return ret;
    }
    //record class driver
    memcpydev->memCpy_class = class_create(THIS_MODULE, DEVICE_NAME);
    if (IS_ERR(memcpydev->memCpy_class))
    {
        printk(KERN_ERR "memcpydevDrv: failed to create device class\n");
        unregister_chrdev_region(memcpydev->first, 1);
        cdev_del(&(memcpydev->memCpy_cdev));
        kfree(memcpydev);
        return -ENOMEM;
    }


    memcpydev->memCpy_dev = device_create(memcpydev->memCpy_class,  NULL, MKDEV(MAJOR(memcpydev->first), MINOR(memcpydev->first)),
                                          NULL, DEVICE_NAME);
    if (IS_ERR(memcpydev->memCpy_dev))
    {
        printk(KERN_ERR "memcpydevDrv: failed create device and registers it\n");
        unregister_chrdev_region(memcpydev->first, 1);
        cdev_del(&(memcpydev->memCpy_cdev));
        class_destroy(memcpydev->memCpy_class);
        return -ENOMEM;
    }

    printk(KERN_INFO "memcpydevDrv: probe successful! <Major,Minor>: <%d, %d>\n",   MAJOR(memcpydev->first), MINOR(memcpydev->first));

    platform_set_drvdata(device, memcpydev);
    return 0;
}//end of function memcpydev_probe

/**
Cette fonction est appelée à la destruction du driver par exemple
quand un module est déchargé. Elle doit donc tout désallouer et déconnecter.
 */
static int memcpydev_remove(struct platform_device* device)
{
    stMemCpyDev_t* memcpydev;

    memcpydev = platform_get_drvdata(device);

    unregister_chrdev_region(memcpydev->first, 1);
    cdev_del(&(memcpydev->memCpy_cdev));
    class_destroy(memcpydev->memCpy_class);
    device_destroy(memcpydev->memCpy_class, memcpydev->first);

    kfree(memcpydev);
    return 0;
}

MODULE_LICENSE("GPL"); ///précise la licence du driver elle sera GPL
MODULE_AUTHOR("François Delaurat"); ///l’auteur du driver
MODULE_DESCRIPTION("memcpy dev"); //description du driver
MODULE_VERSION("0.1"); //version du module
MODULE_DEVICE_TABLE(of, imx_memcpydev_dt_ids);

//Register driver as a platform driver
static int __init memcpydev_init(void)
{
    printk(KERN_INFO "memcpydev_init");
    return platform_driver_register(&memcpydev_driver );
}

//Unregister driver
static void __exit memcpydev_exit(void)
{
    platform_driver_unregister(&memcpydev_driver );
}

module_init(memcpydev_init);
module_exit(memcpydev_exit);




/*
this struct describe the device generals informations to the driver memcpy
This information is used to creat linux driver:
@li sharedMemDrv_cdev: this struct is used to character device
@li sharedMemDrv_dev
@li sharedMemDrv_class : this high level struct used to driver abstract.
@li first contains the major and minir num of device.
*/
typedef struct stMemCpyDev
{
    struct cdev  memCpy_cdev;
    struct device *memCpy_dev;
    struct class *memCpy_class;
    dev_t first;
}stMemCpyDev_t;





/*
this struct describe the memory shared. There are three
information:
@li memAlloc the alloc pointer to return by allocation function
@li handle : parameter return by allocation function and used to free memory
@li physMemAllocate: physical adress of shared memory
@li size : alloc memory size asked
@li totalsize : it is the parameter size multiple of  PAGE_SIZE
the calculate is  ((size % PAGE_SIZE) + 1) * PAGE_SIZE
@li vma : pt of vma after mmap
*/
typedef struct stAllocateMem
{
    void * memAllocate;
    dma_addr_t handle;
    void * physMemAllocate;
    unsigned long size;
    unsigned long totalSize;
    struct vm_area_struct *vma;
}stAllocateMem_t;



/* this struct describe the different address to source and destination
memory and device. the mem can be the destination memry or the src memory
If use the function write it is source memroy and if use the read function it is 
the destination memroy.
@li phyDevSrc : the physical address to the memcpy mem from device (use read fct). phyDevSrc is address of device
@li phyDevDst : the physical address to memcpy mem to device (use write). phyDevDst is address of device
@li mem : it is the mem allocate to receive or send data.
@li dma_mem_ok : validate the sdma transfer
@li dma_mem_chan : channel dma to send or receive data
@li wwq: wait queue to write
@li rwq: read queue to read
@li readStatus : status memcpy mem from device @see MEMCPY_WAIT and @see MEMCPY_OK and @see MEMCPY_ERROR and @see MEMCPY_NO_TRANSFERT
@li writeStatus : status memcpy mem from device @see MEMCPY_WAIT and @see MEMCPY_OK and @see MEMCPY_ERROR and @see MEMCPY_NO_TRANSFERT 
 */
typedef struct stMemCpy
{
    void* phyDevSrc;
    void* phyDevDst;
    stAllocateMem_t mem;
     //sdma info
    struct completion       *dma_mem_ok;
    struct dma_chan         *dma_mem_chan;
    wait_queue_head_t       wwq;
    wait_queue_head_t       rwq;
    int readStatus;
    int writeStatus;

}stMemCpy_t;


#endif /* _SHAREDMEM_DRV_H_ */

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.