Драйвер с поддержкой DMA и непонятное поведение dma_sync_sg_for_cpu
От: cppdev  
Дата: 18.05.15 09:27
Оценка:
Пишу драйвер с поддержкой DMA. Система Ubuntu 15.04 x64, ядро 3.19.0. Столкнулся с тем, что если использую dma_sync_sg_for_cpu с числом элементов в списке > 1, то данные (32 битный счетчик) приходят испорченными в некоторых областях буфера. В большинстве случаев эти области начинаются с нового элемента sg списка и выглядит это так:

//так должно быть
01 00 ...
....
10 00 ...
....
20 00 ...
...
30 00 ...

//такие данные приходят
01 00 ...
....
20 00 ...
....
30 00 ...
...
00 00 ...


Вот работающий код

int i, refcnt, mapcnt;
size_t DmaBufSize;
u_int8_t *DmaBufBaseCreate;
dma_addr_t DmaBufBaseLACreate;
struct scatterlist *sgl;
struct device *device;

refcnt = 1;

sgl = (struct scatterlist *)kmalloc(refcnt * sizeof(struct scatterlist), GFP_KERNEL);

DmaBufBaseCreate = kmalloc(DmaBufSize + PAGE_SIZE, GFP_KERNEL | GFP_DMA32);

sg_init_table(sgl, refcnt);        

sg_set_buf(sgl, DmaBufBaseCreate, DmaBufSize + PAGE_SIZE);

mapcnt = dma_map_sg(device, sgl, refcnt, DMA_BIDIRECTIONAL);

DmaBufBaseLACreate = sg_dma_address(sgl); // по этому адресу устройство осуществляет запись данных

/*
по завершении записи в буфер
перед копированием в user space буфер вызывается
*/

dma_sync_sg_for_cpu(device, sgl, refcnt, DMA_BIDIRECTIONAL);


Код, получающий «битые» данные

int i, refcnt, mapcnt;
size_t DmaBufSize, size, blocksize;
u_int8_t *buf;
u_int8_t *DmaBufBaseCreate;
dma_addr_t DmaBufBaseLACreate;
struct scatterlist *sgl;
struct scatterlist *sg;
struct device *device;

blocksize = 64*1024;
refcnt = ((DmaBufSize + PAGE_SIZE) % blocksize == 0)?
(DmaBufSize + PAGE_SIZE) / blocksize:
((DmaBufSize + PAGE_SIZE) / blocksize + 1);

sgl = (struct scatterlist *)kmalloc(refcnt * sizeof(struct scatterlist), GFP_KERNEL);

DmaBufBaseCreate = kmalloc(DmaBufSize + PAGE_SIZE, GFP_KERNEL | GFP_DMA32);

size = DmaBufSize + PAGE_SIZE;
buf = DmaBufBaseCreate;
sg_init_table(sgl, refcnt);        
for_each_sg(sgl, sg, refcnt, i)
{
    if (size < blocksize) blocksize = size;
    
    sg_set_buf(sg, buf, blocksize);
    
    buf += blocksize;
    size -= blocksize;
}

mapcnt = dma_map_sg(device, sgl, refcnt, DMA_BIDIRECTIONAL);

DmaBufBaseLACreate = sg_dma_address(sgl); // по этому адресу устройство осуществляет запись данных

/*
по завершении записи в буфер
перед копированием в user space буфер вызывается
*/

dma_sync_sg_for_cpu(device, sgl, refcnt, DMA_BIDIRECTIONAL);


В чем может быть причина?
Re: Драйвер с поддержкой DMA и непонятное поведение dma_sync_sg_for_cpu
От: cppdev  
Дата: 26.05.15 14:12
Оценка:
Решение найдено. Во все функции, принимающие в качестве параметра struct device, необходимо передавать поле dev из struct pci_dev.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.