Можно ли использовать доступ к памяти ввода / вывода внутри ISR под Linux (ARM)?
Я создаю драйвер для связи с FPGA под Linux. ПЛИС подключается через интерфейс GPMC. Когда я тестировал чтение / запись из контекста драйвера - все работает отлично. Но проблема в том, что мне нужно прочитать какой-то адрес по прерыванию. Поэтому я создал обработчик прерываний, зарегистрировал его и поместил в него чтение iomemory (функция readw). Но когда срабатывает прерывание - считываются только нули. Я протестировал каждую часть драйвера сверху вниз, и кажется, что проблема заключается в доступе к памяти внутри ISR. Когда я заменил io access на постоянное значение - он успешно перешел в приложение уровня пользователя.
Версия ARM: armv7a (Cortex ARM-A8 (DM3730))
Компилятор: CodeSourcery 2014.05
Вот некоторый код из драйвера, который представляет выполненные действия:
// Request physical memory region for FPGA address IO
void* uni_PhysMem_request(const unsigned long addr, const unsigned long size) {
// Handle to be returned
void* handle = NULL;
// Check if memory region successfully requested (mapped to module)
if (!request_mem_region(addr, size, moduleName)) {
printk(KERN_ERR "\t\t\t\t%s() failed to request_mem_region(0x%p, %lu)\n", __func__, (void*)addr, size);
}
// Remap physical memory
if (!(handle = ioremap(addr, size))) {
printk(KERN_ERR "\t\t\t\t%s() failed to ioremap(0x%p, %lu)\n", __func__, (void*)addr, size);
}
// Return virtual address;
return handle;
}
// ...
// ISR
static irqreturn_t uni_IRQ_handler(int irq, void *dev_id) {
size_t readed = 0;
if (irq == irqNumber) {
printk(KERN_DEBUG "\t\t\t\tIRQ handling...\n");
printk(KERN_DEBUG "\t\t\t\tGPIO %d pin is %s\n", irqGPIOPin, ((gpio_get_value(irqGPIOPin) == 0) ? "LOW" : "HIGH"));
// gUniAddr is a struct which holds GPMC remapped virtual address (from uni_PhysMem_request), offset and read size
if ((readed = uni_ReadBuffer_IRQ(gUniAddr.gpmc.addr, gUniAddr.gpmc.offset, gUniAddr.size)) < 0) {
printk(KERN_ERR "\t\t\t\tunable to read data\n");
}
else {
printk(KERN_INFO "\t\t\t\tdata readed success (%zu bytes)\n", readed);
}
}
return IRQ_HANDLED;
}
// ...
// Read buffer by IRQ
ssize_t uni_ReadBuffer_IRQ(void* physAddr, unsigned long physOffset, size_t buffSize) {
size_t size = 0;
size_t i;
for (i = 0; i < buffSize; i += 2) {
size += uni_RB_write(readw(physAddr + physOffset)); // Here readed value sent to ring buffer. When "readw" replaced with any constant - everything OK
}
return size;
}
0 ответов
Похоже, проблема была в оптимизации кода. Я изменил функцию uni_RB_write, чтобы передать физический адрес и размер данных, также чтение теперь выполняется через функцию ioread16_rep. Так что теперь все работает просто отлично.