Embeddedxen中断申请

原创文章,转载请注明出处.转载自: Li Haifeng's Blog
本文链接地址: Embeddedxen中断申请

先看xen的中断申请


申请中断离不开request_irq和setup_irq


xen只申请了两个irq一个是timer的,另一个是console.

xen/arch/arm/mach-pxa/time.c



void __init xen_pxa_timer_init(void)
{
unsigned long clock_tick_rate;

OIER = 0;
OSSR = OSSR_M0 | OSSR_M1 | OSSR_M2 | OSSR_M3;

if (cpu_is_pxa25x())
clock_tick_rate = 3686400;
else if (machine_is_mainstone())
clock_tick_rate = 3249600;
else
clock_tick_rate = 3250000;

set_oscr2ns_scale(clock_tick_rate);

ckevt_pxa_osmr0.mult =
div_sc(clock_tick_rate, NSEC_PER_SEC, ckevt_pxa_osmr0.shift);

ckevt_pxa_osmr0.max_delta_ns =
clockevent_delta2ns(0x7fffffff, &ckevt_pxa_osmr0);
ckevt_pxa_osmr0.min_delta_ns =
clockevent_delta2ns(MIN_OSCR_DELTA * 2, &ckevt_pxa_osmr0) + 1;

cksrc_pxa_oscr0.mult =
clocksource_hz2mult(clock_tick_rate, cksrc_pxa_oscr0.shift);

setup_irq(IRQ_OST0, &pxa_ost0_irq);

/* timebase_freq = CLOCK_TICK_RATE: source clock rate in Hz */
//timebase_freq = 1000000LL;
timebase_freq = CLOCK_TICK_RATE;
system_timer_clocksource = &cksrc_pxa_oscr0;
system_timer_clockevent = &ckevt_pxa_osmr0;

    ckevt_pxa_osmr0.set_mode(CLOCK_EVT_MODE_PERIODIC, &ckevt_pxa_osmr0);

}

所安装的irqaction是pxa_ost0_irq,xen/arch/arm/mach-pxa/time.c

static struct irqaction pxa_ost0_irq = {
.name = “ost0″,
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
.handler = pxa_ost0_interrupt,
.dev_id = &ckevt_pxa_osmr0,
};



