PCIE驱动学习之4

这里主要看看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;
}

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
VxWorks PCIe驱动程序开发 PDF是一本关于VxWorks操作系统下开发PCI Express接口设备驱动程序的PDF格式电子书籍。该书涵盖了各种相关的主题,包括PCIe总线结构、PCIe设备的驱动开发方法、VxWorks操作系统中PCIe驱动程序的编写和调试等内容。 首先,PCIe总线结构是现代计算机系统中用于高速数据传输的通信总线之一。了解PCIe总线结构对于进行PCIe设备驱动程序的开发至关重要。本书详细介绍了PCIe总线的基本知识、术语和工作原理,帮助读者了解该总线的特点和性能。 其次,本书详细介绍了在VxWorks操作系统中开发PCIe设备驱动程序的方法。它涵盖了驱动程序的基本概念、VxWorks驱动程序框架、设备初始化和资源分配、驱动程序的读写操作以及中断处理等方面的内容。读者可以通过本书学习到如何使用VxWorks提供的API和工具来开发高效可靠的PCIe驱动程序。 最后,在开发过程中,驱动程序的调试是非常重要的一步。本书提供了一些常用的调试技术和工具,帮助开发人员快速定位和解决问题。此外,本书还介绍了一些常见的PCIe设备的驱动程序开发案例,并给出了相应的源码和演示程序,帮助读者更好地理解和应用书中的知识。 总的来说,VxWorks PCIe驱动程序开发PDF是一本全面而实用的教材,适合有一定嵌入式系统开发经验的工程师和学者阅读。通过学习该书,读者可以掌握在VxWorks操作系统中开发PCIe设备驱动程序的基本原理和方法,提高驱动程序开发的效率和质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值