这里主要看看PCIE的中断部分。
14,pci_alloc_irq_vectors
pci_alloc_irq_vectors
是 Linux 内核中用于为 PCI 设备分配中断向量的一个函数。它是 Linux 内核 PCI 子系统的一部分。该函数的作用是为 PCI 设备分配一组中断向量。中断向量用于将硬件中断映射到操作系统中的软件中断处理程序。
int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs,
unsigned int max_vecs, unsigned int flags)
这里是各参数的解释:
struct pci_dev *dev: 要分配中断向量的 PCI 设备。
unsigned int min_vecs: 要分配的最小中断向量数量。
unsigned int max_vecs: 要分配的最大中断向量数量。
unsigned int flags: 控制函数行为的标志位, 如分配的中断类型(如 MSI、MSI-X 或传统中断)。
该函数返回成功分配的中断向量数量, 如果硬件或系统配置不支持请求的向量数量, 返回值可能小于最大请求值。在调用 pci_alloc_irq_vectors 后, 可以使用 pci_irq_vector 函数访问分配的中断向量, 该函数将中断向量索引映射到实际的硬件中断号。
这个函数通常用于 PCI 设备驱动程序开发, 在这种情况下, 驱动程序需要为设备设置中断处理。
15:request_irq
request_irq
是 Linux 内核中用于请求和注册硬件中断的中断处理程序的函数。
int request_irq(unsigned int irq,
irq_handler_t handler,
unsigned long flags,
const char *name,
void *dev_id)
下面是各参数的解释:
1,unsigned int irq: 要请求的中断请求(IRQ)号。
2,irq_handler_t handler: 当中断发生时将被调用的中断处理程序函数。
3,unsigned long flags: 控制中断处理程序行为的标志位, 如边缘触发或电平触发。
4,const char *name: 用于调试和信息目的的中断处理程序标识符字符串。
5,void *dev_id: 请求中断的设备或驱动程序的唯一标识符, 用于在中断发生时识别正确的中断处理程序。
request_irq 函数执行以下任务:1,将中断处理程序函数注册到内核的中断子系统。
2,在硬件中断控制器中启用指定的中断。
3,可选地, 分配并设置任何必要的硬件资源, 如 GPIO 引脚或 DMA 通道。如果函数执行成功, 它会返回 0。否则, 它会返回一个负的错误码。
在中断处理程序被注册后, 内核会在相应的硬件中断发生时调用指定的 handler 函数。handler 函数负责处理中断并在硬件中清除中断条件。
request_irq 函数通常用于设备驱动程序开发, 在这种情况下, 驱动程序需要为设备的硬件设置中断处理。它是 Linux 内核中断处理基础设施的重要组成部分。
我们来看看XTRX实际代码中(A)申请中断并对应(B)中断处理函数的部分。
先看中断申请部分,就是用pci_alloc_irq_vectors申请指定数量的中断向量,之后分别制定每个中断向量的处理函数。这里也存在请求多个中断向量但是只申请到了一个中断向量,这时候就只能有一个中断处理的入口进入中断,之后通过读中断状态寄存器再分配不同的中断处理函数进行处理。
#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,11,0)
err = pci_enable_msi_range(pdev, XTRX_MSI_COUNT, XTRX_MSI_COUNT);
#else
err = pci_alloc_irq_vectors(pdev, XTRX_MSI_COUNT, XTRX_MSI_COUNT, PCI_IRQ_MSI);
#endif
// 申请XTRX_MSI_COUNT个中断向量
if (err == XTRX_MSI_COUNT) { //如果申请成功就分别注册一下四个中断对应的处理函数
xtrxdev->inttype = XTRX_MSI_4;
printk(KERN_INFO PFX "liwei [%d] (err == XTRX_MSI_COUNT) \n", __LINE__ );
err = request_irq(pdev->irq + INT_1PPS, xtrx_msi_irq_pps, 0, "xtrx_pps", xtrxdev);
if (err) {
dev_err(&pdev->dev, "Failed to register CTRL MSI.\n");
err = -ENODEV;
goto err_msi;
}
err = request_irq(pdev->irq + INT_DMA_TX, xtrx_msi_irq_tx, 0, "xtrx_tx", xtrxdev);
if (err) {
dev_err(&pdev->dev, "Failed to register TX MSI.\n");
err = -ENODEV;
goto err_irq_0;
}
err = request_irq(pdev->irq + INT_DMA_RX, xtrx_msi_irq_rx, 0, "xtrx_rx", xtrxdev);
if (err) {
dev_err(&pdev->dev, "Failed to register RX MSI.\n");
err = -ENODEV;
goto err_irq_1;
}
err = request_irq(pdev->irq + 3, xtrx_msi_irq_other, 0, "xtrx_other", xtrxdev);
if (err) {
dev_err(&pdev->dev, "Failed to register OTHER MSI.\n");
err = -ENODEV;
goto err_irq_2;
}
}
else {//如果不成功就重新只申请一个中断向量注册一个中断处理函数。
printk(KERN_INFO PFX "liwei [%d] (err != XTRX_MSI_COUNT) \n", __LINE__ );
#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,11,0)
err = pci_enable_msi_range(pdev, 1, 1);
#else
err = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
#endif
if (err == 1) {
xtrxdev->inttype = XTRX_MSI_SINGLE;
err = request_irq(pdev->irq, xtrx_msi_irq_single, 0, "xtrx_msi_single", xtrxdev);
if (err) {
dev_err(&pdev->dev, "Failed to register SINGLE MSI.\n");
err = -ENODEV;
goto err_msi;
}
} else {
dev_err(&pdev->dev, "Failed to enable MSI, falling back to legacy mode.\n");
xtrxdev->inttype = XTRX_LEGACY;
err = request_irq(pdev->irq, xtrx_irq_legacy, IRQF_SHARED, "xtrx_legacy", xtrxdev);
if (err) {
dev_err(&pdev->dev, "Failed to register Legacy interrupt.\n");
err = -ENODEV;
goto err_alloc1;
}
}
}
只申请到一个中断向量只能注册一个中断处理函数的情况下,在这个中断处理函数读了中断状态寄存器,查看到是具体哪个中断,之后再分支到具体的中断处理程序。如下代码所示:
// Interrupt routine functions
static void xtrx_interrupt_gpspps(struct xtrx_dev *xtrxdev)
{
struct pps_event_time ts;
pps_get_ts(&ts);
pps_event(xtrxdev->pps, &ts, PPS_CAPTUREASSERT, NULL);
}
static void xtrx_process_l_interrupts(struct xtrx_dev *xtrxdev, uint32_t imask)
{
if (imask & (1 << INT_RFIC0_SPI)) {
atomic_inc((atomic_t*)xtrxdev->shared_mmap + XTRX_KERN_MMAP_CTRL_IRQS);
wake_up_interruptible(&xtrxdev->queue_ctrl);
}
if (imask & (1 << INT_I2C)) {
atomic_inc((atomic_t*)xtrxdev->shared_mmap + XTRX_KERN_MMAP_I2C_IRQS);
wake_up_interruptible(&xtrxdev->queue_i2c);
}
if (imask & (1 << INT_GPS_UART_RX)) {
// TODO tty or user notificatio
//atomic_inc((atomic_t*)xtrxdev->shared_mmap + XTRX_KERN_MMAP_GPS_RX_IRQS);
//wake_up_interruptible(&xtrxdev->uart_gps_rx);
spin_lock(&xtrxdev->port_gps.lock);
if (xtrxdev->gps_ctrl_state) {
unsigned tx_fifo_used;
xtrx_uart_do_rx(&xtrxdev->port_gps, &tx_fifo_used);
xtrx_uart_do_tx(&xtrxdev->port_gps, tx_fifo_used);
}
spin_unlock(&xtrxdev->port_gps.lock);
}
if (imask & (1 << INT_SIM_UART_RX)) {
// TODO tty or user notificatio
//atomic_inc((atomic_t*)xtrxdev->shared_mmap + XTRX_KERN_MMAP_SIM_RX_IRQS);
//wake_up_interruptible(&xtrxdev->uart_gps_rx);
spin_lock(&xtrxdev->port_sim.lock);
if (xtrxdev->sim_ctrl_state & WR_SIM_CTRL_ENABLE) {
unsigned tx_fifo_used;
xtrx_uart_do_rx(&xtrxdev->port_sim, &tx_fifo_used);
xtrx_uart_do_tx(&xtrxdev->port_sim, tx_fifo_used);
}
spin_unlock(&xtrxdev->port_sim.lock);
}
}
static irqreturn_t xtrx_msi_irq_pps(int irq, void *data)
{
struct xtrx_dev *xtrxdev = data;
xtrx_interrupt_gpspps(xtrxdev);
atomic_inc((atomic_t*)xtrxdev->shared_mmap + XTRX_KERN_MMAP_1PPS_IRQS);
wake_up_interruptible(&xtrxdev->onepps_ctrl);
return IRQ_HANDLED;
}
static irqreturn_t xtrx_msi_irq_tx(int irq, void *data)
{
struct xtrx_dev *xtrxdev = data;
atomic_inc((atomic_t*)xtrxdev->shared_mmap + XTRX_KERN_MMAP_TX_IRQS);
wake_up_interruptible(&xtrxdev->queue_tx);
return IRQ_HANDLED;
}
static irqreturn_t xtrx_msi_irq_rx(int irq, void *data)
{//此函数可以通过两个入口进来,一个系统注册的中断函数入口,另外一个是xtrx_msi_irq_single调用的xtrx_irq_legacy入口。
struct xtrx_dev *xtrxdev = data;
atomic_inc((atomic_t*)xtrxdev->shared_mmap + XTRX_KERN_MMAP_RX_IRQS);
wake_up_interruptible(&xtrxdev->queue_rx);
return IRQ_HANDLED;
}
static irqreturn_t xtrx_msi_irq_other(int irq, void *data)
{
struct xtrx_dev *xtrxdev = data;
uint32_t imask;
imask = xtrx_readl(xtrxdev, GP_PORT_RD_INTERRUPTS);
xtrx_process_l_interrupts(xtrxdev, imask);
return IRQ_HANDLED;
}
static irqreturn_t xtrx_irq_legacy(int irq, void *data)
{
struct xtrx_dev *xtrxdev = data;
uint32_t imask = xtrx_readl(xtrxdev, GP_PORT_RD_INTERRUPTS);
//在这里进行了分支
if (imask == 0) {
return IRQ_NONE;
}
if (imask & (1 << (INT_1PPS))) {
xtrx_msi_irq_pps(irq, data);
}
if (imask & (1 << (INT_DMA_TX))) {
xtrx_msi_irq_tx(irq, data);
}
if (imask & (1 << (INT_DMA_RX))) {
xtrx_msi_irq_rx(irq, data);
}
xtrx_process_l_interrupts(xtrxdev, imask);
return IRQ_HANDLED;
}
static irqreturn_t xtrx_msi_irq_single(int irq, void *data)
{
xtrx_irq_legacy(irq, data);
return IRQ_HANDLED;
}