xen/arch/arm/hypervisor/irq.c
int setup_irq(unsigned int irq, struct irqaction *new)
{
int rc = 0;
        int shared = 0;
        struct irqaction *old, **p;
        unsigned long flags;
        struct irqdesc *desc;

if(irq >= NR_IRQS) {
printk(“BAD IRQ = %dn”, irq);
while(1);
}

        desc = get_irq_descriptor(irq);

        spin_lock_irqsave(&desc->lock, flags);

        p = &desc->action;

        if ((old = *p) != NULL) {
                /* Can’t share interrupts unless both agree to */
                if (!(old->flags & new->flags & IRQF_SHARABLE) ||
   ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {
    printk(“IRQ sharing failed.n”);

rc = -EBUSY;
goto out;
                }

                /* add new interrupt at end of irq queue */
                do {
                        p = &old->next;
                        old = *p;
                } while (old);
                shared = 1;
        }

        *p = new;

        if (!shared) {
if(new->flags & IRQF_TRIGGER_MASK) {
if(desc->chip->set_type) {
desc->chip->set_type(irq, new->flags & IRQF_TRIGGER_MASK);
} else {
printk(“No set_irq_type function for irq %dn”, irq);
rc = -1;
goto out;
}
}

desc->status &= ~(IRQ_AUTO_DETECT | IRQ_WAITING | IRQ_IN_PROGRESS);

if(!(desc->status & IRQ_NO_AUTO_ENABLE)) {
desc->disable_depth = 0;
desc->status &= ~IRQ_DISABLED;
desc->chip->unmask(irq);
} else {
desc->disable_depth = 1;
}

        }

desc->flags &= ~IRQF_GUEST_BOUND;

out:
        spin_unlock_irqrestore(&desc->lock, flags);

        return rc;
}


setup_irq timer后,就开始setup_irq console了。

console的设置稍微复杂了一些:


在__start_xen中:xen/arch/arm/hypersivor/setup.c



pxaserial.baud = 115200;
pxaserial.data_bits = 8;
pxaserial.parity = ‘n';
pxaserial.stop_bits = 1;
pxaserial.io_base = 1;
pxaserial.irq = IRQ_FFUART;

//pxaserial.io_base = 0x3f8;
pxaserial_init(0, &pxaserial);

pxaserial的结构体:xen/drivers/char/pxa.c
static struct pxaserial {
int baud, data_bits, parity, stop_bits, irq;


unsigned long io_base; /* I/O port or memory-mapped I/O address. */


char *remapped_io_base; /* Remapped virtual address of mmap I/O.  */


/* UART with IRQ line: interrupt-driven I/O. */


struct irqaction irqaction;


/* UART with no IRQ line: periodically-polled I/O. */


struct timer timer;


unsigned int timeout_ms;


} pxaserial_com[2] = { { 0 }, { 0 } };


pxaserial_init  xen/drivers/char/pxa.c

void __init pxaserial_init(int index, struct pxaserial_defaults *defaults)


{


struct pxaserial *uart = &pxaserial_com[index];


if ( (index < 0) || (index> 1) )


return;


if ( defaults != NULL )


{


uart->baud = defaults->baud;


uart->data_bits = defaults->data_bits;


uart->parity = parse_parity_char(defaults->parity);


uart->stop_bits = defaults->stop_bits;


uart->irq = defaults->irq;


uart->io_base = defaults->io_base;


}


pxaserial_parse_port_config(uart, index);


}



pxaserial_parse_port_config xen/drivers/char/pxa.c


static void __init pxaserial_parse_port_config(struct pxaserial *uart, int index)


{


int baud;


const char *conf = ((index == 0) ? opt_com1 : opt_com2);


/* (LSU) Defined in xen/lib/vsprintf.c */


extern unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base);


/* No user-specified configuration? */


if ( (conf == NULL) || (*conf == ‘’) )


{


/* Some platforms may automatically probe the UART configuartion. */


if ( uart->baud != 0 )


goto config_parsed;


return;


}


if ( strncmp(conf, “auto”, 4) == 0 )


{


uart->baud = BAUD_AUTO;


conf += 4;


}


else if ( (baud = simple_strtoul(conf, (char **) &conf, 10)) != 0 )


uart->baud = baud;


if ( *conf != ‘,’ )


goto config_parsed;


conf++;


uart->data_bits = simple_strtoul(conf, (char **) &conf, 10);


uart->parity = parse_parity_char(*conf);


conf++;


uart->stop_bits = simple_strtoul(conf,(char **)  &conf, 10);


if ( *conf == ‘,’ )


{


conf++;


uart->io_base = simple_strtoul(conf, (char **) &conf, 0);


if ( *conf == ‘,’ )


{


conf++;


uart->irq = simple_strtoul(conf, (char **) &conf, 10);


}


}


config_parsed:


/* Sanity checks. */


if ( (uart->baud != BAUD_AUTO) && ((uart->baud < 1200) || (uart->baud> 115200)) )


PARSE_ERR(“Baud rate %d outside supported range.”, uart->baud);


if ( (uart->data_bits < 5) || (uart->data_bits> 8) )


PARSE_ERR(“%d data bits are unsupported.”, uart->data_bits);


if ( (uart->stop_bits < 1) || (uart->stop_bits> 2) )


PARSE_ERR(“%d stop bits are unsupported.”, uart->stop_bits);


if ( uart->io_base == 0 )


PARSE_ERR(“I/O base address must be specified.”);


/* Register with generic serial driver. */


serial_register_uart(0, &pxaserial_driver, uart);


}


上面的这个pxaserial_parse_port_config函数有点长,不过,除了最后一句有用外,其他的都在扯淡。特别是中间那一大段完全没有被执行,所以不要有压力哈。

serial_register_uart xen/drivers/char/serial.c

void __init serial_register_uart(int idx, struct uart_driver *driver, void *uart)


{


    /* Store UART-specific info. */


    com[idx].driver = driver;


    com[idx].uart   = uart;


    /* Default is no transmit FIFO. */


    com[idx].tx_fifo_size = 1;


}


其实这个函数不再是重点了,重点的是其中的值:
我们看一下,com[0]的类型是:

struct serial_port {


    /* Uart-driver parameters. */


    struct uart_driver *driver;


    void               *uart;


    /* Number of characters the port can hold for transmit. */


