Project Description

The DeckLink Mini Recorder 4K is a PCI Express capture card featuring one 6G-SDI and one HDMI 2.0a connection. It can record all formats uncompressed up to 2160p30. The big difference with this card compared to many other capture cards is that it captures all the raw data uncompressed. This allows for it to be used for automatic testing of graphics cards.

Blackmagic provides a Linux driver for this card but it is proprietary and provides only a custom interface/SDK to access the card. We need this to be a fully functional V4L2 device and preferably work on non x86_64 architectures.

The existing driver consists of a proprietary blob and an open shim. To reverse engineer the device I plan to use tracing on the shim part of the proprietary driver.

Goal for this Hackweek

The goal would be to have a working V4L2 device driver that can eventually be upstreamed into the kernel.


DeckLink - Techical Specifications

Looking for hackers with the skills:

Nothing? Add some keywords!

This project is part of:

Hack Week 20 Hack Week 21


  • almost 2 years ago: cakeisamadeupdrug liked this project.
  • almost 2 years ago: ybonatakis liked this project.
  • almost 2 years ago: dmdiss liked this project.
  • about 3 years ago: vliaskovitis liked this project.
  • over 3 years ago: patrikjakobsson started this project.
  • over 3 years ago: patrikjakobsson originated this project.

  • Comments

    • patrikjakobsson
      almost 2 years ago by patrikjakobsson | Reply

      This is the second hackweek I'm working on this project. I'm using a black-box approach to reverse engineer the hardware. By tracing all the accesses to the hardware I can deduce what the registers do and hopefully write a device driver that can control enough of the hardware to support it as a V4L2 device.

    • patrikjakobsson
      almost 2 years ago by patrikjakobsson | Reply

      Hackweek 21 has come and gone. I got quite a bit further but unfortunately haven't figured out enough to start streaming data from the device.

      The driver consists of a binary blog which connects to a shim-layer that interacts with the linux kernel. I'm using a blackbox approach to reverse engineering the driver. In all relevant calls (eg. mmio) into the shim-layer I add a printk of the function name and parameters of interest. I also add a trace dump that my kernel log parser can pick up. With this method it is possible to see the function names of the binary blob functions that are calling the shim. This helps me to figure out the purpose of the registers.

      All this information ends up in the kernel log and I have written a special parser to collect all this information in a readable format.

      Here's a snippet of what the parsed output look like:

      310 1,615.2875  ioread32() reg = SET_VIDEO_INPUT_SOURCE_2_0x84 val = 0x10 <TASK> _ZN21BlackmagicIOPCIDevice12memoryRead32Ehy+0x32/0x34 ? _raw_spin_unlock_irqrestore+0xa/0x2e ...
      311 1,615.2882  iowrite32() reg = SET_VIDEO_INPUT_SOURCE_2_0x84 val = 0x16 <TASK> _ZN21BlackmagicIOPCIDevice13memoryWrite32Ehyj+0x33/0x36 ? _raw_spin_unlock_irqrestore+0xa/0x2e ...
      312 1,615.2889  iowrite32() reg = IER_0x44 val = 0xc67f3c0f <TASK> _ZN21BlackmagicIOPCIDevice13memoryWrite32Ehyj+0x33/0x36 ? _ZN23RegisterAccessInterface13writeToDeviceEjj+0x16/0x18 ...
      313 1,615.2895  ioread32() reg = DETECTED_INPUT_MODE_0x9c val = 0x403 <TASK> _ZN21BlackmagicIOPCIDevice12memoryRead32Ehy+0x32/0x34 ? _ZN23RegisterAccessInterface14readFromDeviceEj+0x11/0x14 ...
      314 1,615.2908  ioread32() reg = GET_DETECTED_INPUT_MODE_0x228 val = 0x0 <TASK> _ZN21BlackmagicIOPCIDevice12memoryRead32Ehy+0x32/0x34 ? _ZN23RegisterAccessInterface14readFromDeviceEj+0x11/0x14 ...
      315 1,615.2921  ioread32() reg = DETECTED_INPUT_MODE_0x9c val = 0x403 <TASK> _ZN21BlackmagicIOPCIDevice12memoryRead32Ehy+0x32/0x34 ? _ZN23RegisterAccessInterface14readFromDeviceEj+0x11/0x14 ...
      316 1,615.2934  ioread32() reg = VIDEO_INPUT_MODE_0x1bc val = 0x0 <TASK> 
      317 1,615.2934  ioread32() reg = ISR_0x40 val = 0x1000 
      318 1,615.2949  ioread32() reg = VIDEO_INPUT_MODE_0x1bc val = 0x0 <IRQ> _ZN21BlackmagicIOPCIDevice12memoryRead32Ehy+0x32/0x34 ? _ZN23RegisterAccessInterface14readFromDeviceEj+0x11/0x14 ...
      319 1,615.2968  ioread32() reg = STATUS_MONITOR_0x108 val = 0x0 <TASK> _ZN21BlackmagicIOPCIDevice12memoryRead32Ehy+0x32/0x34 ? _ZN23RegisterAccessInterface14readFromDeviceEj+0x11/0x14 ...
      320 1,615.2980  ioread32() reg = DETECTED_INPUT_MODE_0x9c val = 0x403 <TASK> _ZN21BlackmagicIOPCIDevice12memoryRead32Ehy+0x32/0x34 ? _ZN23RegisterAccessInterface14readFromDeviceEj+0x11/0x14 ...
      321 1,615.2993  ioread32() reg = GET_DETECTED_INPUT_MODE_0x228 val = 0x0 <TASK> _ZN21BlackmagicIOPCIDevice12memoryRead32Ehy+0x32/0x34 ? _ZN23RegisterAccessInterface14readFromDeviceEj+0x11/0x14 ...

      The output is colored to provide readability but this is not shown in the example above. The format is pretty simple but needs some explaining. The first column (ie 310) is the row number of the entire log. If you pass the row number to the parser it will show additional details of that specific trace (see example below). The second column is the timestamp in seconds. The third column is the function name which follows by the relevant function parameters (ie reg, val, etc...). After that comes the context of the backtrace (ie <task> or <irq>). After that follow the first couple of lines from the backtrace.

      Here's an example of a detailed view of a trace:

      0 1,615.2968    ioread32() reg = STATUS_MONITOR_0x108 val = 0x0 
              ? _ZN23RegisterAccessInterface14readFromDeviceEj+0x11/0x14
              ? _ZN23RegisterAccessInterface12readRegisterEj+0x27/0x2a
              ? _ZN19VideoHardwareDevice12updateStatusEP13StatusMonitor+0x273/0x402
              ? _ZN8OSObjectC2EPK11OSMetaClass+0xe/0x20
              ? _ZN19VideoHardwareDevice7messageEjP9IOServicePv+0x22d8/0x22f6
              ? _ZN20OSCollectionIterator14withCollectionEPK12OSCollection+0x31/0x48
              ? _ZNK8OSObject6retainEv+0x2c/0x36
              ? _ZNK15OSMetaClassBase8metaCastEPK11OSMetaClass+0x13/0x34
              ? _ZN9IOService14messageClientsEjPvl+0x67/0x86
              ? _ZN18BlackmagicIODriver7messageEjP9IOServicePv+0x29d/0x37c
              ? console_unlock+0x2d5/0x4b0
              ? _ZN13StatusMonitorD0Ev+0x20/0x20
              ? _ZN13StatusMonitor22statusUpdateTimerFiredEP8OSObjectP18IOTimerEventSource+0x31/0x48
              ? _ZN10IOWorkLoop9runActionEPFiP8OSObjectPvS2_S2_S2_ES1_S2_S2_S2_S2_+0x44/0x66
              ? _ZN13StatusMonitor7messageEjP9IOServicePv+0xf4/0x108
              ? _ZN18BlackmagicIODriver7messageEjP9IOServicePv+0x358/0x37c
              ? _ZN19VideoHardwareDevice16enableVideoInputEv+0x8d/0x96
              ? _ZN19VideoInputInterface6enableEP15UserClientClassjb+0x77/0xd8
              ? _ZN15UserClientClass12drawFrameNowEP29BlackmagicOutputFrameParamRecj+0x2e/0x34
              ? _ZN15UserClientClass16enableVideoInputEj+0x2a/0x36
              ? _ZNK4Util20UserClientDispatcher10DispatcherINS0_18ScalarSetScalarGetILj1ELj0EEEM15UserClientClassFijEEclEPS4_P25IOExternalMetho+0x4e/0x56
              ? _ZN15UserClientClass14externalMethodEjP25IOExternalMethodArgumentsP24IOExternalMethodDispatchP8OSObjectPv+0x576/0x98c
              ? _ZN18IoctlMessageKernel6unpackEv+0x146/0x150
              ? _ZN20UserClientClassLinux5ioctlEjm+0xae/0xe0
              ? bmio_client_ioctl+0xc/0xe
              ? bmio_ioctl+0x16/0x30
              ? __x64_sys_ioctl+0x8e/0xd0
              ? do_syscall_64+0x67/0x80
              ? do_syscall_64+0x5b/0x80
              ? do_user_addr_fault+0x1ff/0x730
              ? exc_page_fault+0x71/0x170
              ? entry_SYSCALL_64_after_hwframe+0x44/0xae

    • patrikjakobsson
      almost 2 years ago by patrikjakobsson | Reply

      At the very end of a trace there is a summary that shows all the accessed registers and how they were used. This is also very useful for trying to figure out which bits are being used for different operations.

      Here's an example output of the summary:

      ISR_0x40 R 0x1000 0x800 0x400 0x20000 0x2
      SET_VIDEO_INPUT_SOURCE_2_0x84 RW 0x10 0x16 0x16 0x10
      IER_0x44 W 0xc67f3c0f 0xc67f300f
      DETECTED_INPUT_MODE_0x9c R 0x403
      VIDEO_INPUT_MODE_0x1bc R 0x0
      STATUS_MONITOR_0x108 R 0x0
      SET_VIDEO_INPUT_PIXEL_FORMAT_0x94 RW 0x0 0x40e 0x40e 0x1040e
      DMA_CTL_0x20 RW 0x30 0xb0 0xb8 0xb0 0xb8 0xb2 0x30
      DMA_TRANS_2_0x18 W 0x1
      DMA_TRANS_3_0x1c W 0x4c281000
      DMA_TRANS_5_0x3c W 0x20
      SET_NEXT_INPUT_FRAME_2_0x90 W 0x4000000 0x491e600 0x523cc00
      SET_NEXT_INPUT_FRAME_1_0x74 W 0x43f4800 0x4d12e00 0x5631400
      DMA_UNK_0x8 W 0x1
      DMA_ADDR_0xc W 0x3781000 0xb9c1000 0x10d01000 0x18141000
      DMA_LEN_0x34 W 0x30 0x40 0x50
      AUDIO_INPUT_WRITE_SAMPLE_POSITION_0xa0 R 0x6400000 0xc800000

      The names of the registers are made up by me based on what they seem to be used for. They may of may not be correct at this point. I will provide more information on what I know about the mode detection, dma transfers and irq handling in a later post.

    Similar Projects

    This project is one of its kind!