Determine Intel HD Graphics Card Version Via WinAPI - windows

So I am trying to get information on graphics cards via WinAPI functions, but have a question regarding how to get my exact card information. I have written code to call EnumDisplayDevices and through my call I receive back the DeviceString "Intel(R) HD Graphics Family". However this is not telling me the exact card, for example the Intel HD Graphics Family has a 4200, 4400, so on (from my understanding).
I searched around more for ways to find this information, even looking through the registry which still led me to a description string of "Intel(R) HD Graphics Family"... Now I am here. Thanks alot to anybody who can help me out, it was infact a stackoverflow thread which led me in the direction of EnumDisplayDevices in the first place :)

I would use OpenGL instead WinAPI for this because I do not know if WinAPI even has some feature like this and too lazy to research. Yes you can search registry keys for this info but that is not safe in the future due to possible location/name change. The OpenGL way is straight forward:
create OpenGL rendering context
obtain the appropriate vendor and device strings.
release OpenGL rendering context
It will work for any OpenGL capable graphic card/driver supporting OpenGL 1.0 which are all of the nowadays cards.
see: C++ example of OpenGL context initialization
or: complete GL+VAO/VBO+GLSL+shaders example in C++
You can use OpenGL function glGetString to obtain this info:
GL_VENDOR Returns the company responsible for this OpenGL implementation. This name does not change from release to release.
GL_RENDERER Returns the name of the renderer. This name is typically specific to a particular configuration of a hardware platform. It does not change from release to release.
GL_VERSION Returns a version or release number (of supported OpenGL and gfx driver version)
GL_EXTENSIONS Returns a space-separated list of supported extensions to OpenGL (all capabilities of your card)
The rest is easy (using gl_init,gl_exit from second link):
gl_init(Your_App_Window_Handle);
char *vendor =(char*)glGetString(GL_VENDOR);
char *device =(char*)glGetString(GL_RENDERER);
char *version=(char*)glGetString(GL_VERSION);
char *ext =(char*)glGetString(GL_EXTENSIONS);
gl_exit();
Mine setup returns:
vendor ="NVIDIA Corporation"
device ="GeForce GTX 550 Ti/PCIe/SSE2"
version="4.5.0 NVIDIA 347.25"
ext ="GL_AMD_multi_draw_indirect GL_ARB_arrays_of_arrays GL_ARB_base_instance GL_ARB_blend_func_extended GL_ARB_buffer_storage GL_ARB_clear_buffer_object GL_ARB_clear_texture GL_ARB_clip_control GL_ARB_color_buffer_float GL_ARB_compatibility GL_ARB_compressed_texture_pixel_storage GL_ARB_conservative_depth GL_ARB_compute_shader GL_ARB_compute_variable_group_size GL_ARB_conditional_render_inverted GL_ARB_copy_buffer GL_ARB_copy_image GL_ARB_cull_distance GL_ARB_debug_output GL_ARB_depth_buffer_float GL_ARB_depth_clamp GL_ARB_depth_texture GL_ARB_derivative_control GL_ARB_direct_state_access GL_ARB_draw_buffers GL_ARB_draw_buffers_blend GL_ARB_draw_indirect GL_ARB_draw_elements_base_vertex GL_ARB_draw_instanced GL_ARB_enhanced_layouts GL_ARB_ES2_compatibility GL_ARB_ES3_compatibility GL_ARB_ES3_1_compatibility GL_ARB_explicit_attrib_location GL_ARB_explicit_uniform_location GL_ARB_fragment_coord_conventions GL_ARB_fragment_layer_viewport GL_ARB_fragment_program GL_ARB_fragment_program_shadow GL_ARB_fragment_shader GL_ARB_framebuffer_no_attachments GL_ARB_framebuffer_object GL_ARB_framebuffer_sRGB GL_ARB_geometry_shader4 GL_ARB_get_program_binary GL_ARB_get_texture_sub_image GL_ARB_gpu_shader5 GL_ARB_gpu_shader_fp64 GL_ARB_half_float_pixel GL_ARB_half_float_vertex GL_ARB_imaging GL_ARB_indirect_parameters GL_ARB_instanced_arrays GL_ARB_internalformat_query GL_ARB_internalformat_query2 GL_NV_internalformat_sample_query GL_ARB_invalidate_subdata GL_ARB_map_buffer_alignment GL_ARB_map_buffer_range GL_ARB_multi_bind GL_ARB_multi_draw_indirect GL_ARB_multisample GL_ARB_multitexture GL_ARB_occlusion_query GL_ARB_occlusion_query2 GL_ARB_pipeline_statistics_query GL_ARB_pixel_buffer_object GL_ARB_point_parameters GL_ARB_point_sprite GL_ARB_program_interface_query GL_ARB_provoking_vertex GL_ARB_robust_buffer_access_behavior GL_ARB_robustness GL_ARB_sample_shading GL_ARB_sampler_objects GL_ARB_seamless_cube_map GL_ARB_separate_shader_objects GL_ARB_shader_atomic_counters GL_ARB_shader_bit_encoding GL_ARB_shader_draw_parameters GL
[Notes]
you need to include only gl.h for this no need for glu,glew or anything else (except wglext.h which should be included on its own inside gl.h). It is usually located in separate include subdirectory like:
#include <gl\gl.h>
[Edit1]
As Intel OpenGL implementation does not expose the version too I see only one other option. You need to acquire PCI ID VID/PID of the card and cross check against known values:
Model CPU arch. Release date PCI ID Execution units
HD Graphics Arrandale 2010 8086:0046 6
HD Graphics Sandy Bridge January 2011 8086:0106 6
HD Graphics 2000 Sandy Bridge January 2011 8086:0102 6
HD Graphics 3000 Sandy Bridge January 2011 8086:0116 16
HD Graphics 2500 Ivy Bridge April 2012 8086:016A 6
HD Graphics 4000 Ivy Bridge April 2012 8086:0166 16
HD Graphics 4400 Haswell 2013 20
HD Graphics 4600 Haswell 2013 8086:0416 20
HD Graphics 5500 Broadwell 2015 24
taken from here.
To obtain the PCI ID you can use this:
my setupapi.h C++ example
Just change the parsing string from TEXT("USB") to TEXT("PCI") and check VID 8086 devices so:
bool PCIinfo()
{
int i,n;
AnsiString s,txt="";
DWORD dwSize,dwPropertyRegDataType;
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
TCHAR szDesc[1024];
// hDevInfo = SetupDiGetClassDevs(NULL, NULL , NULL, DIGCF_PRESENT|DIGCF_ALLCLASSES); // List all devices
// hDevInfo = SetupDiGetClassDevs(NULL, TEXT("USB"), NULL, DIGCF_PRESENT|DIGCF_ALLCLASSES); // List all connected USB devices
hDevInfo = SetupDiGetClassDevs(NULL, TEXT("PCI"), NULL, DIGCF_PRESENT|DIGCF_ALLCLASSES); // List all connected PCI devices
if (hDevInfo == INVALID_HANDLE_VALUE) return false;
// Find the ones that are driverless
for (i=0;;i++)
{
DeviceInfoData.cbSize = sizeof(DeviceInfoData);
if (!SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData)) break;
SetupDiGetDeviceRegistryProperty(hDevInfo, &DeviceInfoData, SPDRP_DEVICEDESC,&dwPropertyRegDataType, (BYTE*)szDesc,sizeof(szDesc),&dwSize);
s=szDesc; n=48; while (s.Length()<n) s+=" "; if (s.Length()>n) s=s.SubString(1,n); txt+=s+" ";
SetupDiGetDeviceRegistryProperty(hDevInfo, &DeviceInfoData, SPDRP_HARDWAREID,&dwPropertyRegDataType, (BYTE*)szDesc,sizeof(szDesc),&dwSize);
s=szDesc; n=64; while (s.Length()<n) s+=" "; if (s.Length()>n) s=s.SubString(1,n); txt+=s+" ";
SetupDiGetDeviceRegistryProperty(hDevInfo, &DeviceInfoData, SPDRP_LOCATION_INFORMATION,&dwPropertyRegDataType, (BYTE*)szDesc,sizeof(szDesc),&dwSize);
s=szDesc; n=40; while (s.Length()<n) s+=" "; if (s.Length()>n) s=s.SubString(1,n); txt+=s+" ";
txt+="\r\n";
}
Main->mm_log->Lines->Add(txt);
return true;
}
that code returns on my setup this:
PCI standard host CPU bridge PCI\VEN_1022&DEV_1400&SUBSYS_00000000&REV_00 PCI bus 0, device 24, function 0
PCI standard host CPU bridge PCI\VEN_1022&DEV_1401&SUBSYS_00000000&REV_00 PCI bus 0, device 24, function 1
PCI standard host CPU bridge PCI\VEN_1022&DEV_1402&SUBSYS_00000000&REV_00 PCI bus 0, device 24, function 2
PCI standard host CPU bridge PCI\VEN_1022&DEV_1403&SUBSYS_00000000&REV_00 PCI bus 0, device 24, function 3
PCI standard host CPU bridge PCI\VEN_1022&DEV_1404&SUBSYS_00000000&REV_00 PCI bus 0, device 24, function 4
PCI standard host CPU bridge PCI\VEN_1022&DEV_1405&SUBSYS_00000000&REV_00 PCI bus 0, device 24, function 5
PCI standard host CPU bridge PCI\VEN_1022&DEV_1410&SUBSYS_14101022&REV_00 PCI bus 0, device 0, function 0
PCI Express standard Root Port PCI\VEN_1022&DEV_1412&SUBSYS_12341022&REV_00 PCI bus 0, device 2, function 0
PCI Express standard Root Port PCI\VEN_1022&DEV_1414&SUBSYS_12341022&REV_00 PCI bus 0, device 4, function 0
PCI Express standard Root Port PCI\VEN_1022&DEV_1417&SUBSYS_12341022&REV_00 PCI bus 0, device 7, function 0
AMD IOMMU Device PCI\VEN_1022&DEV_1419&SUBSYS_14191022&REV_00 PCI bus 0, device 0, function 2
AMD SATA Controller PCI\VEN_1022&DEV_7801&SUBSYS_B0021458&REV_40 PCI bus 0, device 17, function 0
Standard OpenHCD USB Host Controller PCI\VEN_1022&DEV_7807&SUBSYS_50041458&REV_11 PCI bus 0, device 18, function 0
Standard OpenHCD USB Host Controller PCI\VEN_1022&DEV_7807&SUBSYS_50041458&REV_11 PCI bus 0, device 19, function 0
Standard Enhanced PCI to USB Host Controller PCI\VEN_1022&DEV_7808&SUBSYS_50041458&REV_11 PCI bus 0, device 18, function 2
Standard Enhanced PCI to USB Host Controller PCI\VEN_1022&DEV_7808&SUBSYS_50041458&REV_11 PCI bus 0, device 19, function 2
Standard OpenHCD USB Host Controller PCI\VEN_1022&DEV_7809&SUBSYS_50041458&REV_11 PCI bus 0, device 20, function 5
AMD SMBus PCI\VEN_1022&DEV_780B&SUBSYS_780B1022&REV_14 PCI bus 0, device 20, function 0
High Definition Audio Controller PCI\VEN_1022&DEV_780D&SUBSYS_A0021458&REV_01 PCI bus 0, device 20, function 2
PCI standard ISA bridge PCI\VEN_1022&DEV_780E&SUBSYS_780E1022&REV_11 PCI bus 0, device 20, function 3
PCI standard PCI-to-PCI bridge PCI\VEN_1022&DEV_780F&SUBSYS_00000000&REV_40 PCI bus 0, device 20, function 4
AMD USB 3.0 Host Controller PCI\VEN_1022&DEV_7812&SUBSYS_50041458&REV_03 PCI bus 0, device 16, function 0
AMD USB 3.0 Host Controller PCI\VEN_1022&DEV_7812&SUBSYS_50041458&REV_03 PCI bus 0, device 16, function 1
High Definition Audio Controller PCI\VEN_10DE&DEV_0BEE&SUBSYS_35371458&REV_A1 PCI bus 1, device 0, function 1
NVIDIA GeForce GTX 550 Ti PCI\VEN_10DE&DEV_1244&SUBSYS_35371458&REV_A1 PCI bus 1, device 0, function 0
Realtek PCIe GBE Family Controller PCI\VEN_10EC&DEV_8168&SUBSYS_E0001458&REV_06 PCI bus 2, device 0, function 0
MSI TV#Anywhere Plus PCI\VEN_1131&DEV_7133&SUBSYS_62311462&REV_D1 PCI bus 4, device 6, function 0
Etron USB 3.0 Extensible Host Controller PCI\VEN_1B6F&DEV_7023&SUBSYS_50071458&REV_01 PCI bus 3, device 0, function 0
so run it on different IntelHD graphics computers and check if different PCI ID is there ...