    int                 tx_fifo_size;


    /* Transmit data buffer (interrupt-driven uart). */


    char               *txbuf;


    unsigned int        txbufp, txbufc;


    /* Force synchronous transmit. */


    int                 sync;


    /* Receiver callback functions (asynchronous receivers). */


    serial_rx_fn        rx_lorx_hi, rx;


    /* Receive data buffer (polling receivers). */


    char                rxbuf[SERIAL_RXBUFSZ];


    unsigned int        rxbufp, rxbufc;


    /* Serial I/O is concurrency-safe. */


    spinlock_t          rx_lock, tx_lock;


};


也就是说,设置了com[0]的两个成员变量,一个是driver,另一个是uart.我们在回头看一下pxaserial_parse_port_config(struct pxaserial *uart, int index),其实这个函数调用serial_register_uart(0, &pxaserial_driver, uart)的并不优雅,它将原本是index确定为了0.不过对于本项目显然是没有问题的。可以看看linux是怎样实现的。
在函数serial_register_uart里面讲3个成员变量定以后,就剩下其他的小东西了。这些东西紧接着就定义了,不要急。我们先来看一下struct uart_driver *driver=&pxaserial_driver.

改变定义在xen/drivers/char/pxa.c文件中

static struct uart_driver pxaserial_driver = {


.init_preirq = pxa_initpreirq,


.putc = pxaserial_putc,


.getc = pxaserial_getc,


.irq = pxaserial_irq,


.init_postirq = pxaserial_init_postirq,


.endboot = NULL,


.tx_empty = serial_pxa_tx_empty,


};


以上分别定义了struct pxaserial类型的pxaserial,定义了struct uart_driver类型的pxaserial_driver.然后将它们赋值给了struct serial_port类型的com[0].

我们回过头来看看,实际上,并没有定义struct pxaserial pxaserial的全部成员,比如irqaction,比如timer.也许是以后还会有。我们需要耐心。
好,这里已经定义好了serial_port的3个重要的成员变量了,革命尚未成功,同志仍需努力!

返回到__start_xen的主程序,接下来开始执行

serial_init_preirq(void);


void __init serial_init_preirq(void)


{


    int i;


    for ( i = 0; i < ARRAY_SIZE(com); i++ )


        if ( com[i].driver && com[i].driver->init_preirq )


            com[i].driver->init_preirq(&com[i]);


}


可以看到,实际上执行的是pxa_initpreirq(&com[0]),com[1]没有初始化,因此com[1]并没有被执行。


/* from startup…*/


/* (LSU) Applied awaited signature */


//static int pxa_initpreirq(struct serial_port *port) {


static void pxa_initpreirq(struct serial_port *port) {


struct uart_pxa_port *up = &serial_pxa_port;


unsigned long flags;


// int retval;


if (up->line == 3) /* HWUART */


up->mcr |= UART_MCR_AFE;


else


up->mcr = 0;


/*


* Clear the FIFO buffers and disable them.


* (they will be reenabled in set_termios())


*/


serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);


serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR


| UART_FCR_CLEAR_XMIT);


serial_out(up, UART_FCR, 0);


/*


* Clear the interrupt registers.


*/


(void) serial_in(up, UART_LSR);


(void) serial_in(up, UART_RX);


(void) serial_in(up, UART_IIR);


(void) serial_in(up, UART_MSR);


/*


* Now, initialize the UART


*/


serial_out(up, UART_LCR, UART_LCR_WLEN8);


spin_lock_irqsave(&up->lock, flags);


up->mctrl |= TIOCM_OUT2;


serial_pxa_set_mctrl(port, up->mctrl);


spin_unlock_irqrestore(&up->lock, flags);


//return 0;


}


只里面又包含了已经被定义好的东东    xen/drivers/char/pxa.c

static struct uart_pxa_port serial_pxa_port = {


.name = “FFUART”, 


.cken = CKEN6_FFUART,


.membase = (void *)&FFUART, 


.mapbase = __PREG(FFUART),


 .irq = IRQ_FFUART,


 .uartclk = 921600 * 16,


 .line = 0, 


};


