cmv has asked for the wisdom of the Perl Monks concerning the following question:

Folks-

I'm just learning all about the many cool OID ioctl's that can be used to communicate with device drivers - OS independently (as I understand). The latest one I came across is OID_PNP_QUERY_POWER, which directs the device driver to: "Tell me what power state you are currently at".

Windows XP identifies that the responses from using OID_PNP_QUERY_POWER should be one of:

0x0 NdisDeviceStateUnspecified (PM not supported) 0x1 NdisDeviceStateD0 (fully powered) 0x2 NdisDeviceStateD1 (less-than-fully powered) 0x3 NdisDeviceStateD2 (more-less-than-fully powered) 0x4 NdisDeviceStateD3 (really-almost-not-powered-at-all)
Since I was interested in trying to get this information out of the collection of NIC devices on my laptop (along with other stuff), I hacked up a quick script and made it available below.

My problem is that although the OID_802_3* ioctl's are working, the OMP_PNP_QUERY_POWER one does not.

Any thoughts on what I am doing wrong?

Thanks

-Craig
UPDATE:...thats *PNP* not *PMP (sorry...)
UPDATE: I'm attaching the code here instead of in my scratchpad, by request:

use strict; use Win32API::File qw(CreateFile DeviceIoControl :FILE_SHARE_ :Misc); use Data::Dumper; sub IOCTL_NDIS_QUERY_GLOBAL_STATS () { 0x17 << 16 | 2 }; sub OID_802_3_PERMANENT_ADDRESS () { 0x01010101 }; sub OID_802_3_CURRENT_ADDRESS () { 0x01010102 }; sub OID_PNP_SET_POWER () { 0xFD010101 }; sub OID_PNP_QUERY_POWER () { 0xFD010102 }; # This routine graciously provided by PerlMonk almut # http://www.perlmonks.org/?node_id=580097 # http://www.perlmonks.org/?node_id=639099 sub NDIS_Query { my ($handle, $oid) = @_; my $nBytes = 0; my $buf = "\0"x10; my $oidp = pack("L", $oid); DeviceIoControl($handle, IOCTL_NDIS_QUERY_GLOBAL_STATS(), $oidp, length($oidp), $buf, length($buf), $nBytes, [] ); if($nBytes > 0) { #return join "-", unpack("(a2)*", unpack("H*", $buf) ); return(unpack("b*", $buf)); } return(); } # Get nic info via wmic (not available on all versions of windows)... my @devs = split(/^\s*\cM\n/m, `wmic nicconfig list /format:value`); shift(@devs); # Remove initial blank line # Iterate through each device... foreach my $d (@devs) { $d =~ s/\cM//g; # Grrrr, rotten windows my %node = split(/[=\n]/, $d); # Hashify information print "\n$node{Description} ($node{ServiceName}) \n"; # Make device to do the ioctl on... my $fname = '//./' . $node{SettingID}; #my $fname = '//Device/NPF_' . $node{SettingID}; my $fd = CreateFile($fname, 0, FILE_SHARE_READ(), [], OPEN_EXISTING(), 0, []); print "\t$fname "; if(!$fd) { print "$!\n"; next; } else { print "\n" }; # print info... print "\tCurrent Power :", NDIS_Query($fd, OID_PNP_QUERY_POWER()), + ":\n"; print "\tPermanent MAC :", NDIS_Query($fd, OID_802_3_PERMANENT_ADD +RESS()), ":\n"; print "\tCurrent MAC :", NDIS_Query($fd, OID_802_3_CURRENT_ADDRESS +()), ":\n"; #print Dumper(\%node); }

