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:
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
Adapter | Host Bus | Card | Address | UINumber | Parent Device object's location information |
CardBus | PCI | 1st | 0x0 | 0xffffffff | "PCI
bus x, device y, function 0" |
CardBus | PCI | 2nd | 0x0 | 0xffffffff | "PCI
bus x, device y, function 1" |
CardBus (ACPI method
_SUN implemented) | PCI | 1st | 0x0 | 0x0 | "PCI
bus x, device y, function 0" |
CardBus (ACPI method
_SUN implemented) | PCI | 2nd | 0x0 | 0x1 | "PCI
bus x, device y, function 1" |
PCIC | PCI | 1st | 0x0 | 0xffffffff | "PCI
bus x, device y, function 0" |
PCIC | PCI | 2nd | 0x40 | 0xffffffff | "PCI
bus x, device y, function 0" |
PCIC | ISA | 1st | 0x0 | 0xffffffff | n/a |
PCIC | ISA | 2nd | 0x40 | 0xffffffff | n/a |
Windows 2000
Adapter | Host bus | Card | Address | UINumber | Parent Device object's location information |
CardBus | PCI | 1st | 0x0 | 0x0 | "PCI
bus x, device y, function 0" |
CardBus | PCI | 2nd | 0x0 | 0x0 | "PCI
bus x, device y, function 1" |
PCIC | PCI | 1st | 0x0 | 0x0 | "PCI
bus x, device y, function 0" |
PCIC | PCI | 2nd | 0x40 | 0x1 | "PCI
bus x, device y, function 0" |
PCIC | ISA | 1st | 0x0 | 0x0 | n/a |
PCIC | ISA | 2nd | 0x40 | 0x1 | n/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:
Download 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 the
LocationInformation 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