Interrupts in UART 16550 and Linux Kernel

2019-07-18 02:23发布

I'm trying to use interrupts to see if there are errors in an UART 16550D and when a character is available to be read.

The UART configurations is as follows:

#define UART    0x03f8      // Endereço da Porta Serial - I (com1) 
#define UART_IER    1
#define UART_LCR    3
#define UART_LSR    5
#define UART_DLL    0   /* Out: Divisor Latch Low */
#define UART_DLM    1   /* Out: Divisor Latch High */
#define UART_LSR_THRE   0x20 /* Transmit-hold-register empty */
#define UART_LSR_DR     0x01 /* Receiver data ready */
#define UART_RX     0   /* In:  Receive buffer */ 
#define UART_TX     0   /* Out: Transmit buffer */

void UART_init(void){
    outb( 0x80 , UART + UART_LCR );     
    outb( 0x00 , UART + UART_DLM );     
    outb( 0x60 , UART + UART_DLL );     
    outb( 0x1f , UART + UART_LCR );     
    outb( 0x07 , UART + UART_IER );         
    return;
}

And the interrupt

irqreturn_t short_interrupt(int irq, void *dev_id){

        printk("INTERRUPT HAPPENED. WILL NOW RETURN\n");

        return 0;
}

static int seri_init(void){
        int result, i;

        UART_init();  

        request_irq(4, short_interrupt, SA_SHIRQ, "seri", NULL);

        ....

So for now I just want to see if the handler is called or not. 4 is defined as the IRQ in the virtual box settings I'm using.

What I want to know is, is there something wrong about this setup? When testing, I have no problem reading and processing what I'm reading. Thing is, the handler is never called.

The return from request_irq() is -22. There are no problems during compilation.

4条回答
放荡不羁爱自由
2楼-- · 2019-07-18 02:51

Change

outb( 0x80 , UART + UART_LCR );     
outb( 0x00 , UART + UART_DLM );     
outb( 0x60 , UART + UART_DLL );     
outb( 0x1f , UART + UART_LCR );     
outb( 0x07 , UART + UART_IER );    

To

outb( 0x80 , UART + UART_LCR );     
outb( 0x00 , UART + UART_DLM );     
outb( 0x60 , UART + UART_DLL );     
outb( 0x07 , UART + UART_IER );    // Set IER before clearing DLAB bit in LCR
outb( 0x1f , UART + UART_LCR );     
// You may also need
outb( 8 , UART + 4 );  // Some systems use this as a master interrupt enable
查看更多
Emotional °昔
3楼-- · 2019-07-18 02:57

I know this is an old post, but thought I would throw up an answer. use gpio_to_irq to get interrupt number. The third parameter determines when the interrupt occurs.

irqNumber = gpio_to_irq(gpioUART);

// This next call requests an interrupt line
result = request_irq(irqNumber,       // The interrupt number requested
    (irq_handler_t) short_interrupt, // The pointer to handler function below
    IRQF_TRIGGER_FALLING,            // Interrupt on FALL edge ()
    "seri",                          // For /proc/interrupts identity
    NULL);                           // The *dev_id for shared interrupt
查看更多
何必那么认真
4楼-- · 2019-07-18 03:08

-22 is EINVAL and I think it's because of NULL last argument. This page: http://www.makelinux.net/books/lkd2/ch06lev1sec3 says:

The fifth parameter, dev_id, is used primarily for shared interrupt lines. When an interrupt handler is freed (discussed later), dev_id provides a unique cookie to allow the removal of only the desired interrupt handler from the interrupt line. Without this parameter, it would be impossible for the kernel to know which handler to remove on a given interrupt line. You can pass NULL here if the line is not shared, but you must pass a unique cookie if your interrupt line is shared

So requesting a shared irq wiht NULLed dev_id won't work.

查看更多
Lonely孤独者°
5楼-- · 2019-07-18 03:15

My analysis on why the interrupt handler is not triggered:

(1) Make sure the first parameter passed to request_irq(virq,...) is hardware irq number or virtual irq number. On some platform, there is a mapping between the hardware irq and the number which used by request_irq(...). If there is a mapping on your box, then you connect your handler to other interrupt source mistakenly;

(2) Make sure the interrupt mask register on 16550D is set correctly. I think there should be an interrupt mask register on 16550D which can mask/unmask the interrupt events;

(3) Check the return value of request_irq(...) to make sure the interrupt is connected successfully.

Hope the above help you.

查看更多
登录 后发表回答