Replies are listed 'Best First'.
Re: OID Power Status Query
by NetWallah (Canon) on Oct 02, 2007 at 20:29 UTC
    The following info from a google groups discussion may be relevent:
    The DDK wording is not straightforward enough. It should say:

    "The OID_PNP_QUERY_POWER OID requests the miniport driver to indicate whether it can transition its NIC to the low- power state specified in the NDIS_DEVICE_POWER_STATE value pointed by InformationBuffer." instead of "low-power state specified in the InformationBuffer". One time I interpreted it as:

    PowerState = (NDIS_DEVICE_POWER_STATE) InformationBuffer;

    In addition, in responding to a previous discussion on wmi, it bothered me that a some external code was run and interpreted in order to get NIC info, so I came up with this: (You need to install DBD::WMI:
    use DBI; use strict; my $dbh = DBI->connect('dbi:WMI:'); # You need to install DBD::WM +I my $sth = $dbh->prepare(<<WQL ); SELECT * from Win32_NetworkAdapterConfiguration WHERE IPEnabled = True WQL my @atts = qw[caption DefaultIPGateway Description ServiceName Index + IPAddress MACAddress ]; # $sth->execute(); while (my @row = $sth->fetchrow) { my $item= $row[0]; for (@atts){ if (ref $item->{$_} eq "ARRAY"){ print "$_\t=". join (",", @{$item->{$_}}) . ";\n"; }else{ print "$_\t=". $item->{$_} . ";\n"; } } print "\n"; }
    This probably needs some tweaking to get the info you require. MS document is at http://msdn2.microsoft.com/en-us/library/aa394217.aspx .

    Update: One more point : I read somewhere in the MSDN site (Can't find the URL now) that using the WIN32 API to get NDIS info is deprecated. It recommended using WMI.

         "As you get older three things happen. The first is your memory goes, and I can't remember the other two... " - Sir Norman Wisdom

      Wow, great reply NetWallah! I've been chewing through information links since last night. The google group pointer was interesting reading, but I wasn't able to apply anything they said to get things working (there was a lot I didn't understand though).

      On the plus side, I was able to track down some stuff yesterday afternoon. After my original posting, I was thinking that the hardcoded use of IOCTL_NDIS_QUERY_GLOBAL_STATS may be the problem. I ran across some other possibilities that could be used, here:

      // // NtDeviceIoControlFile IoControlCode values for this device. // // Warning: Remember that the low two bits of the code specify how the // buffers are passed to the driver! // #define _NDIS_CONTROL_CODE(request,method) \ CTL_CODE(FILE_DEVICE_PHYSICAL_NETCARD, request, method, FI +LE_ANY_ACCESS) #define IOCTL_NDIS_QUERY_GLOBAL_STATS _NDIS_CONTROL_CODE(0, METHOD_O +UT_DIRECT) #define IOCTL_NDIS_QUERY_ALL_STATS _NDIS_CONTROL_CODE(1, METHOD_O +UT_DIRECT) #define IOCTL_NDIS_DO_PNP_OPERATION _NDIS_CONTROL_CODE(2, METHOD_B +UFFERED) #define IOCTL_NDIS_QUERY_SELECTED_STATS _NDIS_CONTROL_CODE(3, METHOD_O +UT_DIRECT) #define IOCTL_NDIS_ENUMERATE_INTERFACES _NDIS_CONTROL_CODE(4, METHOD_B +UFFERED) #define IOCTL_NDIS_ADD_TDI_DEVICE _NDIS_CONTROL_CODE(5, METHOD_B +UFFERED) #define IOCTL_NDIS_GET_LOG_DATA _NDIS_CONTROL_CODE(7, METHOD_O +UT_DIRECT) #define IOCTL_NDIS_GET_VERSION _NDIS_CONTROL_CODE(8, METHOD_O +UT_DIRECT)
      I couldn't make much sense of the CTL_CODE macro, but found documentation on it here, and found a freeBSD implementation of it at this link:
      #define CTL_CODE(devType, func, meth, acc) (((devType) << 16) | ((acc) + << 14) | ((func) << 2) | (meth))
      Using any of these didn't help me any towards a solution (although you may have to deal with the METHOD_BUFFERED differently), and that's as far as I've gotten so far.

      Also, thanks for the DBD::WMI code! Alas, I'm constrained to only using modules distributed via activestate, and this isn't one of them. I actually asked for some help on solving this issue last November, but got nowhere.

      Any other thoughts, or pointers are welcome!

      -Craig