IPANIC at (sep_pci_suspend+0x1e/0x80)

本文探讨了在处理PCIE设备初始化过程中遇到的问题,重点分析了sep_probe函数中初始化失败的原因,并提供了解决策略,避免了驱动绑定错误和内存访问异常导致的系统崩溃。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

慢慢的的将理论的东西与实际相结合了,这个问题很奇怪,手机不到几分钟,就会报这样的错误,问题其实根本就跟这个sep_pci_suspend函数关系并不是很大,主要是因为在probe函数中,没有初始化好函数,

drivers/staging/sep/sep_main.c中:

/**
 *sep_probe - probe a matching PCI device
 *@pdev:    pci_device
 *@ent:    pci_device_id
 *
 *Attempt to set up and configure a SEP device that has been
 *discovered by the PCI layer. Allocates all required resources.
 */
static int __devinit sep_probe(struct pci_dev *pdev,
    const struct pci_device_id *ent)
{
    int error = 0;
    struct sep_device *sep = NULL;

    /* Used for telling the sep our new shared memory physical address */
    struct sep_msg_shared_mem_addr_to_sep *shm_to_sep = NULL;
    struct sep_msg_shared_mem_addr_from_sep *shm_from_sep = NULL;

    if (sep_dev != NULL) {
        dev_dbg(&pdev->dev, "only one SEP supported.\n");
        return -EBUSY;
    }

    /* Enable the device */
    error = pci_enable_device(pdev);
    if (error) {
        dev_warn(&pdev->dev, "error enabling pci device\n");
        goto end_function;
    }

    /* Allocate the sep_device structure for this device */
    sep_dev = kzalloc(sizeof(struct sep_device), GFP_ATOMIC);
    if (sep_dev == NULL) {
        dev_warn(&pdev->dev,
            "can't kmalloc the sep_device structure\n");
        error = -ENOMEM;
        goto end_function_disable_device;
    }

    /*
     * We're going to use another variable for actually
     * working with the device; this way, if we have
     * multiple devices in the future, it would be easier
     * to make appropriate changes
     */
    sep = sep_dev;

    sep->pdev = pci_dev_get(pdev);

    init_waitqueue_head(&sep->event_transactions);
    init_waitqueue_head(&sep->event_interrupt);
    spin_lock_init(&sep->snd_rply_lck);
    spin_lock_init(&sep->sep_queue_lock);
    sema_init(&sep->sep_doublebuf, SEP_DOUBLEBUF_USERS_LIMIT);

    INIT_LIST_HEAD(&sep->sep_queue_status);

    dev_dbg(&sep->pdev->dev,
        "sep probe: PCI obtained, device being prepared\n");
    dev_dbg(&sep->pdev->dev, "revision is %d\n", sep->pdev->revision);

    /* Set up our register area */
    sep->reg_physical_addr = pci_resource_start(sep->pdev, 0);
    if (!sep->reg_physical_addr) {
        dev_warn(&sep->pdev->dev, "Error getting register start\n");
        error = -ENODEV;
        goto end_function_free_sep_dev;
    }

    sep->reg_physical_end = pci_resource_end(sep->pdev, 0);
    if (!sep->reg_physical_end) {
        dev_warn(&sep->pdev->dev, "Error getting register end\n");
        error = -ENODEV;
        goto end_function_free_sep_dev;
    }

    sep->reg_addr = ioremap_nocache(sep->reg_physical_addr,
        (size_t)(sep->reg_physical_end - sep->reg_physical_addr + 1));
    if (!sep->reg_addr) {
        dev_warn(&sep->pdev->dev, "Error getting register virtual\n");
        error = -ENODEV;
        goto end_function_free_sep_dev;
    }

    dev_dbg(&sep->pdev->dev,
        "Register area start %llx end %llx virtual %p\n",
        (unsigned long long)sep->reg_physical_addr,
        (unsigned long long)sep->reg_physical_end,
        sep->reg_addr);

    /* Allocate the shared area */
    sep->shared_size = SEP_DRIVER_MESSAGE_SHARED_AREA_SIZE_IN_BYTES +
        SYNCHRONIC_DMA_TABLES_AREA_SIZE_BYTES +
        SEP_DRIVER_DATA_POOL_SHARED_AREA_SIZE_IN_BYTES +
        SEP_DRIVER_STATIC_AREA_SIZE_IN_BYTES +
        SEP_DRIVER_SYSTEM_DATA_MEMORY_SIZE_IN_BYTES;

    if (sep_map_and_alloc_shared_area(sep)) {
        error = -ENOMEM;
        /* Allocation failed */
        goto end_function_error;
    }

    /* Clear ICR register */
    sep_write_reg(sep, HW_HOST_ICR_REG_ADDR, 0xFFFFFFFF);

    /* Set the IMR register - open only GPR 2 */
    sep_write_reg(sep, HW_HOST_IMR_REG_ADDR, (~(0x1 << 13)));

    /* Initialize send/receive counters */
    sep->send_ct = sep->reply_ct = 0;

    /* Get the interrupt line */
    error = request_irq(pdev->irq, sep_inthandler, IRQF_SHARED,
        "sep_driver", sep);

    if (error)
        goto end_function_deallocate_sep_shared_area;

    /* The new chip requires a shared area reconfigure */
    error = sep_reconfig_shared_area(sep);
    if (error)
        goto end_function_free_irq;

    /* Create the rpmb workqueue */
    sep->rpmb_workqueue = create_workqueue(DRVNAME "-ewq");
    if (unlikely(!sep->rpmb_workqueue)) {
        dev_err(&sep->pdev->dev, "Unable to create rpmb workqueue");
        error = -EFAULT;
        goto end_function_free_irq;
    }
    INIT_WORK(&sep->rpmb_work, rpmb_process_request);

    sep->in_use = 1;

    /**
     * Now we need to tell the sep the newly acqured shared
     * memory physical address.
     * This also serves the purpose of informing the sep that
     * we are in post-os boot; ie; that it's the os that is
     * now using the sep and no longer the scu. This is required
     * for the RPMB functionality.
     */

    /* First set the outgoing and incomming message pointers */
    dev_dbg(&sep->pdev->dev, "Sending shared phys to sep\n");

    shm_to_sep = (struct sep_msg_shared_mem_addr_to_sep *)
        (sep->shared_addr +
        SEP_DRIVER_MESSAGE_AREA_OFFSET_IN_BYTES);

    shm_from_sep = (struct sep_msg_shared_mem_addr_from_sep *)
        (sep->shared_addr +
        SEP_DRIVER_MESSAGE_AREA_OFFSET_IN_BYTES);

    /* fill in the outgoing meessage to the sep */

    shm_to_sep->token = SEP_START_MSG_TOKEN;
    shm_to_sep->command = DX_SEP_HOST_SEP_SEP_DRIVER_LOADED_OP_CODE;
    shm_to_sep->shared_phys = (u32)sep->shared_bus;
    shm_to_sep->crc = 0;
    shm_to_sep->size = 7;

    /* Now send this command */
    error = sep_send_command_handler(sep);

    /* Now wait for sep */
    wait_event_timeout(sep->event_interrupt,
        (test_bit(SEP_WORKING_LOCK_BIT,
            &sep->in_use_flags) == 0),
        (WAIT_TIME * HZ));
     error = -1;
    /* see if the sep got the message okay */
    if (shm_from_sep->token != SEP_START_MSG_TOKEN) {
        dev_dbg(&sep->pdev->dev, "shm_from_sep->token incorrect\n");
        goto end_function_free_workqueue;
    }

    if (shm_from_sep->command !=
        DX_SEP_HOST_SEP_SEP_DRIVER_LOADED_OP_CODE) {

        dev_dbg(&sep->pdev->dev, "shm_from_sep->command incorrect\n");
        goto end_function_free_workqueue;
    }

    if (shm_from_sep->result != 0) {
        dev_dbg(&sep->pdev->dev, "shm_from_sep->result incorrect\n");
        dev_dbg(&sep->pdev->dev, "got %x instead of %x\n",
            shm_from_sep->result, 0);
        goto end_function_free_workqueue;
    }

    /* clear the lock bit */
    clear_bit(SEP_WORKING_LOCK_BIT, &sep->in_use_flags);

    dev_dbg(&sep->pdev->dev, "Done sending shared phys to sep\n");

    /* Finally magic up the device nodes */
    /* Register driver with the fs */
    error = sep_register_driver_with_fs(sep);

    if (error) {
        dev_dbg(&sep->pdev->dev, "error registering dev file\n");
        goto end_function_free_workqueue;
    }

    sep->in_use = 0; /* through touching the device */
#ifdef SEP_ENABLE_RUNTIME_PM
    pm_runtime_put_noidle(&sep->pdev->dev);
    pm_runtime_allow(&sep->pdev->dev);
    pm_runtime_set_autosuspend_delay(&sep->pdev->dev,
        SUSPEND_DELAY);
    pm_runtime_use_autosuspend(&sep->pdev->dev);
    pm_runtime_mark_last_busy(&sep->pdev->dev);
    sep->power_save_setup = 1;
#endif
    /* register kernel crypto driver */
#if defined(CONFIG_ENABLE_SEP_KERNEL_CRYPTO)
    error = sep_crypto_setup();
    if (error) {
        dev_dbg(&sep->pdev->dev, "crypto setup fail\n");
        goto end_function_free_workqueue;
    }
#endif
    goto end_function;

end_function_free_workqueue:
    destroy_workqueue(sep->rpmb_workqueue);

end_function_free_irq:
    free_irq(pdev->irq, sep);

end_function_deallocate_sep_shared_area:
    /* De-allocate shared area */
    sep_unmap_and_free_shared_area(sep);

end_function_error:
    iounmap(sep->reg_addr);

end_function_free_sep_dev:
    pci_dev_put(sep_dev->pdev);
    kfree(sep_dev);
    sep_dev = NULL;

end_function_disable_device:
    pci_disable_device(pdev);

end_function:
    return error;
}

