关于对使用 i.MX6 的 mx6s_capture 驱动的应用程序键入 ctrl+C 的一个 BUG

        在 i.MX6 的视频接口(CSI)驱动 mx6s_capture 中发现了一个 BUG,如果使用该驱动的应用在没有更改 SIGINT 的行为的情况下,键入 ctrl+C 退出应用后会卡住。

        经过调试,发现驱动卡在了函数 mx6s_csi_disable 中,驱动此时无法对 CSI 寄存器进行任何操作:

static void mx6s_csi_disable(struct mx6s_csi_dev *csi_dev)
{
    struct v4l2_pix_format *pix = &csi_dev->pix;

    csi_dmareq_rff_disable(csi_dev);
    csi_disable_int(csi_dev);

    /* set CSI_CSIDMASA_FB1 and CSI_CSIDMASA_FB2 to default value */
    csi_write(csi_dev, 0, CSI_CSIDMASA_FB1);
    csi_write(csi_dev, 0, CSI_CSIDMASA_FB2);

    csi_buf_stride_set(csi_dev, 0);

    if (pix->field == V4L2_FIELD_INTERLACED) {
        csi_deinterlace_enable(csi_dev, false);
        csi_tvdec_enable(csi_dev, false);
    }

    csi_enable(csi_dev, 0);
}

        加入调试信息才发现 ctrl+C 的退出调用顺序变成了:

由系统自行调用 mx6s_csi_close,相当于调用了 close(fd) 关闭文件。

-> mx6s_csi_close
        -> mx6s_csi_deinit
                -> csi_clk_disable
                        -> clk_disable_unprepare(先关闭了 CSI 时钟)

由系统自行调用 mx6s_stop_streaming,相当于调用了 ioctl(fd, VIDIOC_STREAMOFF, ...) 停止数据传输。

-> mx6s_stop_streaming
        -> mx6s_csi_disable(无法对 CSI 寄存器进行任何操作)

        然而应用程序的正常退出调用是这样的顺序: 

ioctl(fd, VIDIOC_STREAMOFF, ...)
        -> video_ioctl2
                -> mx6s_vidioc_streamoff
                        -> vb2_streamoff
                                -> mx6s_stop_streaming
                                        -> mx6s_csi_disable(CSI 寄存器相关清理工作)

close(fd)
        -> mx6s_csi_close
                -> mx6s_csi_deinit
                        -> csi_clk_disable
                                -> clk_disable_unprepare(最后关闭 CSI 时钟)

        结论是 ctrl+C 导致 stop_streaming 和 close 的顺序调转了。因为 stop_streaming 必须 CSI 的时钟工作才会正常运行,并且 close 会关闭 CSI 时钟,所以它们的顺序是不能调转的,必须先 stop_streaming 然后再 close。

解决办法如下二选一:

一、修改应用

        设置 SIGINT 的行为,加上 ioctl(fd, VIDIOC_STREAMOFF, ...) 和 close(fd) 语句。

二、修改驱动

        mx6s_capture 的 clock_prepare_enable 只存在于文件操作 open 中,clk_disable_unprepare 只存在于 close 操作中,这是引起 BUG 的根本原因,把这两个函数加到别的地方去。

        凡是涉及到寄存器初始化和清理工作的封装函数的头尾加上  clock_prepare_enable / clk_disable_unprepare。而对于 start_streaming 和 stop_streaming 这两个与传输有关的函数,    前者加上 clock_prepare_enable,后者加上 clk_disable_unprepare。

        这两个函数重复使用不会造成寄存器出现问题,因为它们调用的 clk_core_enable / clk_core_disable 函数中会判断时钟的引用次数,当时钟未开启,调用 clock_prepare_enable 会真正开启时钟,否则都是把时钟的引用次数加一。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值