struct uart_pxa_port {


spinlock_t lock;


unsigned char ier;


unsigned char lcr;


unsigned char mcr;


unsigned int mctrl;


unsigned int lsr_break_flag;


unsigned int cken;


int line;


char *name;


unsigned long iobase; /* io base address */


void __iomem *membase; /* ioremap cookie or NULL */


unsigned long mapbase; /* resource base */


unsigned int irq; /* interrupt number */


unsigned int uartclk; /* UART clock rate */


unsigned char regshift; /* register shift */


unsigned char iotype; /* UPIO_* */


unsigned char hub6;


upf_t flags; /* UPF_* flags */


};


从上面看来,应该是一些硬件参数信息了。不过和pxaserial那个结构体似乎有某种联系。

还是看那个函数,有用的只有这么一行:serial_pxa_set_mctrl(port, up->mctrl);
我们进去看看:

static void serial_pxa_set_mctrl(struct serial_port *port, unsigned int mctrl) {


struct uart_pxa_port *up = &serial_pxa_port;


unsigned char mcr = 0;


if (mctrl & TIOCM_RTS)


mcr |= UART_MCR_RTS;


if (mctrl & TIOCM_DTR)


mcr |= UART_MCR_DTR;


if (mctrl & TIOCM_OUT1)


mcr |= UART_MCR_OUT1;


if (mctrl & TIOCM_OUT2)


mcr |= UART_MCR_OUT2;


if (mctrl & TIOCM_LOOP)


mcr |= UART_MCR_LOOP;


mcr |= up->mcr;


serial_out(up, UART_MCR, mcr);


}


晕倒啊,将port传进来有意义么?根本就没有用到port,还是用的原来的serial_pxa_port
然后就是serial_out(up,UART_MCR,mcr).

static inline void serial_out(struct uart_pxa_port *up, int offset, int value)


{


offset <<= 2;


writel(value, up->port.membase + offset);


}


drivers/serial/pxa.c

struct uart_pxa_port {


struct uart_port        port;


unsigned char           ier;


unsigned char           lcr;


unsigned char           mcr;


unsigned int            lsr_break_flag;


unsigned int cken;


char *name;


};


这里的uart_port

struct uart_port {


spinlock_t lock; /* port lock */


unsigned int iobase; /* in/out[bwl] */


unsigned char __iomem *membase; /* read/write[bwl] */


unsigned int irq; /* irq number */


unsigned int uartclk; /* base uart clock */


unsigned int fifosize; /* tx fifo size */


unsigned char x_char; /* xon/xoff char */


unsigned char regshift; /* reg offset shift */


unsigned char iotype; /* io access style */


unsigned char unused1;


#define UPIO_PORT (0)


#define UPIO_HUB6 (1)


#define UPIO_MEM (2)


#define UPIO_MEM32 (3)


#define UPIO_AU (4) /* Au1x00 type IO */


#define UPIO_TSI (5) /* Tsi108/109 type IO */


unsigned int read_status_mask; /* driver specific */


unsigned int ignore_status_mask; /* driver specific */


struct uart_info *info; /* pointer to parent info */


struct uart_icount icount; /* statistics */


struct console *cons; /* struct console, if any */


#ifdef CONFIG_SERIAL_CORE_CONSOLE


unsigned long sysrq; /* sysrq timeout */


#endif


upf_t flags;


#define UPF_FOURPORT ((__force upf_t) (1 << 1))


#define UPF_SAK ((__force upf_t) (1 << 2))


#define UPF_SPD_MASK ((__force upf_t) (0x1030))


#define UPF_SPD_HI ((__force upf_t) (0x0010))


#define UPF_SPD_VHI ((__force upf_t) (0x0020))


#define UPF_SPD_CUST ((__force upf_t) (0x0030))


#define UPF_SPD_SHI ((__force upf_t) (0x1000))


#define UPF_SPD_WARP ((__force upf_t) (0x1010))


#define UPF_SKIP_TEST ((__force upf_t) (1 << 6))


#define UPF_AUTO_IRQ ((__force upf_t) (1 << 7))


#define UPF_HARDPPS_CD ((__force upf_t) (1 << 11))


#define UPF_LOW_LATENCY ((__force upf_t) (1 << 13))


#define UPF_BUGGY_UART ((__force upf_t) (1 << 14))


#define UPF_MAGIC_MULTIPLIER ((__force upf_t) (1 << 16))


#define UPF_CONS_FLOW ((__force upf_t) (1 << 23))


#define UPF_SHARE_IRQ ((__force upf_t) (1 << 24))


#define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28))


#define UPF_DEAD ((__force upf_t) (1 << 30))


#define UPF_IOREMAP ((__force upf_t) (1 << 31))


#define UPF_CHANGE_MASK ((__force upf_t) (0x17fff))


#define UPF_USR_MASK ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY))


unsigned int mctrl; /* current modem ctrl settings */


unsigned int timeout; /* character-based timeout */


unsigned int type; /* port type */


const struct uart_ops *ops;


unsigned int custom_divisor;


unsigned int line; /* port index */


unsigned long mapbase; /* for ioremap */


struct device *dev; /* parent device */


unsigned char hub6; /* this should be in the 8250 driver */


unsigned char unused[3];


};