/**
 * sep_remove -    handles removing device from pci subsystem
 * @pdev:    pointer to pci device
 *
 * This function will handle removing our sep device from pci subsystem on exit
 * or unloading this module. It should free up all used resources, and unmap if
 * any memory regions mapped.
 */
static void sep_remove(struct pci_dev *pdev)
{
    struct sep_device *sep = sep_dev;

    /* Destroy rpmb workqueue */
    destroy_workqueue(sep->rpmb_workqueue);

    /* Unregister from fs */
    misc_deregister(&sep->miscdev_sep);

    /* Unregister from kernel crypto */
    sep_crypto_takedown();

    /* Free the irq */
    free_irq(sep->pdev->irq, sep);

    /* Free the shared area  */
    sep_unmap_and_free_shared_area(sep_dev);
    iounmap((void *) sep_dev->reg_addr);

#ifdef SEP_ENABLE_RUNTIME_PM
    if (sep->in_use) {
        sep->in_use = 0;
        pm_runtime_forbid(&sep->pdev->dev);
        pm_runtime_get_noresume(&sep->pdev->dev);
    }
#endif
    pci_dev_put(sep_dev->pdev);
    kfree(sep_dev);
    sep_dev = NULL;

}

是不是感觉这种逻辑很奇怪,看看解决人是怎么说的:

