Multiport Ethernet Device

everything below looks correct to me…

// Set some properties for the child device
WDF_DEVICE_PNP_CAPABILITIES_INIT(&pnp_capabilities);
//pnp_capabilities.Removable = WdfTrue;
//pnp_capabilities.EjectSupported = WdfTrue;
//pnp_capabilities.SurpriseRemovalOK = WdfTrue;

pnp_capabilities.Address = port_number;
pnp_capabilities.UINumber = port_number;

WdfDeviceSetPnpCapabilities(child_device, &pnp_capabilities);

// Create a custom interface so that other drivers can
// query (IRP_MN_QUERY_INTERFACE) and use our callbacks directly.
RtlZeroMemory(&mac_interface, sizeof(mac_interface));

mac_interface.InterfaceHeader.Size = sizeof(mac_interface);
mac_interface.InterfaceHeader.Version = 1;
mac_interface.InterfaceHeader.Context = (PVOID) child_device;

// Let the framework handle reference counting.
mac_interface.InterfaceHeader.InterfaceReference =
WdfDeviceInterfaceReferenceNoOp;
mac_interface.InterfaceHeader.InterfaceDereference =
WdfDeviceInterfaceDereferenceNoOp;

mac_interface.bus_get_port_number = bus_get_port_number;
mac_interface.bus_get_port_memory = bus_get_port_memory;
mac_interface.IsSafetyLockEnabled = Bus_IsSafetyLockEnabled;

WDF_QUERY_INTERFACE_CONFIG_INIT(
&query_interface_config,
(PINTERFACE) &mac_interface,
&GUID_TOASTER_INTERFACE_STANDARD,
NULL);

// If you need multiple interfaces, you can call WdfDeviceAddQueryInterface
// multiple times to add additional interfaces.
status = WdfDeviceAddQueryInterface(child_device, &query_interface_config);

if (!NT_SUCCESS(status))
{
goto CLEANUP;
}

// Add this device to the FDO’s collection of children.
// After the child device is added to the static collection successfully,
// driver must call WdfPdoMarkMissing to get the device deleted. It
// shouldn’t delete the child device directly by calling WdfObjectDelete.
status = WdfFdoAddStaticChild(device, child_device);
if (!NT_SUCCESS(status))
{
goto CLEANUP;
}

I see a potential problem:

xxxxx@gmail.com wrote:

----- BUS DRIVER CODE ----
mac_interface.InterfaceHeader.Size = sizeof(mac_interface);
mac_interface.InterfaceHeader.Version = 1;
mac_interface.InterfaceHeader.Context = (PVOID) child_device;

----- MINIPORT CODE -----
nt_status = WdfFdoQueryForInterface( <– fails with STATUS_NOT_SUPPORTED
adapter->wdf_device,
&GUID_TOASTER_INTERFACE_STANDARD,
(PINTERFACE) &adapter->bus_interface,
sizeof(TOASTER_INTERFACE_STANDARD),
1,
NULL);// InterfaceSpecific Data

Note the sizes. Is “mac_interface” actually an instance of
TOASTER_INTERFACE_STANDARD? Or is it your own structure derived from
INTERFACE?

(I also need to ask why you haven’t changed the GUID and struct names.
This is no longer a TOASTER device.)


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Don’t use GUID_TOASTER_INTERFACE_STANDARD, generate your own guid. Are you sure your miniport driver is loaded on your pdo? How did you install the miniport driver?

d

debt from my phone


From: xxxxx@gmail.com
Sent: 9/26/2012 2:29 PM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Multiport Ethernet Device

everything below looks correct to me…

// Set some properties for the child device
WDF_DEVICE_PNP_CAPABILITIES_INIT(&pnp_capabilities);
//pnp_capabilities.Removable = WdfTrue;
//pnp_capabilities.EjectSupported = WdfTrue;
//pnp_capabilities.SurpriseRemovalOK = WdfTrue;

pnp_capabilities.Address = port_number;
pnp_capabilities.UINumber = port_number;

WdfDeviceSetPnpCapabilities(child_device, &pnp_capabilities);