有这么长。。。
好了,搞这这么久,实际上根本与Port屁事不关。我擦~~~

回到__start_xen的主程序,继续我们未完成的千秋大业。
init_console();

void __init init_console(void)


{


extern void add_taint(unsigned flag);


    char *p;


    /* Where should console output go? */


    for ( p = opt_console; p != NULL; p = strchr(p, ‘,’) )


    {


        if ( *p == ‘,’ )


            p++;


        if ( strncmp(p, “com”, 3) == 0 )


            sercon_handle = serial_parse_handle(p);


        else if ( strncmp(p, “vga”, 3) == 0 )


            vga_init();


    }


    serial_set_rx_handler(sercon_handle, serial_rx);


    /* HELLO WORLD — start-of-day banner text. */


    printk(xen_banner());


    printk(” http://www.cl.cam.ac.uk/netos/xenn”);


    printk(” University of Cambridge Computer Laboratorynn”);


    printk(” Xen version %d.%d%s (%s@%s) (%s) %sn”,


           xen_major_version(), xen_minor_version(), xen_extra_version(),


           xen_compile_by(), xen_compile_domain(),


           xen_compiler(), xen_compile_date());


    printk(” EmbeddedXEN/PENAR Project – Reconfigurable Embedded Digital System (REDS) Institute from HEIG-VD/Switzerlandn”);


    printk(” http://reds.heig-vd.chnn”);


    printk(” Latest ChangeSet: %snn”, xen_changeset());


    set_printk_prefix(“(XEN) “);


    if ( opt_sync_console )


    {


        serial_start_sync(sercon_handle);


        add_taint(TAINT_SYNC_CONSOLE);


        printk(“Console output is synchronous.n”);


    }


}


这里面有一句挺重要:
serial_set_rx_handler(sercon_handle, serial_rx);

void __init serial_set_rx_handler(int handle, serial_rx_fn fn)


{


    struct serial_port *port = &com[handle & SERHND_IDX];


    unsigned long flags;


    if ( handle == -1 )


        return;


    spin_lock_irqsave(&port->rx_lock, flags);


    if ( port->rx != NULL )


        goto fail;


    if ( handle & SERHND_LO )


    {


        if ( port->rx_lo != NULL )


            goto fail;


        port->rx_lo = fn;


    }


    else if ( handle & SERHND_HI )


    {


        if ( port->rx_hi != NULL )


            goto fail;


        port->rx_hi = fn;


    }


    else


    {


        if ( (port->rx_hi != NULL) || (port->rx_lo != NULL) )


            goto fail;


        port->rx = fn;


    }


    spin_unlock_irqrestore(&port->rx_lock, flags);


    return;


 fail:


    spin_unlock_irqrestore(&port->rx_lock, flags);


    printk(“ERROR: Conflicting receive handlers for COM%dn”,


           handle & SERHND_IDX);


}


这里将port的成员变量rx_lo,rx_hi,rx_rx全部定位了fn,也就是serial_rx

然后在start_of_day()中,com实际上才开始注册了。在start_of_day中
serial_init_postirq();

void __init serial_init_postirq(void)


{


    int i;


    for ( i = 0; i < ARRAY_SIZE(com); i++ )


        if ( com[i].driver && com[i].driver->init_postirq )


            com[i].driver->init_postirq(&com[i]);


}


实际上是pxaserial_init_postirq.–这在前面赋值的时候,已经被赋值了。xen/drivers/char/pxa.c

static void __init pxaserial_init_postirq(struct serial_port *port)


{


struct pxaserial *uart = port->uart;


struct uart_pxa_port *up = &serial_pxa_port;


int rc;


//int bits;


if ( uart->irq < 0 )


return;


serial_async_transmit(port);


/* We do not consider polling mode (DRE) */


uart->irqaction.handler = pxaserial_interrupt;


uart->irqaction.name = “pxaserial”;


uart->irqaction.dev_id = port;


if ( (rc = setup_irq(uart->irq, &uart->irqaction)) != 0 )


printk(“ERROR: Failed to allocate ns16550 IRQ %dn”, uart->irq);


/*


* Finally, enable interrupts.  Note: Modem status interrupts


* are set via set_termios(), which will be occurring imminently


* anyway, so we don’t enable them here.


*/


up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE;


serial_out(up, UART_IER, up->ier);


/*


* And clear the interrupt registers again for luck.


*/


(void) serial_in(up, UART_LSR);


(void) serial_in(up, UART_RX);


(void) serial_in(up, UART_IIR);


(void) serial_in(up, UART_MSR);


}


看到uart->irqaction了吧,被赋值了,呵呵,终于被赋值了,不过timer还没有被赋值,没关系,我们以后再看,至此,本文基本上也就完成一半了。





现在我们来看一下DOM方面的中断申请,不例外,肯定是用reqeust_irq和setup_irq.我们先来看一下request_irq:




 xen_guest/core/evtchn.c




int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char * devname, void *dev_id)
{


 struct irqaction * action;


 int retval;


 /*

  * Sanity-check: shared interrupts must pass in a real dev-ID,


  * otherwise we’ll have trouble later trying to figure out


  * which interrupt is which (messes up the interrupt freeing


  * logic etc).


  */


 if ((irqflags & SA_SHIRQ) && !dev_id)


  return -EINVAL;


 if (irq >= NR_IRQS)


  return -EINVAL;


 if (!handler)


  return -EINVAL;


 action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);

 if (!action)


  return -ENOMEM;


 action->handler = handler;

 action->flags = irqflags;


 cpus_clear(action->mask);


 action->name = devname;


 action->next = NULL;


 action->dev_id = dev_id;


 retval = setup_irq(irq, action);

 if (retval)

  kfree(action);


 return retval;

}