if sep_probe probe failed, we should return a erro number instead zero.otherwise the pci core will binder the driver and call driver's callback function like sep_pci_suspend, in this function, it'll access no exist memory and cause ipanic.


呵呵,做点笔记,以后自己留着看,without the patch,error =0 if(!shm_from_sep->result != 0) and sep_probe rollback,but sep_probe still returns error = 0;we need returns -1 when sep_probe rollback,so pci framework wouldn't bind the driver to device.then ,at s3,sep driver's callback wouldn't be called.

<think>嗯,我需要分析用户提供的这段USB中断服务程序(ISR)。首先,我要理解代码的结构和各个部分的功能。代码开始保存了当前的端点索引,处理各种中断标志,然后恢复端点。这可能是为了确保在处理中断时不影响其他端点的操作。 接下来,用户希望了解这段代码的功能,可能需要分步骤解释每个部分的作用。例如,检查通用中断、发送和接收中断,处理设置包、总线复位等情况。我需要确保用中文清晰地解释每个条件判断和处理流程,同时符合LaTeX格式的要求。 需要注意的是代码中的变量和寄存器操作,比如g_msgflags和gUSBC_ComReg,这些可能涉及硬件寄存器的读写。要解释这些操作如何影响USB通信的状态。此外,中断处理中的各个标志位设置和清除是关键点,需要说明它们的作用,比如处理端点0的控制传输或总线复位事件。 还要注意代码中的注释,例如设置USB地址、处理Setup包等,这些都是USB协议中的标准步骤。需要将这些技术细节转化为用户容易理解的中文解释,同时避免错误。最后,总结整个中断处理的流程和功能,确保用户能够全面理解这段代码的作用。</think>这是一个典型的USB设备中断服务程序(ISR),主要用于处理USB通信过程中的各种中断事件。以下是对代码的逐部分解析: 1. **端点索引保存** ```C index = gUSBC_ComReg->EINDEX; // 保存当前端点索引 ``` 这是为了解决中断嵌套问题,先保存当前端点状态,处理完成后恢复现场。 2. **中断标志捕获** 通过三个标志寄存器捕获不同中断源: - `g_msgflags`:存储通用中断状态(`INTRUSB`) - `g_msgflags1`:存储发送端点中断(`INTRTX_L`) - `ucISR`:存储接收端点中断(`INTRRX_L`) 3. **Setup包处理** ```C if(g_msgflags1 & 0x1) // 检查控制端点0的中断 { if(g_USBNewAddr) // 处理地址变更请求 { gUSBC_ComReg->FADDRR = g_USBNewAddr; g_USBNewAddr = 0; } USBDev_EP0_ISR(); // 调用端点0中断服务 } ``` 处理控制传输的核心逻辑,负责设备地址设置和标准请求处理。 4. **接收数据处理** ```C if(g_msgflags&0x10) // 检查指定端点接收中断 { g_msgflags &= 0xEF; // 清除标志 g_uchUSBStatus |= BIT0; // 设置接收完成状态 } ``` 当指定端点接收到数据时,更新状态标志。 5. **发送完成处理** ```C if(g_msgflags1 & (1<<USB_ENDPOINT_INDEX)) { g_msgflags1 &= ~(...); g_uchUSBStatus |= BIT4; // 设置发送完成状态 } ``` 处理发送完成中断,更新对应状态位。 6. **总线事件处理** ```C if(g_msgflags & 0x04) // 总线复位 { USBDev_BusReset(); // 执行复位初始化 } if(g_msgflags & 0x1) // 挂起事件 { // USBDev_Suspend(); // 挂起处理(注释状态) } ``` 处理USB总线级事件,包括总线复位和挂起事件。 7. **清理阶段** ```C gUSBC_ComReg->EINDEX = index; // 恢复原始端点索引 ``` 保证中断处理完成后恢复原始端点配置。 **主要功能总结**: 该ISR实现了USB设备端的完整中断处理流程,包括: - 控制传输处理(端点0) - 批量数据传输(发送/接收) - 设备地址设置 - 总线状态管理(复位/挂起) - 中断标志的清除与状态更新 通过位操作(`|=`和`&=`)高效处理多个并发的USB事件,使用全局状态变量(`g_uchUSBStatus`)与上层通信。代码结构体现了典型的中断处理模式:保存现场→处理事件→恢复现场。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值