WINAPI determine if specific SCSI device is present - c

I'm currently trying to detect if a machine has a specific NVME SSD installed. On my machine, the file name is: SCSI#Disk&Ven_NVMe&Prod_XPG_SPECTRIX_S40#5&1363da6c&0&000000#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
What would be a reliable way to detect this universally on any machine? I don't know if any of these fields are unique to each machine, and I'm not very familiar with Windows' API.

when disk type hardware is detected, kernel create device for it (really 2 device usually PDO and FDO attached to it) and register well known interface GUID_DEVINTERFACE_DISK (by call IoRegisterDeviceInterface ). as result Symbolic Link will be created under \GLOBAL?? directory. we can got all registered disk interfaces via CM_Get_Device_Interface_ListW.
SCSI#Disk&Ven_NVMe&Prod_XPG_SPECTRIX_S40# obvious unique and will be the same anywhere for this device.
so code can be next
CONFIGRET Is_XPG_SPECTRIX_S40_Present(PBOOL pb)
{
CONFIGRET cr;
ULONG cb = 0, rcb;
union {
PVOID buf;
PZZWSTR Buffer;
};
static volatile UCHAR guz = 0;
PVOID stack = alloca(guz);
do
{
cr = CM_Get_Device_Interface_List_SizeW(&rcb, const_cast<GUID*>(&GUID_DEVINTERFACE_DISK), 0, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
if (cr != CR_SUCCESS)
{
break;
}
if (cb < (rcb *= sizeof(WCHAR)))
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);// /RTCs must be off
}
cr = CM_Get_Device_Interface_ListW(const_cast<GUID*>(&GUID_DEVINTERFACE_DISK),
0, Buffer, cb, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
} while (cr == CR_BUFFER_SMALL);
if (cr == CR_SUCCESS)
{
while (*Buffer)
{
if (wcsstr(Buffer, L"\\SCSI#Disk&Ven_NVMe&Prod_XPG_SPECTRIX_S40#"))
{
*pb = TRUE;
break;
}
Buffer += wcslen(Buffer) + 1;
}
}
return cr;
}

Related

How to read the Master File Table (MFT) using C? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I want to read the MFT of my local system using C.
I search for solution all over the internet but nothing found.
I hope someone has a tutorial for me or a good explanation with example of code about how to do that.
Thanks in advance.
first of all we need open volume handle we FILE_READ_DATA.
then we need query NTFS_VOLUME_DATA_BUFFER with FSCTL_GET_NTFS_VOLUME_DATA control code
from here we got size of single MFT record - BytesPerFileRecordSegment, total size of MFT - MftValidDataLength. so maximum record count is (MftValidDataLength.QuadPart / BytesPerFileRecordSegment)
correct way (synchronized with ntfs) will be read single record via FSCTL_GET_NTFS_FILE_RECORD.
if want read multiple records at once - of course possible read direct from volume. we have the start LCN for MFT - MftStartLcn. but MFT can have several not continuos fragments. so we need use FSCTL_GET_RETRIEVAL_POINTERS if want got all fragments locations. for convert LCN to volume offset need multiple it to BytesPerCluster
demo code:
void ReadMft(PCWSTR szVolume)
{
HANDLE hVolume = CreateFileW(szVolume, FILE_READ_DATA, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, FILE_OPEN_FOR_BACKUP_INTENT, 0);
if (hVolume != INVALID_HANDLE_VALUE)
{
NTFS_VOLUME_DATA_BUFFER nvdb;
OVERLAPPED ov = {};
if (DeviceIoControl(hVolume, FSCTL_GET_NTFS_VOLUME_DATA, 0, 0, &nvdb, sizeof(nvdb), 0, &ov))
{
NTFS_FILE_RECORD_INPUT_BUFFER nfrib;
nfrib.FileReferenceNumber.QuadPart = nvdb.MftValidDataLength.QuadPart / nvdb.BytesPerFileRecordSegment - 1;
ULONG cb = __builtin_offsetof(NTFS_FILE_RECORD_OUTPUT_BUFFER, FileRecordBuffer[nvdb.BytesPerFileRecordSegment]);
PNTFS_FILE_RECORD_OUTPUT_BUFFER pnfrob = (PNTFS_FILE_RECORD_OUTPUT_BUFFER)alloca(cb);
do
{
if (!DeviceIoControl(hVolume, FSCTL_GET_NTFS_FILE_RECORD,
&nfrib, sizeof(nfrib), pnfrob, cb, 0, &ov))
{
break;
}
// pnfrob->FileRecordBuffer :
// here pnfrob->FileReferenceNumber FileRecord
} while (0 <= (nfrib.FileReferenceNumber.QuadPart = pnfrob->FileReferenceNumber.QuadPart - 1));
ReadMft2(szVolume, hVolume, &nvdb);
}
CloseHandle(hVolume);
}
}
void ReadMft2(PCWSTR szVolume, HANDLE hVolume, PNTFS_VOLUME_DATA_BUFFER nvdb)
{
static PCWSTR MFT = L"\\$MFT";
static STARTING_VCN_INPUT_BUFFER vcn {};
static volatile UCHAR guz;
PVOID stack = alloca(guz);
union {
PVOID buf;
PWSTR lpFileName;
PRETRIEVAL_POINTERS_BUFFER rpb;
};
buf = alloca(wcslen(szVolume) * sizeof(WCHAR) + sizeof(MFT));
wcscat(wcscpy(lpFileName, szVolume), MFT);
HANDLE hFile = CreateFileW(lpFileName, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, FILE_OPEN_FOR_BACKUP_INTENT, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
OVERLAPPED ov{};
ULONG cb = RtlPointerToOffset(buf, stack), rcb, ExtentCount = 2;
do
{
rcb = __builtin_offsetof(RETRIEVAL_POINTERS_BUFFER, Extents[ExtentCount]);
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
if (DeviceIoControl(hFile, FSCTL_GET_RETRIEVAL_POINTERS, &vcn, sizeof(vcn), buf, cb, 0, &ov))
{
if (rpb->Extents->Lcn.QuadPart != nvdb->MftStartLcn.QuadPart)
{
__debugbreak();
}
if (ExtentCount = rpb->ExtentCount)
{
auto Extents = rpb->Extents;
ULONG BytesPerCluster = nvdb->BytesPerCluster;
ULONG BytesPerFileRecordSegment = nvdb->BytesPerFileRecordSegment;
LONGLONG StartingVcn = rpb->StartingVcn.QuadPart, NextVcn, len;
PVOID FileRecordBuffer = alloca(BytesPerFileRecordSegment);
do
{
NextVcn = Extents->NextVcn.QuadPart;
len = NextVcn - StartingVcn, StartingVcn = NextVcn;
DbgPrint("%I64x %I64x\n", Extents->Lcn.QuadPart, len);
if (Extents->Lcn.QuadPart != -1)
{
Extents->Lcn.QuadPart *= BytesPerCluster;
ov.Offset = Extents->Lcn.LowPart;
ov.OffsetHigh = Extents->Lcn.HighPart;
// read 1 record
ReadFile(hVolume, FileRecordBuffer, BytesPerFileRecordSegment, 0, &ov);
}
} while (Extents++, --ExtentCount);
}
break;
}
ExtentCount <<= 1;
} while (GetLastError() == ERROR_MORE_DATA);
CloseHandle(hFile);
}
}
The cluster number of the start of the MFT is at offset 0x30 of the NTFS boot sector as described here.
GetDiskFreeSpace will return you the cluster size, and to open a volume for direct access you can do (for example) CreateFile ("\\.\C:", ...) as described here under 'Physical Disks and Volumes'.

How to get module image name from an arbitrary address in Windows kernel space?

I'm trying to see how I can get a loaded module image name from an arbitrary address from the kernel code.
In user mode I would do this:
void* pAddr;
VOID* pBase;
WCHAR buff[MAX_PATH] = {0};
//Get address of some function in some module (just to test it)
pAddr = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "GetCurrentProcess");
//Get module base address
RtlPcToFileHeader(pAddr, &pBase);
//Get module image file name
GetModuleFileNameEx(GetCurrentProcess(), (HMODULE)pBase, buff, SIZEOF(buff));
Is there a way to do the same in kernel mode if I have pAddr that can point to some address in kernel or user space?
EDIT: While waiting for the answer I came up with my own code (using undocumented way of traversing PEB):
#ifdef CALLING_FROM_KERNEL_MODE
//Kernel mode
TEB* pTEB = (TEB*)PsGetCurrentThreadTeb();
#else
//User mode
#if defined(_M_X64)
//64-bit
TEB* pTEB = reinterpret_cast<TEB*>(__readgsqword(reinterpret_cast<DWORD_PTR>(&static_cast<NT_TIB*>(nullptr)->Self)));
#else
//32-bit
TEB* pTEB = reinterpret_cast<TEB*>(__readfsdword(reinterpret_cast<DWORD_PTR>(&static_cast<NT_TIB*>(nullptr)->Self)));
#endif
#endif
PEB* p_PEB = pTEB->ProcessEnvironmentBlock;
PEB_LDR_DATA* pPLD = p_PEB->Ldr;
const WCHAR* pModName = NULL;
LIST_ENTRY* pLE = &pPLD->InMemoryOrderModuleList;
LIST_ENTRY* pLE_Head = pLE;
while(pLE_Head != pLE->Flink)
{
PLDR_DATA_TABLE_ENTRY pLDTE = CONTAINING_RECORD(pLE, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
size_t szcbSizeOfImg = (size_t)pLDTE->Reserved3[1];
if((size_t)pAddr - (size_t)pLDTE->DllBase < szcbSizeOfImg)
{
pModName = pLDTE->FullDllName.Buffer;
break;
}
pLE = pLE->Flink;
}
The problem is that although it works from a user-mode, from a kernel mode PsGetCurrentThreadTeb() seems to return NULL. Does this mean kernel threads do not have a TEB?
this can be done by creating list of all loaded modules via ZwQuerySystemInformation with SystemModuleInformation
void fgt(PVOID *Callers, ULONG Count)
{
NTSTATUS status;
ULONG cb = 0x10000;
do
{
status = STATUS_INSUFFICIENT_RESOURCES;
if (PRTL_PROCESS_MODULES prpm = (PRTL_PROCESS_MODULES)ExAllocatePool(PagedPool, cb))
{
if (0 <= (status = NtQuerySystemInformation(SystemModuleInformation, prpm, cb, &cb)))
{
do
{
PVOID Caller = *Callers++;
if (ULONG NumberOfModules = prpm->NumberOfModules)
{
PRTL_PROCESS_MODULE_INFORMATION Modules = prpm->Modules;
do
{
if ((SIZE_T)Caller - (SIZE_T)Modules->ImageBase < Modules->ImageSize)
{
DbgPrint("%p> %s\n", Caller, Modules->FullPathName);
break;
}
} while (Modules++, --NumberOfModules);
}
} while (--Count);
}
ExFreePool(prpm);
}
} while (status == STATUS_INFO_LENGTH_MISMATCH);
}

How to disable WOW64 file system redirection for GetModuleFileNameEx?

I'm running the following from a 32-bit process on a 64-bit Windows 10:
#ifndef _DEBUG
WCHAR buffPath[MAX_PATH] = {0};
FARPROC pfn = (FARPROC)::GetModuleHandleEx;
HMODULE hMod = NULL;
::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCTSTR)pfn, &hMod);
PVOID pOldVal = NULL;
if(::Wow64DisableWow64FsRedirection(&pOldVal))
{
::GetModuleFileNameEx(::GetCurrentProcess(), hMod, buffPath, _countof(buffPath));
::Wow64RevertWow64FsRedirection(pOldVal);
wprintf(L"Path=%s\n", buffPath);
}
#else
#error run_in_release_mode
#endif
and I'm expecting to receive the path as c:\windows\syswow64\KERNEL32.DLL, but it gives me:
Path=C:\Windows\System32\KERNEL32.DLL
Any idea why?
when we load dll via LoadLibrary[Ex] or LdrLoadDll - first some pre-process transmitted dll name (say convert api-* to actual dll name or redirect dll name based on manifest - well known example comctl32.dll) and then use this (possible modified) dll name to load file as dll. but wow fs redirection - not preprocessed at this stage. if dll was successfully loaded - system allocate LDR_DATA_TABLE_ENTRY structure and save transmitted (after pre-process) dll name here as is.
the GetModuleFileNameEx simply walk throughout LDR_DATA_TABLE_ENTRY double linked list and search entry where DllBase == hModule - if found - copy FullDllName to lpFilename (if buffer big enough). so it simply return dll path used during load dll. the Wow64DisableWow64FsRedirection have no any effect to this call.
if we want get real (canonical) full path to dll - need use GetMappedFileNameW or ZwQueryVirtualMemory with MemoryMappedFilenameInformation
so code can be (if we hope that MAX_PATH is enough)
WCHAR path[MAX_PATH];
GetMappedFileNameW(NtCurrentProcess(), hmod, path, RTL_NUMBER_OF(path));
or if use ntapi and correct handle any path length:
NTSTATUS GetDllName(PVOID AddressInDll, PUNICODE_STRING NtImageName)
{
NTSTATUS status;
union {
PVOID buf;
PUNICODE_STRING ImageName;
};
static volatile UCHAR guz;
PVOID stack = alloca(guz);
SIZE_T cb = 0, rcb = 0x200;
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
if (0 <= (status = ZwQueryVirtualMemory(NtCurrentProcess(), AddressInDll,
MemoryMappedFilenameInformation, buf, cb, &rcb)))
{
return RtlDuplicateUnicodeString(
RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
ImageName, NtImageName);
}
} while (status == STATUS_BUFFER_OVERFLOW);
return status;
}
UNICODE_STRING NtImageName;
if (0 <= GetDllName(hmod, &NtImageName))
{
RtlFreeUnicodeString(&NtImageName);
}
about question "way to convert it to the win32 form" - there is a counter question - for what ?
at first we can use it as is with NtOpenFile (well documented api), at second - the simplest way convert to win32 form, accepted by CreateFileW - add \\?\globalroot prefix to nt path. but not all win32 api (primary shell api) accept this form. if we want exactly dos-device form path (aka X:) need use IOCTL_MOUNTMGR_QUERY_POINTS - got the array of MOUNTMGR_MOUNT_POINT inside MOUNTMGR_MOUNT_POINTS structure and search for DeviceName which is prefix for our nt path and SymbolicLinkName have driver letter form. code can be ~
#include <mountmgr.h>
ULONG NtToDosPath(HANDLE hMM, PUNICODE_STRING ImageName, PWSTR* ppsz)
{
static MOUNTMGR_MOUNT_POINT MountPoint;
static volatile UCHAR guz;
PVOID stack = alloca(guz);
PMOUNTMGR_MOUNT_POINTS pmmp = 0;
DWORD cb = 0, rcb = 0x200, BytesReturned;
ULONG err = NOERROR;
do
{
if (cb < rcb) cb = RtlPointerToOffset(pmmp = (PMOUNTMGR_MOUNT_POINTS)alloca(rcb - cb), stack);
if (DeviceIoControl(hMM, IOCTL_MOUNTMGR_QUERY_POINTS,
&MountPoint, sizeof(MOUNTMGR_MOUNT_POINT),
pmmp, cb, &BytesReturned, 0))
{
if (ULONG NumberOfMountPoints = pmmp->NumberOfMountPoints)
{
PMOUNTMGR_MOUNT_POINT MountPoints = pmmp->MountPoints;
do
{
UNICODE_STRING SymbolicLinkName = {
MountPoints->SymbolicLinkNameLength,
SymbolicLinkName.Length,
(PWSTR)RtlOffsetToPointer(pmmp, MountPoints->SymbolicLinkNameOffset)
};
UNICODE_STRING DeviceName = {
MountPoints->DeviceNameLength,
DeviceName.Length,
(PWSTR)RtlOffsetToPointer(pmmp, MountPoints->DeviceNameOffset)
};
PWSTR FsPath;
if (RtlPrefixUnicodeString(&DeviceName, ImageName, TRUE) &&
DeviceName.Length < ImageName->Length &&
*(FsPath = (PWSTR)RtlOffsetToPointer(ImageName->Buffer, DeviceName.Length)) == '\\' &&
MOUNTMGR_IS_DRIVE_LETTER(&SymbolicLinkName))
{
cb = ImageName->Length - DeviceName.Length;
if (PWSTR psz = new WCHAR[3 + cb/sizeof(WCHAR)])
{
*ppsz = psz;
psz[0] = SymbolicLinkName.Buffer[12];
psz[1] = ':';
memcpy(psz + 2, FsPath, cb + sizeof(WCHAR));
return NOERROR;
}
return ERROR_NO_SYSTEM_RESOURCES;
}
} while (MountPoints++, --NumberOfMountPoints);
}
return ERROR_NOT_FOUND;
}
rcb = pmmp->Size;
} while ((err = GetLastError()) == ERROR_MORE_DATA);
return err;
}
ULONG NtToDosPath(PWSTR lpFilename, PWSTR* ppsz)
{
HANDLE hMM = CreateFile(MOUNTMGR_DOS_DEVICE_NAME, FILE_GENERIC_READ,
FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hMM == INVALID_HANDLE_VALUE)
{
return GetLastError();
}
UNICODE_STRING us;
RtlInitUnicodeString(&us, lpFilename);
ULONG err = NtToDosPath(hMM, &us, ppsz);
CloseHandle(hMM);
return err;
}
PWSTR psz;
if (NtToDosPath(path, &psz) == NOERROR)
{
DbgPrint("%S\n", psz);
delete [] psz;
}
also we can change in code to MOUNTMGR_IS_VOLUME_NAME(&SymbolicLinkName) for get volume (persistent) name form instead driver letter form

Confusion regarding #interrupt-cells configuration on PCA9555 expander

I'm trying to setup a device tree source file for the first time on my custom platform. On the board is a NXP PCA9555 gpio expander. I'm attempting to setup node for the device and am a bit confused.
Here is where I'm at with the node in the dts file:
ioexp0: gpio-exp#21 {
compatible = "nxp,pca9555";
reg = <21>;
interrupt-parent = <&gpio>;
interrupts = <8 0>;
gpio-controller;
#gpio-cells = <2>;
/*I don't understand the following two lines*/
interrupt-controller;
#interrupt-cells = <2>;
};
I got to this point by using the armada-388-gp.dts source as a guide.
My confusion is on what code processes the #interrupt-cells property. The bindings documentation is not very helpful at all for this chip as it doesn't say anything regarding interrupt cell interpretation.
Looking at the pca953x_irq_setup function in the source code for the pca9555 driver - I don't see anywhere that the #interrupt-cells property is handled. Is this handled in the linux interrupt handling code? I'm just confused as to how I'm suppose to know the meaning of the two interrupt cells.
pca953x_irq_setup for your convenience:
static int pca953x_irq_setup(struct pca953x_chip *chip,
int irq_base)
{
struct i2c_client *client = chip->client;
int ret, i;
if (client->irq && irq_base != -1
&& (chip->driver_data & PCA_INT)) {
ret = pca953x_read_regs(chip,
chip->regs->input, chip->irq_stat);
if (ret)
return ret;
/*
* There is no way to know which GPIO line generated the
* interrupt. We have to rely on the previous read for
* this purpose.
*/
for (i = 0; i < NBANK(chip); i++)
chip->irq_stat[i] &= chip->reg_direction[i];
mutex_init(&chip->irq_lock);
ret = devm_request_threaded_irq(&client->dev,
client->irq,
NULL,
pca953x_irq_handler,
IRQF_TRIGGER_LOW | IRQF_ONESHOT |
IRQF_SHARED,
dev_name(&client->dev), chip);
if (ret) {
dev_err(&client->dev, "failed to request irq %d\n",
client->irq);
return ret;
}
ret = gpiochip_irqchip_add_nested(&chip->gpio_chip,
&pca953x_irq_chip,
irq_base,
handle_simple_irq,
IRQ_TYPE_NONE);
if (ret) {
dev_err(&client->dev,
"could not connect irqchip to gpiochip\n");
return ret;
}
gpiochip_set_nested_irqchip(&chip->gpio_chip,
&pca953x_irq_chip,
client->irq);
}
return 0;
}
This is my first time working with device tree so I'm hoping it's something obvious that I'm just missing.
After looking at all of the comments I did some additional reading and figured out my answer.
I now understand that I was misinterpreting some properties of the device tree. I was previously under the impression that the driver had to specify how all properties were handled. I now see that linux will actually handle many of the generic properties such as gpios or interrupts (which makes a lot of sense).
The documentation on the actual interrupts binding was very helpful, not the documentation for the device driver.
Here is a bit more of a detailed explanation of how the translation from intspec to IRQ_TYPE* happens:
The function of_irq_parse_one copies the interrupt specifier integers to a struct of_phandle_args here. This arg is then passed to irq_create_of_mapping via a consumer function (e.g. of_irq_get). This function then maps these args to a struct irq_fwspec via of_phandle_args_to_fwspec and passes it's fwspec data to irq_create_fwspec_mapping. These functions are all found in irqdomain.c. At this point the irq will belong to an irq_domain or use the irq_default_domain. As far I can tell - the pca853x driver uses the default domain. This domain is often setup by platform specific code. I found mine by searching for irq_domain_ops on cross reference. A lot of these seem to do simple copying of intspec[1] & IRQ_TYPE_SENSE_MASK to the type variable in irq_create_fwspec_mapping via irq_domain_translate. From here the type is set to the irq's irq_data via irqd_set_trigger_type.
of_irq_parse_one:
/**
* of_irq_parse_one - Resolve an interrupt for a device
* #device: the device whose interrupt is to be resolved
* #index: index of the interrupt to resolve
* #out_irq: structure of_irq filled by this function
*
* This function resolves an interrupt for a node by walking the interrupt tree,
* finding which interrupt controller node it is attached to, and returning the
* interrupt specifier that can be used to retrieve a Linux IRQ number.
*/
int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_args *out_irq)
{
struct device_node *p;
const __be32 *intspec, *tmp, *addr;
u32 intsize, intlen;
int i, res;
pr_debug("of_irq_parse_one: dev=%s, index=%d\n", of_node_full_name(device), index);
/* OldWorld mac stuff is "special", handle out of line */
if (of_irq_workarounds & OF_IMAP_OLDWORLD_MAC)
return of_irq_parse_oldworld(device, index, out_irq);
/* Get the reg property (if any) */
addr = of_get_property(device, "reg", NULL);
/* Try the new-style interrupts-extended first */
res = of_parse_phandle_with_args(device, "interrupts-extended",
"#interrupt-cells", index, out_irq);
if (!res)
return of_irq_parse_raw(addr, out_irq);
/* Get the interrupts property */
intspec = of_get_property(device, "interrupts", &intlen);
if (intspec == NULL)
return -EINVAL;
intlen /= sizeof(*intspec);
pr_debug(" intspec=%d intlen=%d\n", be32_to_cpup(intspec), intlen);
/* Look for the interrupt parent. */
p = of_irq_find_parent(device);
if (p == NULL)
return -EINVAL;
/* Get size of interrupt specifier */
tmp = of_get_property(p, "#interrupt-cells", NULL);
if (tmp == NULL) {
res = -EINVAL;
goto out;
}
intsize = be32_to_cpu(*tmp);
pr_debug(" intsize=%d intlen=%d\n", intsize, intlen);
/* Check index */
if ((index + 1) * intsize > intlen) {
res = -EINVAL;
goto out;
}
/* Copy intspec into irq structure */
intspec += index * intsize;
out_irq->np = p;
out_irq->args_count = intsize;
for (i = 0; i < intsize; i++)
out_irq->args[i] = be32_to_cpup(intspec++);
/* Check if there are any interrupt-map translations to process */
res = of_irq_parse_raw(addr, out_irq);
out:
of_node_put(p);
return res;
}
EXPORT_SYMBOL_GPL(of_irq_parse_one)
irq_create_fwspec_mapping:
unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
{
struct irq_domain *domain;
struct irq_data *irq_data;
irq_hw_number_t hwirq;
unsigned int type = IRQ_TYPE_NONE;
int virq;
if (fwspec->fwnode) {
domain = irq_find_matching_fwspec(fwspec, DOMAIN_BUS_WIRED);
if (!domain)
domain = irq_find_matching_fwspec(fwspec, DOMAIN_BUS_ANY);
} else {
domain = irq_default_domain;
}
if (!domain) {
pr_warn("no irq domain found for %s !\n",
of_node_full_name(to_of_node(fwspec->fwnode)));
return 0;
}
if (irq_domain_translate(domain, fwspec, &hwirq, &type))
return 0;
/*
* WARN if the irqchip returns a type with bits
* outside the sense mask set and clear these bits.
*/
if (WARN_ON(type & ~IRQ_TYPE_SENSE_MASK))
type &= IRQ_TYPE_SENSE_MASK;
/*
* If we've already configured this interrupt,
* don't do it again, or hell will break loose.
*/
virq = irq_find_mapping(domain, hwirq);
if (virq) {
/*
* If the trigger type is not specified or matches the
* current trigger type then we are done so return the
* interrupt number.
*/
if (type == IRQ_TYPE_NONE || type == irq_get_trigger_type(virq))
return virq;
/*
* If the trigger type has not been set yet, then set
* it now and return the interrupt number.
*/
if (irq_get_trigger_type(virq) == IRQ_TYPE_NONE) {
irq_data = irq_get_irq_data(virq);
if (!irq_data)
return 0;
irqd_set_trigger_type(irq_data, type);
return virq;
}
pr_warn("type mismatch, failed to map hwirq-%lu for %s!\n",
hwirq, of_node_full_name(to_of_node(fwspec->fwnode)));
return 0;
}
if (irq_domain_is_hierarchy(domain)) {
virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, fwspec);
if (virq <= 0)
return 0;
} else {
/* Create mapping */
virq = irq_create_mapping(domain, hwirq);
if (!virq)
return virq;
}
irq_data = irq_get_irq_data(virq);
if (!irq_data) {
if (irq_domain_is_hierarchy(domain))
irq_domain_free_irqs(virq, 1);
else
irq_dispose_mapping(virq);
return 0;
}
/* Store trigger type */
irqd_set_trigger_type(irq_data, type);
return virq;
}
EXPORT_SYMBOL_GPL(irq_create_fwspec_mapping);

How to obtain the correct physical size of the monitor?

How can I get the size of the display in centimeters or inches?
This code does not always works correctly:
HDC hdc = CreateDC(_T("DISPLAY"),dd.DeviceName,NULL,NULL);
int width = GetDeviceCaps(hdc, HORZSIZE);
int height = GetDeviceCaps(hdc, VERTSIZE);
ReleaseDC(0, hdc)
Especially for multi-monitor configuration.
Update: I need to get the size just for ordinary monitors, which have a constant physical size.
I found another way. The physical size of the monitor are stored in the EDID, and Windows are almost always copies of its value in the registry. If you can parse EDID, you can read the width and height of the monitor in centimeters.
Update: Added code
BOOL GetMonitorDevice( TCHAR* adapterName, DISPLAY_DEVICE &ddMon )
{
DWORD devMon = 0;
while (EnumDisplayDevices(adapterName, devMon, &ddMon, 0))
{
if (ddMon.StateFlags & DISPLAY_DEVICE_ACTIVE &&
ddMon.StateFlags & DISPLAY_DEVICE_ATTACHED) // for ATI, Windows XP
break;
devMon++;
}
if (ddMon.DeviceString[0] == '\0')
{
EnumDisplayDevices(adapterName, 0, &ddMon, 0);
if (ddMon.DeviceString[0] == '\0')
_tcscpy_s(ddMon.DeviceString, _T("Default Monitor"));
}
return ddMon.DeviceID[0] != '\0';
}
BOOL GetMonitorSizeFromEDID(TCHAR* adapterName, DWORD& Width, DWORD& Height)
{
DISPLAY_DEVICE ddMon;
ZeroMemory(&ddMon, sizeof(ddMon));
ddMon.cb = sizeof(ddMon);
//read edid
bool result = false;
Width = 0;
Height = 0;
if (GetMonitorDevice(adapterName, ddMon))
{
TCHAR model[8];
TCHAR* s = _tcschr(ddMon.DeviceID, '\\') + 1;
size_t len = _tcschr(s, '\\') - s;
if (len >= _countof(model))
len = _countof(model) - 1;
_tcsncpy_s(model, s, len);
TCHAR *path = _tcschr(ddMon.DeviceID, '\\') + 1;
TCHAR str[MAX_PATH] = _T("SYSTEM\\CurrentControlSet\\Enum\\DISPLAY\\");
_tcsncat_s(str, path, _tcschr(path, '\\')-path);
path = _tcschr(path, '\\') + 1;
HKEY hKey;
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, str, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
DWORD i = 0;
DWORD size = MAX_PATH;
FILETIME ft;
while(RegEnumKeyEx(hKey, i, str, &size, NULL, NULL, NULL, &ft) == ERROR_SUCCESS)
{
HKEY hKey2;
if(RegOpenKeyEx(hKey, str, 0, KEY_READ, &hKey2) == ERROR_SUCCESS)
{
size = MAX_PATH;
if(RegQueryValueEx(hKey2, _T("Driver"), NULL, NULL, (LPBYTE)&str, &size) == ERROR_SUCCESS)
{
if (_tcscmp(str, path) == 0)
{
HKEY hKey3;
if(RegOpenKeyEx(hKey2, _T("Device Parameters"), 0, KEY_READ, &hKey3) == ERROR_SUCCESS)
{
BYTE EDID[256];
size = 256;
if(RegQueryValueEx(hKey3, _T("EDID"), NULL, NULL, (LPBYTE)&EDID, &size) == ERROR_SUCCESS)
{
DWORD p = 8;
TCHAR model2[9];
char byte1 = EDID[p];
char byte2 = EDID[p+1];
model2[0]=((byte1 & 0x7C) >> 2) + 64;
model2[1]=((byte1 & 3) << 3) + ((byte2 & 0xE0) >> 5) + 64;
model2[2]=(byte2 & 0x1F) + 64;
_stprintf(model2 + 3, _T("%X%X%X%X"), (EDID[p+3] & 0xf0) >> 4, EDID[p+3] & 0xf, (EDID[p+2] & 0xf0) >> 4, EDID[p+2] & 0x0f);
if (_tcscmp(model, model2) == 0)
{
Width = EDID[22];
Height = EDID[21];
result = true;
}
else
{
// EDID incorrect
}
}
RegCloseKey(hKey3);
}
}
}
RegCloseKey(hKey2);
}
i++;
}
RegCloseKey(hKey);
}
}
return result;
}
Navigating the registry directly is not only unsupported, but actually fails for devices different than yours. (e.g., the one on which I tested your code).
Unlike what some here say, there is an official way of accessing the EDID key path: by use of the Setup API, and specifically SetupDiOpenDevRegKey.
There's some tedious setup involved - Sample code is here.
EDIT: multiple monitors are handled here.
It is not possible to determine the exact physical size of a video device on windows as this depends on quite a lot of variables (e.g. active monitor profile, horizontal/vertical resolution, pixel size, etc.), some of which are not under the control of the computer.
Think for example of projector devices, where the physical size depends on the distance to the projection area which cannot be determined programmatically, as the video projector could be moved manually anytime.
You can't get the real exact size - you can get an approximation that depends on the DPI setting in windows, and the resolution of the screen, but you can't guarantee that this is the real size.
Especially in a multimonitor situation with different displays (say a 19" CRT and 24" LCD). Further, if the display is CRT then the measurement is the tube measurement, and not the display area.
When programs have needed this information exactly in the past, they've shown a gauge onscreen, and had the user hold a piece of paper up to the screen and measure the paper width with the gauge. Given the paper is 8.5" or A4 then you know the width, and you can use the number that they input to figure out the real DPI for a given display. You may need to have them do that for each monitor in a multimonitor setup.
-Adam
Windows Vista and upper support new function GetMonitorDisplayAreaSize()
http://msdn.microsoft.com/en-us/library/ms775210%28VS.85%29.aspx
Update: It doesn't work properly
You can request LOGPIXELSX from GetDeviceCaps to get the DPI for the display, though it will generally return 96. See also this MSDN article on writing DPI-aware apps.
You can obtain EDID from the registry.

Resources