The primary problem with "driver version" is that video driver consists of multiple components which might be marker differently and the versions you obtain via different APIs might also vary.
Perhaps straightforward way is to use SetupDiGetClassDevs(&GUID_DEVCLASS_DISPLAY, ... API and enumerate the items. This is going to give you the data close to what you see in device manager, with versions - DEVPKEY_Device_DriverVersion obtained with SetupDiGetDeviceRegistryProperty value specifically.
If you run this utility, if includes a section "Display Devices" with enumeration as mentioned, and it does mention driver version:
Display Devices
Intel(R) HD Graphics 4600
Instance: PCI\VEN_8086&DEV_0412&SUBSYS_85341043&REV_06\3&11583659&0&10
DEVPKEY_Device_Manufacturer: Intel Corporation
DEVPKEY_Device_DriverVersion: 20.19.15.4835 <<-----------------------
NVIDIA GeForce GTX 750
Instance: PCI\VEN_10DE&DEV_1381&SUBSYS_288B19DA&REV_A2\4&3834D97&0&0008
DEVPKEY_Device_Manufacturer: NVIDIA
DEVPKEY_Device_DriverVersion: 23.21.13.8813

Related

PCIe Not enough MMIO resources for SR-IOV

I'm trying to write a kernel module for an Intel FPGA design supporting PCIe SR-IOV and placed in the x16 PCIe slot of an IBase M991 Mainboard (Q170 PCH, VT-d activated in BIOS, Integrated graphics only mode enabled).
The CPU is an Intel Core i7-6700TE, which also supports virtualization.
Furthermore I'm using a Yocto - Morty Distribution (Linux Kernel 4.19) with the following Kconfigs enabled:
CONFIG_PCI_IOV=y
CONFIG_PCI_DEBUG=y
CONFIG_INTEL_IOMMU_SVM=y
CONFIG_PCI_REALLOC_ENABLE_AUTO=y
CONFIG_INTEL_IOMMU_DEFAULT_ON=y
CONFIG_IRQ_REMAP=y
CONFIG_IOMMU_DEFAULT_PASSTHROUGH=y
CONFIG_DYNAMIC_DEBUG=y
When doing all of this I see my driver loading (probe function gets called), but after calling pci_enable_sriov with the number of VF I want to activate I get the kernel message
not enough MMIO resources for SR-IOV
What am I doing wrong here? Is there an init function I need to call?
Many thanks for your help.
Edit: More information about the PCIe device:
1 PF, 8 VF
2 BARs (BAR0 and BAR2)
non prefetchable, 32 bit BARs
each BAR size is 4 kB (12bit)

PCI-ISA bridge communication from linux kernel driver

I have a custom linux kernel driver that communicates to an old ISA card (from an old single processor pc with a true ISA bus). I am trying to port this driver into a new system equipped with a PCI-ISA bridge.
The old driver was writing to I/O ISA ports with:
request_region(0x0280, 8, "foo"); //0x0280 is a jumper-configured address in ISA card hardware.
//Then lots of:
outw_p(val, 0x0280);
val = inw_p(0x0282);
... (ports in use 0x0280, 0x0282 and 0x0284)
I've tried the same code but the address mapping seems to not work anymore. Region request does not give errors, but I keep getting always 65535 from all inw_p reads (while in the old system with the same code the card was answering with meaningful data).
I can't find anywhere what to edit in this code to make it work with the bridge.
I've tried opening the bridge as a PCI device and getting its I/O port address with:
dev = pci_get_device(vid, id, NULL); //Called with hardcoded bridge ids from lspci
result = pci_enable_device(dev); //dev not null, no errors
result = pci_request_regions(dev, "foo"); //No errors
value = pci_resource_start(dev, bar); //value is always 0 with any bar value
Device is working as I can get its vendor id by using pci_read_config_word, but I get always 0 from any BAR value, and also lspci -vvvv gives me no address/region section:
04:08.0 ISA bridge: Integrated Technology Express, Inc. IT8888F/G PCI to ISA Bridge with SMB [Golden Gate] (rev 03)
Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
Status: Cap- 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0
Also, no configuration options seems to be available in BIOS for this bridge.
On the internet i found only a few infos about PCI - ISA bridges in general, so I'm asking: what is the procedure necessary to successfully communicate with an ISA card behind a PCI - ISA bridge from a custom linux kernel driver?
Ok, after a lot of trial and error I was able to find the problem. I will share my experience in hope for someone to find it useful (my pc has a HD620-H81 motherboard with IT8888F/G PCI-ISA bridge).
PCI-ISA bridge
My PCI-ISA bridge (and probably many others) is configured by default in "Subtractive Decoding" pci mode, which means "if no other pci device claim an address, then the bridge claims it".
You can check for subtractive decoding mode by running lspci -t and lscpi -vv, and you should see that every PCIe-PCI/PCI-PCI bridge in the tree leading to your PCI-ISA bridge is configured in subtractive decode mode (the ISA bridge itself won't appear as subtractive because from a PCI perspective the ISA bridge is just a PCI device and not a bridge).
That means that you don't have a BAR assigned to the bridge, nor you need to interact with the pci bridge device directly in any way. You can just access directly the absolute address you need and the bridge will take care of managing it correctly (a lot of information is already available on the internet for subtractive decoding anyway if you're interested in details).
To sum up: no action is needed regarding the bridge itself or pci devices, just make sure in the bios that no other device, such as serial or parallel ports, gets assigned the port/address range that you are interested into (if it does, either disable it or change its address).
I/O Ports
I found that my ISA card was located at a port which is 0x0800 ports above the one where it should be (and that was the main problem for me). I'm not sure about why, maybe my bridge adds a fixed offset (if you know the reason maybe comment it below!).
What I did to find out the correct address was to run a function that iterates all port addresses from 0x0100 (which skips the first zone where is better not to write random data into) to 0xFFFF and runs a card check routine at each single port address until it finds the correct answer I expected from the card.
int cardFound = 0;
int i;
for(i = 0x0100; i < 0xFFFF && !cardFound; i++)
{
if (request_region(i, SIZE) == NULL)
continue;
//Do card detection with ioport_map, iowrite16/ioread16, etc.
cardFound = do_custom_card_detection_procedure();
release_region(i, SIZE);
}
if (cardFound)
printk(KERN_INFO "Card found at port %d\n", i);
The downside of this approach is that you may do really bad things by randomly writing data at ports, so be careful and if you really want to try check /proc/ioport for bad ones before doing it (I messed up rendering for a quarter of the screen and I had to do a complete power reset by removing power cord and CMOS battery to bring it back to normal :D).

How does PCIe Endpoint device memory is mapped into the systems memory map (MMIO)?

How does Linux Kernel or BIOS map the PCIe endpoint device memory into systems MMIO space ? Is there any API to achieve it ?
Lets assume that when writing a Linux device driver for a PCIe endpoint device, How can we map PCIe device memory into MMIO space ? Or Is it true that the device is already mapped into MMIO by BIOS during enumeration and what I would need to do it just remap the device MMIO into the kernel virtual address space using ioremap() ?
Platform : Linux on x86
There are two parts to this answer
Role of the BIOS
The BIOS (typically UEFI based) will do some sort of Depth-First Search (DFS) and enumerate all the children as PCIe is a self-enumerating bus. Since it has the view of the world (device, buses, processors) it will write an address to the BAR registers (could be BAR0 and or multiple of them). This will be the address the system will use and it will actually route these requests from the Host Agent (HA on x86/Intel platforms) to the Root Port to a PCIe switch all the way to the end point.
Each of these elements track what address ranges belong to themselves or one of their child devices (example a Switch may be the child of a Root Port)
Role of the Device Driver
The OS/Kernel will provide a toolkit of helper routines that the driver authors will use to access the device registers. Typically a driver may follow the folling routines
This is some sample driver pseudo-code, just to help illustrate the idea
1. pci_resource_flags(pdev, 0) & IORESOURCE_MEM
Check if a resource region is valid, here check for BAR 0
2. pci_request_regions(pdev, "region")
Take ownership of the resource/region
3. drv->registers = pci_iomap(pdev, 0, SIZE_YOU_WANT_TO_MAP)
This will give you kernel virtual address to device register mapping
Note : In case the BIOS does not enumerate, through Linux one can rescan the PCIe tree to see if a device can be seen or not.

Programmatically determine NUMA node or PCI bus, device, function number of Direct3D9Ex device

I'm looking for a way to programmatically determine which NUMA node a particular Direct3D9Ex display adapter is connected to so that I can allocate memory for host to device transfers on that node.
I can use IDirect3D9Ex::GetAdapterIdentifier() to obtain a DeviceName of the form "\.\DISPLAY1".
I can use EnumDisplayDevices() to enumerate the display devices and match this DeviceName to a DeviceKey of the form "\Registry\Machine\System\CurrentControlSet\Control\Video{62E184AC-2614-4AA8-844C-47454B92C142}\0000".
I can use SetupDiEnumDeviceInfo() to enumerate devices and SetupDiGetDeviceProperty() to query properties including DEVPKEY_NAME (e.g. "NVIDIA GeForce GTX TITAN X"), DEVPKEY_Device_LocationInfo (e.g. "PCI bus 2, device 0, function 0") and DEVPKEY_Device_Numa_Node (e.g. 0) but I can't find anything to link one of these devices to either the DeviceName or DeviceKey in EnumDisplayDevices().
I can do equivalent thing is CUDA because cudaDeviceGetPCIBusId() provides the PCI bus, device and function information but I also need to do it in Direct3D9Ex (when CUDA is unavailable).
The other thing I've noticed is that in the registry under "HKLM\SYSTEM\CurrentControlSet\Enum\PCI\VEN_10DE&DEV_17C2&SUBSYS_113210DE&REV_A1" there is a key for each of my display devices and each one contains another key called "Device Parameters" with a value called "VideoID" of the form "{62E184AC-2614-4AA8-844C-47454B92C142}". Unfortunately applications are not supposed to read this part of the registry directly and I don't know how to obtain the same information through the SETUPAPI.

Linux PCI Device Driver - Bus v. Kernel IRQ

I am writing a device driver for a PCIe card in Linux. I am trying to use interrupts in my driver.
Reading the "IRQ Line" section of the PCI configuration register (offset 0x3C) reports that the assigned IRQ line for the device is 11. lspci -b -vv also reports that my device's interrupt number is 11.
Heres where it gets weird... cat /sys/bus/pci/devices/<my_device>/irq reports that the interrupt number is 19. lspci -vv also reports that the interrupt number is 19.
Requesting 11 in my driver does not work. If I request 19 in the driver, I catch interrupts just fine.
What gives?
Thanks!!!
I believe that it has to do with the difference between "physical" and "virtual" IRQ lines. Because the processor has a limited number of physical IRQ lines it assigns virtual IRQ lines to allow the total number of PCI devices to exceed the number of physical lines.
In this instance, 19 is your virtual IRQ line (as recognized by the processor) while 11 is the physical line (as recognized by the PCI device).
By the way, you should probably really get the IRQ number from the struct pci_dev for that device since they're dynamically generated.
Sean's answer is easy to understand. However here I would try to make it more complete.
CPU's IRQ pin, almost always, isn't connected directly to a peripheral device, but via an programmable interrupt controller(PIC, e.g. Intel 8259A). This helps handling large device fan-out and also heterogeneous interrupt format (pin based v.s. message based as in PCIe).
If you run a recent version of lspci, it would print information like
Interrupt: pin A routed to IRQ 26
Here, pin A as 11 in OP, is the physical pin. This is something saved by the PCI device and used by the hardware to exchange between interrupts controller. From LDP:
The PCI set up code writes the pin number of the interrupt controller
into the PCI configuration header for each device. It determines the
interrupt pin (or IRQ) number using its knowledge of the PCI interrupt
routing topology together with the devices PCI slot number and which
PCI interrupt pin that it is using. The interrupt pin that a device
uses is fixed and is kept in a field in the PCI configuration header
for this device. It writes this information into the interrupt line
field that is reserved for this purpose. When the device driver runs,
it reads this information and uses it to request control of the
interrupt from the Linux kernel.
IRQ 26 as 19 in OP is something that kernel code and CPU deal with. According to Linux Documentation/IRQ.txt:
An IRQ number is a kernel identifier used to talk about a hardware
interrupt source. Typically this is an index into the global irq_desc
array, but except for what linux/interrupt.h implements the details
are architecture specific.
So the PCI first receives interrupts from device, translate interrupt source to a IRQ number and informs the CPU. CPU use IRQ number to look into Interrupt Descriptor Table(IDT) and find the correct software handler.
Ref:
http://www.tldp.org/LDP/tlk/dd/interrupts.html
http://www.brokenthorn.com/Resources/OSDevPic.html

Resources