// Create a custom interface so that other drivers can
// query (IRP_MN_QUERY_INTERFACE) and use our callbacks directly.
RtlZeroMemory(&mac_interface, sizeof(mac_interface));

mac_interface.InterfaceHeader.Size = sizeof(mac_interface);
mac_interface.InterfaceHeader.Version = 1;
mac_interface.InterfaceHeader.Context = (PVOID) child_device;

// Let the framework handle reference counting.
mac_interface.InterfaceHeader.InterfaceReference =
WdfDeviceInterfaceReferenceNoOp;
mac_interface.InterfaceHeader.InterfaceDereference =
WdfDeviceInterfaceDereferenceNoOp;

mac_interface.bus_get_port_number = bus_get_port_number;
mac_interface.bus_get_port_memory = bus_get_port_memory;
mac_interface.IsSafetyLockEnabled = Bus_IsSafetyLockEnabled;

WDF_QUERY_INTERFACE_CONFIG_INIT(
&query_interface_config,
(PINTERFACE) &mac_interface,
&GUID_TOASTER_INTERFACE_STANDARD,
NULL);

// If you need multiple interfaces, you can call WdfDeviceAddQueryInterface
// multiple times to add additional interfaces.
status = WdfDeviceAddQueryInterface(child_device, &query_interface_config);

if (!NT_SUCCESS(status))
{
goto CLEANUP;
}

// Add this device to the FDO’s collection of children.
// After the child device is added to the static collection successfully,
// driver must call WdfPdoMarkMissing to get the device deleted. It
// shouldn’t delete the child device directly by calling WdfObjectDelete.
status = WdfFdoAddStaticChild(device, child_device);
if (!NT_SUCCESS(status))
{
goto CLEANUP;
}


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

I generated my own GUID for GUID_TOASTER_INTERFACE_STANDARD. When installing the miniport driver, I used the code below. The device id used is defined in the bus driver, and used when I create each PDO in the bus driver.

The GUID is used as the device ID when I create the PDO.

; For XP and later
[xxx.NT$ARCH$]
%selethlite.DeviceDesc% = xxxx.ndi, {57EE09B1-A93A-4B0D-BF57-EB6CD5BB2344}\XXX

Did you use devcon to install the driver? If yes, devcon update or install as the cmd line?

d

debt from my phone


From: xxxxx@gmail.com
Sent: 9/26/2012 2:59 PM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Multiport Ethernet Device

I generated my own GUID for GUID_TOASTER_INTERFACE_STANDARD. When installing the miniport driver, I used the code below. The device id used is defined in the bus driver, and used when I create each PDO in the bus driver.

The GUID is used as the device ID when I create the PDO.

; For XP and later
[xxx.NT$ARCH$]
%selethlite.DeviceDesc% = xxxx.ndi, {57EE09B1-A93A-4B0D-BF57-EB6CD5BB2344}\XXX


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

i installed via the device manager. The device created by the bus driver showed up under “other devices”, and I installed the device via the inf for the miniport driver.

I fixed it. The problem was in the following:

*IfType = 117 ; IF_TYPE_GIGABITETHERNET
*MediaType = 0 ; NdisMedium802_3
*PhysicalMediaType = 14 ; NdisPhysicalMedium802_3
Characteristics = 0x81 ; NCF_HAS_UI,NCF_VIRTUAL
BusType = 15 ; PNPBus
AddReg = selethlite.Reg ; Standard Registry Items
AddReg = selethliteui.Reg ; User Accessible Registry Items
CopyFiles = selethlite.CopyFiles

I had it stated as 0x84 for characteristics. Changing it to 0x81 solved everything.

What the best way of notifying a CHILD device of an interrupt?

The best designs don’t require that. Take some time looking at what you’re
doing and see if you can avoid it.

If you refuse to refactor around this problem, take the entire problem of
registering and unregistering very, very seriously. Almost everybody ends
up with race conditions there.