setup_irq()

xen_guest/core/evtchn.c





int setup_irq(unsigned int irq, struct irqaction * new)


{

 struct evtchn_desc *desc = evtchn_desc + irq;

 struct irqaction *old, **p;

 unsigned long flags;

 int shared = 0;

 // the following should be commented: jyhwang 2007 July 15

 // printk(“irq=%d, NR_IRQS=%dn”, irq, NR_IRQS);

 if (irq >= NR_IRQS)

  return -EINVAL;

 if (desc->handler == NULL)

  return -ENOSYS;

 /*

  * Some drivers like serial.c use request_irq() heavily,

  * so we have to be careful not to interfere with a

  * running system.

  */

 if (new->flags & SA_SAMPLE_RANDOM) {

  /*

   * This function might sleep, we want to call it first,

   * outside of the atomic block.

   * Yes, this might clear the entropy pool if the wrong

   * driver is attempted to be loaded, without actually

   * installing a new handler, but is this really a problem,

   * only the sysadmin is able to do this.

   */

  rand_initialize_irq(irq);

 }

 /*

  * The following block of code has to be executed atomically

  */

 spin_lock_irqsave(&desc->lock,flags);

 p = &desc->action;

 if ((old = *p) != NULL) {

  /* Can’t share interrupts unless both agree to */

  if (!(old->flags & new->flags & SA_SHIRQ)) {

   spin_unlock_irqrestore(&desc->lock,flags);

   return -EBUSY;

  }

  /* add new interrupt at end of irq queue */

  do {

   p = &old->next;

   old = *p;

  } while (old);

  shared = 1;

 }

 *p = new;//action赋值给evtchn_desc

 if (!shared) {

  desc->depth = 0;

  desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT |

      IRQ_WAITING | IRQ_INPROGRESS);

  if (desc->handler->startup)

   desc->handler->startup(irq);

  else

   desc->handler->enable(irq);

 }

 spin_unlock_irqrestore(&desc->lock,flags);

 new->irq = irq;

 new->dir = NULL;

 return 0;

}


