Writing 64-Bit Audio Drivers

If you are writing a 64-bit driver or writing a driver that can be compiled to run on both 32- and 64-bit systems, follow the porting guidelines in Driver Programming Techniques. Some of the pitfalls that you might encounter in writing a 64-bit audio driver are described below.

First and foremost, a potential problem to look for in existing 32-bit driver code is conversion between pointer types and integer types such as DWORD or ULONG. Programmers with experience writing code for 32-bit machines might be used to assuming that a pointer value fits into a DWORD or ULONG. For 64-bit code, this assumption is dangerous. Casting a pointer to type DWORD or ULONG can cause a 64-bit pointer to be truncated. A better approach is to cast the pointer to type DWORD_PTR or ULONG_PTR. An unsigned integer of type DWORD_PTR or ULONG_PTR is always large enough to store the entire pointer, regardless of whether the code is compiled for a 32- or 64-bit machine.

For example, the IRP pointer field IoStatus.Information is of type ULONG_PTR. The following code shows what not to do when copying a 64-bit pointer value to this field:

    PDEVICE_RELATIONS pDeviceRelations;
    Irp->IoStatus.Information = (ULONG)pDeviceRelations;  // wrong

This code sample erroneously casts the pDeviceRelations pointer to type ULONG, which can truncate the pointer value if sizeof(pDeviceRelations) > sizeof(ULONG). The correct approach is to cast the pointer to ULONG_PTR, as shown in the following:

    PDEVICE_RELATIONS pDeviceRelations;
    Irp->IoStatus.Information = (ULONG_PTR)pDeviceRelations;  // correct

This preserves all 64 bits of the pointer value.

A resource list stores the physical address of a resource in a structure of type PHYSICAL_ADDRESS (see IResourceList). To avoid truncating a 64-bit address, you should access the structure's QuadPart member rather than its LowPart member when copying an address into the structure or reading an address from the structure. For example, the FindTranslatedPort macro returns a pointer to a CM_PARTIAL_RESOURCE_DESCRIPTOR structure that contains the base address of an I/O port. The u.Port.Start member of this structure is a PHYSICAL_ADDRESS pointer to the base address. The following code shows what not to do:

    PUSHORT pBase = (PUSHORT)FindTranslatedPort(0)->u.Port.Start.LowPart;  // wrong

Again, this can truncate the pointer. Instead, you should access the QuadPart of this member, as shown in the following:

    PUSHORT pBase = (PUSHORT)FindTranslatedPort(0)->u.Port.Start.QuadPart;  // correct

This copies the entire 64-bit pointer.

Inline Win64 functions such as PtrToUlong and UlongToPtr safely convert between pointer and integer types without relying on assumptions about the relative sizes of these types. If one type is shorter than the other, it must be extended when converting to the longer type. Whether the shorter type is extended by filling with the sign bit or with zeros is well defined for each Win64 function. This means that any code snippets such as

    ULONG ulSlotPhysAddr[NUM_PHYS_ADDRS];
    ulSlotPhysAddr[0] = ULONG(pulPhysDmaBuffer) + DMA_BUFFER_SIZE;  // wrong

should be replaced by

    ULONG_PTR ulSlotPhysAddr[NUM_PHYS_ADDRS];
    ulSlotPhysAddr[0] = PtrToUlong(pulPhysDmaBuffer) + DMA_BUFFER_SIZE;  // correct

This is preferred even though ulSlotPhysAddr might represent the value of a hardware register that is only 32 rather than 64 bits long. For a list of all the new Win64 helper functions for converting between pointer and integer types, see The New Data Types.