If you absolutely insist that you must go down this path, I suggest the
following strategy:

  1. The child device passes its FDO and an initialized DPC object to the
    parent.
  2. The parent references the child’s FDO and takes note of the DPC object.
  3. The parent handles the ISR internally. It stops the device’s interrupt.
  4. The parent enqueues the child’s DPC, passing the FDO’s device extension
    (or WDF context, or whatever) as the Context.
  5. When the child wants to unregister, the parent masks the interrupt,
    flushes DPCs, destroys the reference to the DPC object and dereferences the
    FDO.

Jake Oshins
Windows Kernel Team

This message offers no warranties and confers no rights.

wrote in message news:xxxxx@ntdev…

What the best way of notifying a CHILD device of an interrupt?

Jake, I know what you are saying. Better to just handle the interrupts in the BUS driver (parents) rather than deal with it in the miniport driver. I’m looking at the PCIDRV driver as a reference for the miniport driver. However, I’m not sure what to do for a multi-port ethernet adapter.

  1. How would I handle interrupts for 4 different ports? Would I just use queues to handle the read/write requests as they are doing in the PCIDRV driver, except create read/write queues for EACH port?

and FYI the DPC design works. Refactoring to make the bus driver handle everything is on my list of to dos however.

The PCIDRV sample is meant to illustrate a lot of different techniques.
It’s not structured for speed. If your NIC is anything other than a WWAN
device, I would suggest that you not use IRP (or WDFREQUEST) queues for
network packets. IRPs don’t map well to the networking concept of “indicate
incoming packets.”

In any case, the first-level interrupt handling, the part where you ensure
that your device stops interrupting, must be done in your parent driver.
The parent driver must own the registers associated with this task. That
leaves you with two questions:

  • How do you split off the rest of the tasks so that other registers are
    owned by the children? (Again, I urge you not to let the children own any
    hardware.)

  • How do you register callback functions with the parent driver in a way
    that you’ve closed all the race conditions in handling events?

My suggestions around DPCs cover the second question.

Jake Oshins
Windows Kernel Team

This message offers no warranties and confers no rights.

wrote in message news:xxxxx@ntdev…

Jake, I know what you are saying. Better to just handle the interrupts in
the BUS driver (parents) rather than deal with it in the miniport driver.
I’m looking at the PCIDRV driver as a reference for the miniport driver.
However, I’m not sure what to do for a multi-port ethernet adapter.

  1. How would I handle interrupts for 4 different ports? Would I just use
    queues to handle the read/write requests as they are doing in the PCIDRV
    driver, except create read/write queues for EACH port?

I have the following code in my bus driver’s iinterrupt handler. For some reason, when the miniport driver is in the running state (connected), and I attempt to disable the driver, its never pauses (continues sending/receiving packets, and trying to pause). I don’t know why the driver never pauses.

// Send interrupt to each port
for (port_number = 1;port_number <= NUM_OF_PORTS;port_number++)
{
// Don’t do anything if interrupt is not registered for port
if (fdo_data->dpc_list[port_number-1] == NULL)
{
continue;
}

// Compute bar0 address for each port
bar_address =
(PUCHAR)(ULONG_PTR)fdo_data->pci_bus_info.pci_bar0_virtual +
((port_number - 1) * fdo_data->pci_bus_info.pci_bar0_length);

imask = READ_REGISTER_ULONG((PULONG)((PUCHAR)bar_address + IMASK_OFFSET));
ievent = READ_REGISTER_ULONG((PULONG)((PUCHAR)bar_address + IEVENT_OFFSET));

if (imask && ievent)
{
// Disable Interrupts
WRITE_REGISTER_ULONG((PULONG)((PUCHAR)bar_address + IMASK_OFFSET), 0);

// Queue DPC in miniport (upper edge)
ASSERT(*(fdo_data->dpc_list[port_number-1]));
enqueue_state = WdfDpcEnqueue(*(fdo_data->dpc_list[port_number-1]));
}
}

Did your MiniportPause routine get called?

Calvin

On Mon, Oct 1, 2012 at 4:03 PM, wrote:

> I have the following code in my bus driver’s iinterrupt handler. For some
> reason, when the miniport driver is in the running state (connected), and I
> attempt to disable the driver, its never pauses (continues
> sending/receiving packets, and trying to pause). I don’t know why the
> driver never pauses.
>
> // Send interrupt to each port
> for (port_number = 1;port_number <= NUM_OF_PORTS;port_number++)
> {
> // Don’t do anything if interrupt is not registered for port
> if (fdo_data->dpc_list[port_number-1] == NULL)
> {
> continue;
> }
>
> // Compute bar0 address for each port
> bar_address =
> (PUCHAR)(ULONG_PTR)fdo_data->pci_bus_info.pci_bar0_virtual +
> ((port_number - 1) * fdo_data->pci_bus_info.pci_bar0_length);
>
> imask = READ_REGISTER_ULONG((PULONG)((PUCHAR)bar_address +
> IMASK_OFFSET));
> ievent = READ_REGISTER_ULONG((PULONG)((PUCHAR)bar_address +
> IEVENT_OFFSET));
>
> if (imask && ievent)
> {
> // Disable Interrupts
> WRITE_REGISTER_ULONG((PULONG)((PUCHAR)bar_address +
> IMASK_OFFSET), 0);
>
> // Queue DPC in miniport (upper edge)
> ASSERT((fdo_data->dpc_list[port_number-1]));
> enqueue_state =
> WdfDpcEnqueue(
(fdo_data->dpc_list[port_number-1]));
> }
> }
>
> —
> NTDEV is sponsored by OSR
>
> For our schedule of WDF, WDM, debugging and other seminars visit:
> http://www.osr.com/seminars
>
> To unsubscribe, visit the List Server section of OSR Online at
> http://www.osronline.com/page.cfm?name=ListServer
>

never called

I know it has something to do with my WDFDPC calls…I’m just not sure what. The parent device of the DPC object is the device object of the miniport driver, and I pass this DPC object to the bus driver, which enqueues it whenever a interrupt arrives for the miniport.

STATE

Miniport Running
Device PnP REMOVED
Datapath Normal
Operational status DORMANT
Operational flags 00000004 ? DORMANT_PAUSED
Admin status ADMIN_UP
Media Connected
Power D0
References 8
User Handles 0
Total Resets 0
Pending OID None
Flags 2c452208
? BUS_MASTER, SG_DMA, DEFAULT_PORT_ACTIVATED, SUPPORTS_MEDIA_SENSE,
DOES_NOT_DO_LOOPBACK, MEDIA_CONNECTED
PnPFlags 00200012
? VIRTUAL_DEVICE, REMOVE_IN_PROGRESS, HARDWARE_DEVICE

You probably (95%) have 1+ outstanding TX or an OID get stuck. Pause can be
initiated from several places (PNP remove is one of them). Some pauses
aren’t gated by outstandings i.e. a protocol bind/unbind on-fly. PNP remove
will be held if there exists any outstanding send.
Good luck!
Calvin
On Mon, Oct 1, 2012 at 4:43 PM, wrote:

> never called
>
> —
> NTDEV is sponsored by OSR
>
> For our schedule of WDF, WDM, debugging and other seminars visit:
> http://www.osr.com/seminars
>
> To unsubscribe, visit the List Server section of OSR Online at
> http://www.osronline.com/page.cfm?name=ListServer
>

If my pause handler is what drains the send queue, but is never called, I’m confused on what to fix.

Find the OID or SEND packet that got stuck. Pause in the context of PNP
remove will not arrive so you would have a chicken and egg issue.

You can register a chk_for_hang routine to deal with TX timeout.

Have fun!

On Mon, Oct 1, 2012 at 5:01 PM, wrote:

> If my pause handler is what drains the send queue, but is never called,
> I’m confused on what to fix.
>
> —
> NTDEV is sponsored by OSR
>
> For our schedule of WDF, WDM, debugging and other seminars visit:
> http://www.osr.com/seminars
>
> To unsubscribe, visit the List Server section of OSR Online at
> http://www.osronline.com/page.cfm?name=ListServer
>

I have a check for hang that catches TX hangs. As far as the OID…how would I check that?