看到setup_irq,似乎与Linux没有什么太大的不同,可是这也正是移植的妙处,总是要尽量的和原来的系统兼容,对吧。

我们来看看desc->handler->startup(irq);这句吧,这才是核心。我们知道在DOM进行初始化的时候,是怎么初始化呢?如果是pirq的话,分配的handler是pirq_type,这个handler的startup是被startup_pirq()实例化的。那么我们就来看看startup_pirq吧:



#define probing_irq(_irq) (evtchn_desc[(_irq)].action == NULL)

static unsigned int startup_pirq(unsigned int irq)
{


// evtchn_op_t op = { .cmd = EVTCHNOP_bind_pirq };


 evtchn_bind_pirq_t op;


 int evtchn = evtchn_from_irq(irq), ret = 0;


 if (VALID_EVTCHN(evtchn)) {

  goto out;


 }


 op.pirq  = irq;

 /* NB. We are happy to share unless we are probing. */


 op.flags = probing_irq(irq) ? 0 : BIND_PIRQ__WILL_SHARE;


 ret = HYPERVISOR_event_channel_op(EVTCHNOP_bind_pirq, &op);

 if (ret != 0) {


  if (!probing_irq(irq))


   printk(KERN_INFO “Failed to obtain physical IRQ %dn”, irq);


  return 0;


 }


 evtchn = op.port;


 pirq_query_unmask(irq_to_pirq(irq));

 bind_evtchn_to_cpu(evtchn, 0);

 evtchn_to_irq[evtchn] = irq;


 irq_info[irq] = mk_irq_info(IRQT_PIRQ, irq, evtchn);


 out:

 unmask_evtchn(evtchn);


 pirq_unmask_notify(irq_to_pirq(irq));


 return 0;

}




由于我们现在工作于DOMU的内核中,如果在x86架构中的话,是在ring1态,但这是在arm架构中,因此仍然是在ring3。不过,如果要申请进入ring0,也要进行HYPERVISOR_event_channel_op


 


xen/common/event_channel.c 


long do_event_channel_op(int cmd, XEN_GUEST_HANDLE(void) arg)


{



  case EVTCHNOP_bind_pirq: {
        struct evtchn_bind_pirq bind_pirq;


        if ( copy_from_guest(&bind_pirq, arg, 1) != 0 )


            return -EFAULT;


        rc = evtchn_bind_pirq(&bind_pirq);


        if ( (rc == 0) && (copy_to_guest(arg, &bind_pirq, 1) != 0) )


            rc = -EFAULT; /* Cleaning up here would be a mess! */


        break;


    }




}


 


evtchn_bind_pirq(&bind_pirq);


xen/common/event_channel.c 


static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind)

{


    struct evtchn *chn;


    struct domain *d = current->domain;


    int            port, pirq = bind->pirq;


    long           rc;


    if ( (pirq < 0) || (pirq >= ARRAY_SIZE(d->pirq_to_evtchn)) )

        return -EINVAL;


    if ( !irq_access_permitted(d, pirq) )

        return -EPERM;


    spin_lock(&d->evtchn_lock);

    if ( d->pirq_to_evtchn[pirq] != 0 )

        ERROR_EXIT(-EEXIST);


    if ( (port = get_free_port(d)) < 0 )//得到evtchn号

        ERROR_EXIT(port);


    chn = evtchn_from_port(d, port);//从evtchn号得到chn数据结构

    d->pirq_to_evtchn[pirq] = port;

    rc = pirq_guest_bind(d->vcpu[0], pirq, !!(bind->flags & BIND_PIRQ__WILL_SHARE));


    if ( rc != 0 )


    {


        d->pirq_to_evtchn[pirq] = 0;


        goto out;


    }


    chn->state  = ECS_PIRQ;//充实这个数据结构

    chn->u.pirq = pirq;//充实


    bind->port = port;//充实bind,要带回去的

 out:

    spin_unlock(&d->evtchn_lock);


    return rc;
}



 


port与pirq


port属于DOM的,pirq属于系统的。


当irq来了以后,实际上就是pirq,找到port,然后找到chn,然后就设定vcpu响应的Port了.(这些在中断响应文章中都有叙述)


 


也就是说xen->dom的interface是通过port来进行的,而在申请的时候,是通过结构体evtchn_bind_pirq






void send_guest_pirq(struct domain *d, int pirq)


