Драйвер с поддержкой DMA и непонятное поведение dma_sync_sg_for_cpu
Пишу драйвер с поддержкой 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);
В чем может быть причина?