Доброго времени суток!
Работаю над драйвером под ядро 2.6.37, платформа с чипом TI DM3730, который с ядром Cortex A8, которое на основе ARMv7.
Драйвер у меня позволяет чипу общаться с FPGA по GPMC, и всё это дело вполне сносно работает по DMA, когда я выделяю память под DMA буфер при помощи dma_alloc_coherent().
Но задача смотрит немного дальше: нужно не просто получить данные, а отправить их на DSP ядро, забрать оттуда и затем вручить радостному юзеру. Логично при этом использовать идеологию zero-copy, и вот тут-то возникают нюансы.
Во-первых, памяти нужно много, т.е. больше, чем даёт мне dma_alloc_coherent (1Мб). Во-вторых, в эту память должно уметь лазить DSP ядро. Производитель чипа предоставил модуль, который умеет выделять большие непрерывные объёмы памяти, называется CMEM. В более поздних ядрах появится «официальный» CMA, но тут что есть, тому и рады. Смысл в том, что в параметрах загрузчика указывается, что оперативной памяти меньше, чем есть на самом деле, и адрес зарезервированного остатка передаётся модулю CMEM. Тот выделяет в этой области нужные буферы, указатели на которые передаются и в DSP, и моему драйверу для DMA.
Мой драйвер берёт нужный буфер и последовательно заполняет его кусочками, применяя к ним dma_map_single(NULL,virt_address,size,DMA_FROM_DEVICE) перед тем, как сказать omap_start_dma и соответственно dma_unmap_single(NULL,dma_address,size,DMA_FROM_DEVICE) после вызова omap_stop_dma.
Судя по документации, после вызова unmap юзер волен забирать данные на все четыре стороны. Вот как бы оно так и есть, но не совсем: запускаю юзерскую программу, она выделяет память через CMEM, открывает драйвер, тот сбрасывает счётчики и указатели на начало, и закачивает пачку данных с FPGA. Программа сохраняет данные в NAND-плеш, освобождает память и уходит. Смотрим в файл - данные на месте. Запускаем ещё раз, порядок тот же, только вместо данных все нули. ВСЕ, не частично. И никакого мусора. Запускаем в третий раз - данные снова на месте. Ещё раз - нули. И далее они так чередуются. Я перекрестился, повтыкал flush_cache_all() тут и там, но не помогло. rmmod-insmod драйвера на порядок чередования не влияет.
Что за дела - решительно не понимаю. Какой-то прикол с CMEM? Но буферы-то физически одни и те же... Специально проверил все адреса и указатели в случае нормы и в случае нулей - всё одинаково. Мозг сломался, нужна помощь!