{


    int port = d->pirq_to_evtchn[pirq];


    struct evtchn *chn;


 


    ASSERT(port != 0);


 


    chn = evtchn_from_port(d, port);


 


    evtchn_set_pending(d->vcpu[chn->notify_vcpu_id], port);


}


  然后当DOM执行时,


evtchn_do_upcall


 
asmlinkage void evtchn_do_upcall(struct pt_regs *regs)


{


unsigned long  l1, l2;


unsigned int   l1i, l2i, port;


int            irq, cpu = smp_processor_id();


shared_info_t *s = HYPERVISOR_shared_info;


vcpu_info_t   *vcpu_info = &s->vcpu_info[cpu];


retry:


xchg(&vcpu_info->evtchn_upcall_pending,0);


l1 = xchg(&vcpu_info->evtchn_pending_sel, 0);


while (l1 != 0) {


l1i = __ffs(l1);


l1 &= ~(1UL << l1i);


while ((l2 = active_evtchns(cpu, s, l1i)) != 0) {


l2i = __ffs(l2);


port = (l1i * BITS_PER_LONG) + l2i;


if ((irq = evtchn_to_irq[port]) != -1) {  //evtchn号转化为irq号


irq_enter();


do_IRQ(irq, regs); //guest分发中断函数


irq_exit();


}  else {


evtchn_device_upcall(port);


}


}


}


if(vcpu_info->evtchn_upcall_pending) {//当有多个中断的时候


goto retry;


}


}


好了,这部分的回顾,就到此打住。





int pirq_guest_bind(struct vcpu *v, int irq, int will_share)
{


 struct irqdesc      *desc;


 irq_guest_action_t  *action;


 unsigned long       flags;


 int                 rc = 0;


 if ( (irq < 0) || (irq >= NR_IRQS) )

  return -EINVAL;


 desc = get_irq_descriptor(irq);

 spin_lock_irqsave(&desc->lock, flags);

 action = (irq_guest_action_t *)desc->action;//

 if (!(desc->flags & IRQF_GUEST_BOUND)) {

  if (desc->action != NULL ) {


   printk(“Cannot bind IRQ %d to guest. In use by %s.n”,(int)irq, desc->action->name);


   rc = -EBUSY;


   goto out;


  }//:


  action = xmalloc(irq_guest_action_t);

  if ((desc->action = (struct irqaction *)action) == NULL ) {


   printk(“Cannot bind IRQ %d to guest. Out of memory.n”, irq);


   rc = -ENOMEM;


   goto out;


  }


  action->shareable = 1;

  action->nr_guests = 0;


  action->in_flight = 0;


  action->ack_type  = pirq_ack_type(irq);


  desc->disable_depth = 0;

  desc->flags |= IRQF_GUEST_BOUND;


  if(will_share) {


   desc->flags |= IRQF_SHARABLE;


  }


  desc->chip->unmask(irq);

 } else if ( !will_share || !action->shareable ) {

  printk(“Cannot bind IRQ %d to guest. Will not share with others.n”, irq);


  rc = -EBUSY;


  goto out;


 }


 if ( action->nr_guests == IRQ_MAX_GUESTS ) {

  printk(“Cannot bind IRQ %d to guest. Already at max share.n”, irq);


  rc = -EBUSY;


  goto out;


 }


 action->guest[action->nr_guests++] = v->domain;

out:

 spin_unlock_irqrestore(&desc->lock, flags);

 return rc;

}


 在上面的这个pirq_guest_bind需要注意的是action的初始化。这里的action和domu的action是两个概念。不是同一回事,一个是irqdesc,一个是evtchn_desc_t。也就是说,一个是中断描述符,另一个是事件通道描述符。当中断由下往上传输的时候,先有irqdesc工作,然后,交给DOMU的时候,evtchn_desc_t开始工作。


我们看到,在request_irq中已经将action实例化了因此,在 pirq_guest_bind(struct vcpu *v, int irq, int will_share)中,直接更改action->guest[acton->nr_guests++]=v->domain,然后返回。


好了,接下来,我们会分析一个实例,来看一下,具体是怎样利用接口request_irq()申请中断.


From Li Haifeng's Blog, post Embeddedxen中断申请

Post Footer automatically generated by wp-posturl plugin for wordpress.

分享到: