Showing posts with label pci. Show all posts
Showing posts with label pci. Show all posts

Thursday, July 12, 2018

Linux PCI Driver Setup and Teardown

Leave a Comment

After looking at the kernel docs here: https://www.kernel.org/doc/Documentation/PCI/pci.txt I am lost as to the ordering of function calls to set up and tear down a PCI driver.

I have two questions:

  1. For setup, does pci_enable_device() always come before pci_request_regions()? The documentation seems to point to this fact, but does state:

    OS BUG: we don't check resource allocations before enabling those resources. The sequence would make more sense if we called pci_request_resources() before calling pci_enable_device(). Currently, the device drivers can't detect the bug when when two devices have been allocated the same range. This is not a common problem and unlikely to get fixed soon. This has been discussed before but not changed as of 2.6.19: http://lkml.org/lkml/2006/3/2/194

    However, after doing a quick look through of the source code of several drivers, the consensus is that pci_enable_device() always comes first. Which one of these calls is supposed to come first and why?

  2. For tearing down the driver, I get even more confused. Assuming pci_enable_device() comes first, I would expect that you first call pci_release_regions() prior to calling pci_disable_device() (i.e., following some symmetry). However, the kernel docs say that pci_release_regions() should come last. What makes matters more complicated is that I looked at many drivers and almost all of them had pci_release_regions() before pci_disable_device(), like I would expect. However, I then stumbled across this driver: https://elixir.bootlin.com/linux/v4.12/source/drivers/infiniband/hw/hfi1/pcie.c (code is reproduced below).

    void hfi1_pcie_cleanup(struct pci_dev *pdev) {     pci_disable_device(pdev);     /*      * Release regions should be called after the disable. OK to      * call if request regions has not been called or failed.      */     pci_release_regions(pdev); } 

    Which function is supposed to come first when tearing down the driver? It seems that drivers in the kernel itself can't agree.

1 Answers

Answers 1

The statement that gives a final say is as follows :

o wake up the device if it was in suspended state,

o allocate I/O and memory regions of the device (if BIOS did not),

o allocate an IRQ (if BIOS did not).

So, it makes no sense to ask kernel to reserve resource if there is none. In most cases when we do not need to allocate the resource because it has been done by bios, in those cases we can keep either function first but do only if you are absolutely sure.

Read More

Thursday, April 21, 2016

Accessing the PCI config space with Win32 API

Leave a Comment

Given the address of a PCI device (i.e. bus, device, function), how can one programatically read, using Win32 API calls in userspace, the config space (e.g. vendor ID, device ID) for that device?

On Linux, one openes the /sys/bus/pci/devices//config file and reads from the right offset.

1 Answers

Answers 1

You can use the SetupDiGetClassDevs API to traverse a list of devices. If you pass PCI in the Enumerator parameter, you can enumerate only PCI devices.

Read More

Sunday, April 17, 2016

How to detect if HDMI cable is plugged into PCMCIA card / no signal?

Leave a Comment

I'm trying to write a simple helper application that is used to prompt the user to turn on a camcorder if no signal is detected, which in this case would mean the camcorder is off and/or the HDMI cable is not plugged into the PCMCIA capture card. If the signal is present, then I launch the respective recording application, in this case Wirecast.

How could I perhaps go about creating this using C# in VisualStudio?

Update

I think I'm alot closer now by trying a suggestion based in one of the comments suggesting to use GraphEdit and seeing what is available on the hardware. I was able to find within the properties of the capture device, a 'Signal Detected' flag that changes from 0 to 1 if the camcorder is on/off or the HDMI cable is unplugged, which is what I want.

Now, How would I go about accessing this flag through code? I think I'm really close, but don't know how to access the structure of cElems and pElems from the caGUID. cElems returns a value of 3, which is the same number of tabs displayed in the GraphEdit property window shown below in a screenshot. pElems returns a different value every time I run the app, so I'm not sure what's going on in that structure. I would think the flag I'm looking for lies somewhere within those structures.

Code:

using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; using DirectShowLib;  namespace Test {     static class Program     {         [STAThread]          static void Main()         {             using (System.Threading.Mutex mutex = new System.Threading.Mutex(false, "Global\\" + appGuid))             {                 if (!mutex.WaitOne(0, false))                 {                     return;                 }                  DsDevice[] capDevices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);                  foreach (var dev in capDevices)                 {                     if (dev.DevicePath == @"@device:pnp:\\?\pci#ven_1131&dev_7160&subsys_12abf50a&rev_03#6&37bccbbe&0&000800e1#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\{6f814be9-9af6-43cf-9249-c0340100021c}")                     {                         IFilterGraph2 m_FilterGraph = (IFilterGraph2)new FilterGraph();                          IBaseFilter capFilter = null;                         ICaptureGraphBuilder2 capGraph = null;                          capGraph = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();                          int hr;                          hr = capGraph.SetFiltergraph(m_FilterGraph);                         hr = m_FilterGraph.AddSourceFilterForMoniker(dev.Mon, null, dev.Name, out capFilter);                          ISpecifyPropertyPages pProp = capFilter as ISpecifyPropertyPages;                          FilterInfo filterInfo;                         hr = capFilter.QueryFilterInfo(out filterInfo);                          DsCAUUID caGUID;                          hr = pProp.GetPages(out caGUID);                          Console.WriteLine(caGUID.cElems);                         Console.WriteLine(caGUID.pElems);                          // caGUID.cElems returns '3', which is the correct number of tabs in the property pages shown in GraphEdit.                         // caGUID.pElems returns a different value every time                          break;                     }                 }                  Application.EnableVisualStyles();                 Application.SetCompatibleTextRenderingDefault(false);                 Application.Run(new Form1());             }         }          private static string appGuid = "z0a76b5a-02cd-15c5-b9d9-d303zcdde7b9";     } } 

enter image description hereenter image description here

3 Answers

Answers 1

I can't translate to C# because I no longer use Windows really, but if you are fine with translating the following C++ to C# then you can use it..

There is this WinAPI called RegisterDeviceNotification that lets you know when a device is plugged in or its state changed via a WinProc-Callback..

Taken from my Github: https://github.com/Brandon-T/HDMI

See also: https://msdn.microsoft.com/en-us/library/windows/desktop/aa363480(v=vs.85).aspx and https://msdn.microsoft.com/en-us/library/windows/desktop/aa363431(v=vs.85).aspx

List of GUID's that I've used in my own projects:

GUID devices[] = {     {0x4D36E96E, 0xE325, 0x11CE, 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18},  //PlugNPlay Display     {0xA5DCBF10, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED},  //GUID_DEVINTERFACE_USB_DEVICE     {0x0850302A, 0xB344, 0x4FDA, 0x9B, 0xE9, 0x90, 0x57, 0x6B, 0x8D, 0x46, 0xF0},  //GUID_BTHPORT_DEVICE_INTERFACE     {0xE6F07B5F, 0xEE97, 0x4a90, 0xB0, 0x76, 0x33, 0xF5, 0x7B, 0xF4, 0xEA, 0xA7},  //GUID_DEVINTERFACE_MONITOR     {0x1CA05180, 0xA699, 0x450A, 0x9A, 0x0C, 0xDE, 0x4F, 0xBE, 0x3D, 0xDD, 0x89},  //GUID_DISPLAY_DEVICE_ARRIVAL     {0x5B45201D, 0xF2F2, 0x4F3B, 0x85, 0xBB, 0x30, 0xFF, 0x1F, 0x95, 0x35, 0x99},  //GUID_DEVINTERFACE_DISPLAY_ADAPTER     {0x1AD9E4F0, 0xF88D, 0x4360, 0xBA, 0xB9, 0x4C, 0x2D, 0x55, 0xE5, 0x64, 0xCD},  //GUID_DEVINTERFACE_VIDEO_OUTPUT_ARRIVAL }; 

Then I create a class to monitor a specific device:

#include <windows.h> #include <dbt.h> #include <algorithm>  class Device { private:     HDEVNOTIFY hNotify;  public:     Device() : hNotify(NULL) {}     Device(HWND window, GUID dev_guid);     Device(Device&& dev) : hNotify(NULL) {std::swap(hNotify, dev.hNotify);}     ~Device() {UnregisterDeviceNotification(hNotify);}      Device(const Device& dev) = delete;     Device& operator = (const Device& dev) = delete;     Device& operator = (Device&& dev) {std::swap(hNotify, dev.hNotify);return *this;} };  Device::Device(HWND window, GUID dev_guid) : hNotify(NULL) {     if (window)     {         DEV_BROADCAST_DEVICEINTERFACE filter;         memset(&filter, 0, sizeof(filter));         filter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);         filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;         filter.dbcc_classguid = dev_guid;         hNotify = RegisterDeviceNotification(window, &filter, DEVICE_NOTIFY_WINDOW_HANDLE); //DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES     } } 

Finally, I create a window/message-window to monitor the devices:

int Create() {     WNDCLASSEX wx = {0};     wx.cbSize = sizeof(WNDCLASSEX);     wx.lpfnWndProc = WndProc;     wx.hInstance = GetModuleHandle(NULL);     wx.lpszClassName = "HDMI_MONITOR";     if (RegisterClassEx(&wx))     {         MSG msg = {0};         CreateWindowEx(0, "HDMI_MONITOR", "HDMI_MONITOR", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);         while(GetMessage(&msg, NULL, 0, 0) > 0)         {             TranslateMessage(&msg);             DispatchMessage(&msg);         }         return msg.wParam;     }     return 0; }   //The callback function: LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {     static std::unique_ptr<Device> dev;      switch(msg)     {         case WM_CREATE:         {             dev.reset(new Device(hwnd, devices[0])); //GUID for plug-n-play devices..         }         break;          case WM_DEVICECHANGE:         {             DEV_BROADCAST_DEVICEINTERFACE* info = (DEV_BROADCAST_DEVICEINTERFACE*) lParam;              switch(wParam)             {                 case DBT_DEVICEARRIVAL:                     std::cout<<"Device was plugged in\n";                     break;                  case DBT_DEVICEREMOVECOMPLETE:                     std::cout<<"Device was un-plugged in\n";                     break;                  default:                     std::cout<<"wParam: "<<(void*)wParam<<"\n";                     break;             }         }         break;          case WM_DESTROY:             PostQuitMessage(0);             break;          default:             return DefWindowProc(hwnd, msg, wParam, lParam);     }     return 0; } 

Answers 2

This really may depend on the vendor, but testing with a similar device, the vendor is writing to a custom registry key when the device is plugged in and removed.

  • To detect registry changes before and after an event I use RegShot, there is a handy tutorial over here that describes the process.
  • From this, once you have hopefully determined what key they are updating, you can subscribe via WMI to the Registry. Check out this answer for how to do this

Answers 3

I believe I just figured it out! I stumbled upon the IAMAnalogVideoDecoder method: get_HorizontalLocked.

This method returns true or false for me when the camcorder is off and/or the HDMI cable is unplugged, which is perfect for my needs.

DirectShowLib can be found here: https://sourceforge.net/projects/directshownet/files/

using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; using DirectShowLib;  namespace Test {     static class Program     {         [STAThread]          static void Main()         {             using (System.Threading.Mutex mutex = new System.Threading.Mutex(false, "Global\\" + appGuid))             {                 if (!mutex.WaitOne(0, false))                 {                     return;                 }                  DsDevice[] capDevices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);                  foreach (var device in capDevices)                 {                     if (device.DevicePath == @"@device:pnp:\\?\pci#ven_1131&dev_7160                        &subsys_12abf50a&rev_03#6&37bccbbe&0&000800e1#{65e8773d-8f56                        -11d0-a3b9-00a0c9223196}\{6f814be9-9af6-43cf                        -9249-c0340100021c}")                     {                         IFilterGraph2 m_FilterGraph = (IFilterGraph2)new FilterGraph();                          IBaseFilter capFilter = null;                         ICaptureGraphBuilder2 capGraph = null;                          capGraph = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();                          int hr;                          hr = capGraph.SetFiltergraph(m_FilterGraph);                         hr = m_FilterGraph.AddSourceFilterForMoniker(device.Mon, null, device.Name, out capFilter);                          IAMAnalogVideoDecoder videoDec = capFilter as IAMAnalogVideoDecoder;                          bool signalDetected = false;                          hr = videoDec.get_HorizontalLocked(out signalDetected);                          if (signalDetected == true)                         {                             System.Diagnostics.Process.Start(                             @"C:\Users\PC\Documents\HIDDEN_FOLDER\WirecastLaunch.wcst");                              return;                         }                         else                         {                             // Poll for 'signal' change                         }                          break;                     }                 }                  Application.EnableVisualStyles();                 Application.SetCompatibleTextRenderingDefault(false);                 Application.Run(new Form1());             }         }          private static string appGuid = "z0a76b5a-02cd-15c5-b9d9-d303zcdde7b9";     } } 
Read More