How to identify identical 16-bit PC cards that are inserted in different slots (323610)



The information in this article applies to:

  • Microsoft Windows XP Driver Development Kit (DDK)
  • Microsoft Windows 2000 Driver Development Kit (DDK)

This article was previously published under Q323610

SUMMARY

Applications that access PC cards may have to identify the cards by using some characteristics that can be externally perceived, such as the physical position that the PC cards occupy. This ability to identify a card is especially important when more than one card of the same type is inserted in different slots. When this situation occurs, applications have to indicate to the end user the specific card that a certain operation is being executed on. For example, an application can request the end user to connect an external cable to one of the PC cards that is inserted in a specific slot.

This article discusses how to identify identical 16-bit PC cards that are inserted in different slots. There is no way to provide this information in the form of a slot number or as a relative position (such as "upper slot" or "lower slot"). However, you can gather sufficient information to distinguish two identical cards in different slots and to recognize whether a card is plugged in to the same slot that it occupied earlier.

Note Although this article is meant for identification of 16-bit PC cards, the algorithm that is shown later in this article can be applied to 32-bit CardBus cards with some simplifications (see the "Additional Notes" heading in the "More Information" section of this article).

The "More Information" section includes numbered references to footnotes ([#]) that are listed in the "Notes" section at the end of the article. To continue reading the article after you read the note, click the "back to" link.

MORE INFORMATION

Identifying and locating the slot

Buses that report information about the physical location of a card can provide the location to the UINumber field of the DEVICE_CAPABILITIES data structure. This data structure is filled in by the bus driver itself in response to the IRP_MN_QUERY_CAPABILITIES method. The presence of the UINumber field is so that OEMs can visually identify a slot on the outside of the computer case. For example, a numbered label can match the information that appears in the Microsoft Windows user interface. Advanced configuration and power interface (ACPI) BIOSes can report this information by implementing the _SUN method.

For more information about the _SUN method, visit the following Web site:

Advanced Configuration and Power Interface Specification, Revision 2.0b
http://www.acpi.info/DOWNLOADS/ACPIspec-2-0b.pdf

Find the topic "6.1.6 _SUN (Slot User Number)" on page 156 of the ACPI specification document.

Microsoft Windows XP

On Windows XP, the information from the _SUN method, if implemented, is correctly passed by the PCMCIA bus driver. If this method is not implemented, or if the system does not have an ACPI BIOS, the default value of 0xFFFFFFFF appears in this field.

Microsoft Windows 2000

On Windows 2000, the PCMCIA bus driver incorrectly overwrites this field with zero for most PCMCIA adapters (also referred to as HBAs, Host Bus Adapters, or PCMCIA controllers), regardless of the real position that is occupied by the PC card.

Generally, computers that support Personal Computer Memory Card International Association (PCMCIA) cards offer two sockets. However, four or more sockets can be made available on a single computer. Depending on the underlying hardware, device objects that are enumerated by the operating system for each PCMCIA adapter may be associated with one or more sockets.

Adapters, device objects, and sockets

Adapters are in one of the following categories:
  • 2 or more sockets per device object; 1 device object per adapter

    16-bit PCMCIA adapters that are designed for the Intel x86 operating system may have up to 16 sockets per adapter. [1] Generally, they have two sockets. The adapter is enumerated as a single device object by the operating system, and the adapter handles its two or more sockets. Most 16-bit adapters are PC-Card Interrupt Controller(PCIC)-compatible adapters.
  • 1 socket per device object; 2 or more device objects per adapter

    CardBus adapters can implement up to eight sockets. Generally, they have two sockets. The adapter implements a Peripheral Component Interconnect (PCI) PCI-to-CardBus bridge for each socket, and each bridge is a separate PCI function in the adapter. [2] This means that each of these bridges appears to the operating system as an independent device object that handles its own socket.

    For example, the common case of an adapter that is composed of two PCI-to-CardBus bridges causes the operating system to enumerate two device objects. These objects represent the bridges in the adapter; they do not represent the adapter itself.
According to this categorization, a device object in this context can represent either an adapter with multiple sockets or a socket that belongs to a PCI-to-CardBus bridge. When you do not have to distinguish between these two cases, the single term parent or parent device object is adopted.

Computers that require more than two sockets may have more adapters on board. For example, a desktop computer might have in its expansion slots (PCI, ISA, and others) various cards to implement more than one adapter. These adapters belong to one of the categories that are mentioned earlier in this article, possibly in varying combinations.

How to identify a socket

Generally, to identify a socket, you must gather the following information:
  • The position of the socket in the adapter when the parent device object owns multiple sockets. This piece of information is known as a socket number.
  • The location of the parent on the host bus. This piece of information is known as parent location information.

Socket number

The Socket number of the socket that the card is plugged in to is provided by the bus driver by handling the plug-and-play I/O request packet (PnP IRP)IRP_MJ_PNP/IRP_MN_QUERY_CAPABILITIES. The bus driver fills the Address member of the DEVICE_CAPABILITIES structure that is associated with this IRP.
  • The address is set to 0x0 for PC cards that are inserted in the first socket.
  • The address is set to 0x40 for PC cards that are inserted in the second socket.[3]
The same value is available to user mode code by calling the SetupDiGetDeviceRegistryProperty function.

Parent location information

To retrieve the parent location information, you have to move from the DevNode of the PC card to its parent DevNode.[4] The parent DevNode is indeed the DevNode of the parent device object. Its LocationInformation property (stored in the registry in the device hardware key), contains unique information about the physical location of the adapter or the bridge on the host bus.[5]

For example, in a CardBus adapter with two bridges:
  • Bridge 1 has LocationInformation set to "PCI bus X, device Y, function 0".
  • Bridge 2 has LocationInformation set to "PCI bus X, device Y, function 1".
To see similar information, as shown in Device Manager, open the properties of a CardBus device, click the General tab, and then view the information in the Location field.

The following table summarizes the information that is already mentioned in this article for various cases of adapters. As mentioned earlier, Windows 2000 and Windows XP report UINumber differently.

Windows XP

AdapterHost BusCardAddressUINumberParent Device object's location information
CardBusPCI1st0x00xffffffff"PCI bus x, device y, function 0"
CardBusPCI2nd0x00xffffffff"PCI bus x, device y, function 1"
CardBus (ACPI
method _SUN
implemented)
PCI1st0x00x0"PCI bus x, device y, function 0"
CardBus (ACPI
method _SUN
implemented)
PCI2nd0x00x1"PCI bus x, device y, function 1"
PCICPCI1st0x00xffffffff"PCI bus x, device y, function 0"
PCICPCI2nd0x400xffffffff"PCI bus x, device y, function 0"
PCICISA1st0x00xffffffffn/a
PCICISA2nd0x400xffffffffn/a

Windows 2000

AdapterHost busCardAddressUINumberParent Device object's location information
CardBusPCI1st0x00x0"PCI bus x, device y, function 0"
CardBusPCI2nd0x00x0"PCI bus x, device y, function 1"
PCICPCI1st0x00x0"PCI bus x, device y, function 0"
PCICPCI2nd0x400x1"PCI bus x, device y, function 0"
PCICISA1st0x00x0n/a
PCICISA2nd0x400x1n/a
Note The ISA bus does not provide any means to locate devices over it. No location information is reported for a parent on the ISA bus, as shown in the last two rows of both tables. On the condition that there is a single ISA adapter in the computer, sufficient information is present to distinguish it from other adapters on the PCI bus, including 16-bit adapters and CardBus adapters.

Sample implementation

For this implementation to work, the code of the user mode must cooperate with the code of the kernel mode. Kernel mode code is supposed to be implemented in the driver of the PC card. User mode code is a console application, and it lists identification information.

The following file is available for download from the Microsoft Download Center:

DownloadDownload the PcmSktId.zip package now. Release Date: Oct. 1, 2003

For more information about how to download Microsoft support files, click the following article number to view the article in the Microsoft Knowledge Base:

119591 How to obtain Microsoft support files from online services

Microsoft scanned this file for viruses. Microsoft used the most current virus-detection software that was available on the date that the file was posted. The file is stored on security-enhanced servers that help prevent any unauthorized changes to the file.

In a real implementation, the user mode code is called by an application each time that the application must gather information about the cards that are currently plugged in.

The following basic principles are used to read both the socket number and the parent location information:
  • Kernel Mode
    • A device interface class is defined for the driver.[6]
    • Each device object that is owned by the driver is associated with a plugged-in PC card. These device objects expose the device interface of the device interface class that was just mentioned.
  • User Mode
    • PC cards that are managed by the driver belong to the device interface class that is mentioned earlier in this article. To find all the devices that belong to this class, collect a list of all identical PC cards that are plugged in at a specified time.
    • For each device in the list, the Address field is read.
    • For each device in the list, move through the device tree view. By moving up in the device hierarchy, you find the parent of the PC card. It represents either the adapter that has the card plugged in to one of its sockets, or it represents the PCI-to-CardBus bridge that the card is attached to.
    • The Parent location information is read.

Details about the kernel mode part

To define a new device interface class, use either the Guidgen.exe utility or the Uuidgen.exe utility to generate a GUID. Later, you put this GUID in a source code file, typically a header (.h) file. For more information about how to use GUIDs in drivers, see the "References" section of this article.

In the following sample code, the device interface GUID is associated with the constant GUID_DEVICE_PCCRDNUL. This constant is defined as follows:
// Header file for GUID definition

// {8CE365E5-D1AA-4d72-9E09-1C8B83B34186}
DEFINE_GUID(GUID_DEVICE_PCCRDNUL,
0x8ce365e5, 0xd1aa, 0x4d72, 0x9e, 0x9, 0x1c, 0x8b, 0x83, 0xb3, 0x41, 0x86);
Important To avoid collisions, generate a new GUID that is associated with a meaningful constant for each real implementation of the resolution that is provided in this article. Inclusion of the Initguid.h header file before the definition of a GUID determines whether the GUID is stored in a data section, or whether the GUID definition references a data section of another module that is linked to the driver.

In the kernel mode code, the defined device interface must be registered, enabled, and disabled as the device interface is required.

A device interface is registered by calling the IoRegisterDeviceInterface function. Generally, this registration is performed in the AddDevice function of the driver. The resulting symbolic link that is returned by the interface registration must be stored in a string that is allocated by the driver. This string is typically defined and allocated in the device extension.

Header file for main driver definitions

The following sample code is from the header file for the main driver definitions and includes device extension.
typedef struct _MY_DEVICE_EXTENSION {
  ...
  UNICODE_STRING usSymbolicLinkInterfaceName;
  ...
} MY_DEVICE_EXTENSION, *PMY_DEVICE_EXTENSION;

Driver implementation file

The following sample code is from the driver implementation file (.c or .cpp).
NTSTATUS
AddDevice(
  IN PDRIVER_OBJECT DriverObject,
  IN PDEVICE_OBJECT PhysicalDeviceObject
)
{
  PMY_DEVICE_EXTENSION deviceExtension;
  ...
  deviceExstension = (PMY_DEVICE_EXTENSION) DeviceObject->DeviceExtension;
  ...
  IoRegisterDeviceInterface
  ( PhysicalDeviceObject,&GUID_DEVICE_PCCRDNUL,
    NULL,&deviceExtension->usSymbolicLinkInterfaceName
  );
...
}
The device interface is generally enabled while handling IRP_MN_START_DEVICE and is disabled while handling IRP_MN_REMOVE_DEVICE. Device interfaces are both enabled and disabled by calling the IoSetDeviceInterfaceState function with appropriate parameters. After the symbolic link is no longer in use (typically, after the device interface has been disabled with a call to IoSetDeviceInterfaceState), the relative string must be freed.

Driver implementation file

The following sample code is from the driver implementation file (.c or .cpp).
NTSTATUS
DispatchPnp (
  IN PDEVICE_OBJECT DeviceObject,
  IN PIRP Irp
)
{
  PIO_STACK_LOCATION irpStack;
  NTSTATUS status=STATUS_SUCCESS;
  PMY_DEVICE_EXTENSION deviceExtension;
  ...
  deviceExtension = (PMY_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
  irpStack = IoGetCurrentIrpStackLocation(Irp);
  ...
  switch(irpStack->MinorFunction) {
  ...
    case IRP_MN_START_DEVICE:
      ...
      if(NT_SUCCESS(status)) {
        IoSetDeviceInterfaceState(&deviceExtension->usSymbolicLinkInterfaceName,TRUE);
      }
      ...
      break;
    ...
    case IRP_MN_REMOVE_DEVICE:
      ...
      IoSetDeviceInterfaceState(&deviceExtension->usSymbolicLinkInterfaceName,FALSE);
      RtlFreeUnicodeString(&deviceExtension->usSymbolicLinkInterfaceName);
      ...
      break;
    ...
  }
  ...
}

You do not have to de-register a device interface class. For more information about device interface routines and how to handle device interfaces, see the "References" section.

Details about the user mode part

Devices that belong to the driver (that is, devices that belong to the device interface class that is defined earlier in this article) are enumerated in the user mode part to create a list of plugged-in cards. This list is named a Device Information Set.[7]

After the list of devices is built, the socket number is read for each device. This is done by calling the SetupDiGetDeviceRegistryProperty function with the Property parameter set to SPDRP_ADDRESS.

To gather parent location information, you have to start from the PC card Devnode. Devnodes form a tree that is similar to the one that is shown in the Device Manager (when the View option is set to Device by connection). The parent Devnode is actually the Devnode that is associated with the parent (the PCMCIA adapter or the PCI-to-CardBus bridge). You have to use many SetupDi* functions and many CM_* functions for the complete implementation. The most important function is CM_Get_Parent. This function permits you to go from a DevNode to its parent node.

Among the various properties that are stored in the hardware registry key of the device for each Devnode is theLocationInformation property. Location information is unique for each device. It is impossible for two devices of the same bus type to exist on the same bus and to have identical bus-relative locations. This string contains sufficient information to identify the parent that the card is plugged in to.

Some buses (for example, the ISA bus) do not provide any location information. As mentioned earlier, if only one of the adapters resides on a bus that has no location information, sufficient information still exists for the identification to be made. For example, if a single adapter stays on the ISA bus, it is uniquely identified because it is the only one that has no location information.

For the most common case of PC card or CardBus adapters that are hosted on the PCI bus, a string in the following format is retrieved:

"PCI bus X, device Y, function Z"

Again, this string is unique in the system because no other device exists with the same triple string (PCI bus, device, function). This string can be used as one piece of information to identify the PC card device that is plugged in to this adapter.

In PCI implementations, the string in the form "PCI bus X, device Y, function Z" can be parsed to find and show to the end user the relevant information. In our example, this is the function number ("function Z" in the sample string).

Note "Function 0" probably implements the first socket, and "function 1" probably implements the second socket. However, this is not mandated by any specification. Additionally, on most laptops, sockets are not labeled. Therefore, from the end user's perspective, there is no concept of first or second socket; only an upper or lower socket is present.

Regardless of the information that is provided to the end user, the fundamental concept is that there exists an association between a device interface name and its location information. At application level, a PC card that is known by this means can be opened by passing to the CreateFile function the device interface name as the file name.[8] In this way, an application can open the same device later - possibly after the computer has been restarted - and thereby make sure that you can access the card that occupied this same physical position.

Sample output by different controllers

This section provides two examples of sample output from the console application:
  • A computer that has two sockets that are implemented by a PCI CardBus controller.
  • A computer that has an ISA PCIC controller with two sockets.
For each configuration, the output shows the PC card inserted in both the first slot and the second slot. Identification information is always in pairs: location information, and socket number.

PCI CardBus controller

The difference between the following socket information is the function field. First socket
Socket Number        : 0
Parent Device ID     : PCI\VEN_104C&DEV_AC51&SUBSYS_012A1028&REV_00\4&139E449D&0&08F0
Location Information : PCI bus 2, device 1, function 0
Interface Symb. Link : \\?\pcmcia#card_manufacturer_and_model_strings#1#{8ce365e5-d1aa-4d72-9e09-1c8b83b34186}
-------------------------------------------------------------------------------

Completed successfully
Second socket
Socket Number        : 0
Parent Device ID     : PCI\VEN_104C&DEV_AC51&SUBSYS_012A1028&REV_00\4&139E449D&0&09F0
Location Information : PCI bus 2, device 1, function 1
Interface Symb. Link : \\?\pcmcia#card_manufacturer_and_model_strings#1#{8ce365e5-d1aa-4d72-9e09-1c8b83b34186}
-------------------------------------------------------------------------------

Completed successfully

ISA PCIC controller

The difference between the following socket information is the socket number field.

Note When only one adapter is present, Location Information unavailable is a valid value.First socket
Socket Number        : 0
Location Information unavailable, likely adapter on ISA bus
Interface Symb. Link : \\?\pcmcia#card_manufacturer_and_model_strings#1#{8ce365e5-d1aa-4d72-9e09-1c8b83b34186}
-------------------------------------------------------------------------------

Completed successfully
Second socket
Socket Number        : 1
Location Information unavailable, likely adapter on ISA bus
Interface Symb. Link : \\?\pcmcia#card_manufacturer_and_model_strings#1#{8ce365e5-d1aa-4d72-9e09-1c8b83b34186}
-------------------------------------------------------------------------------

Completed successfully

Additional notes

Device co-installer

The sample code is intended to gather the required location information at runtime; a real application that is based on the sample will gather it each time that it is required. However, this technique can be improved by permanently storing the information in the registry together with the other information that is related to a specific instance of the PC card. You can do this in a standard way by writing a device co-installer.[9]

The co-installer gathers the required location information only one time during device installation (by using the method that is described in this article) and creates a position identification string in the device hardware registry. One additional improvement for drivers that name their device objects is to build the name of the device object based on the identification information that was prepared by the co-installer in the registry.

The device on the PCI bus

If an application has to determine whether the device is on the PCI bus (for example, to parse the location information string with a specific algorithm), the application can call the SetupDiGetDeviceRegistryProperty function with the following settings:
  • The Property parameter is set to SPDRP_BUSTYPEGUID.
  • The GUID for the PCI bus is defined in the Wdmguid.h file.
  • The associated constant is GUID_BUS_TYPE_PCI.

16-bit PC cards and 32-bit CardBus cards

The principle that is described in this article applies to both 16-bit PC cards and 32-bit CardBus cards. However, the 32-bit card appears to the operating system to be a regular PCI device. This kind of card has its own location information that uniquely identifies it. Therefore, you do not have to refer to the parent location information.

back to Summary

REFERENCES

For more information about using GUIDs in drivers, see the following documentation:

Windows DDK, Kernel-Mode Driver Architecture, Design Guide, Driver Programming Techniques, Using GUIDs in Drivers

For more information about device interface routines and handling device interfaces, see the following documentation:

Windows DDK, Kernel-Mode Driver Architecture, Reference, Driver Support Routines, Summary of Kernel-Mode Support, Plug and Play Routines, Device Interface Routines.

Notes

To continue reading the article, click the "back to" link.
  1. Anderson, Don. PCMCIA System Architecture: 16-bit PC Cards. Addison Wesley Professional. 2nd ed. Chapter 10, "The PC Card Host Bus Adapter", page 117, "Maximum Number of Sockets per HBA".
    back to 1
  2. Anderson, Don. CardBus System Architecture. Addison-Wesley Canada Ltd, December 1995. Chapter 17, "CardBus Bridge Configuration Registers", page 261, "Introduction".
    back to 2
  3. Microsoft Windows Driver Development Kit (DDK), Kernel-Mode Driver Architecture, Reference, System Structures, Plug and Play Structures, DEVICE_CAPABILITIES, description of member Address.
    back to 3
  4. Windows DDK, Device Installation, Design Guide, Device Classes, Device Information Sets.
    back to 4
  5. Windows DDK, Device Installation, Design Guide, Device Installation Overview, Driver Information in the Registry.
    back to 5
  6. Windows DDK, Device Installation, Design Guide, Device Classes, Device Interface Classes.
    back to 6
  7. Windows DDK, Device Installation, Design Guide, Device Classes, Device Information Sets.
    back to 7
  8. Windows DDK, Device Installation, Reference, Device Installation Functions, SetupDiEnumDeviceInterfaces, section Comments.
    back to 8
  9. Windows DDK, Device Installation, Design Guide, Writing a Co-installer.
    Coinstaller of the Toaster sample, available in the Windows DDK at the following path: DDKDIR:\Src\General\Toaster\Coinstaller
    back to 9

Modification Type:MajorLast Reviewed:9/8/2005
Keywords:kbhowto KB323610 kbAudDeveloper