Emulating a Serial Port, Part IV

Bill M: Thanks for the pointer to III, it worked like a charm.

Alas, now I’m suffering from another problem.

To reiterate, I’m working on a virtual bus that creates pairs of serial ports
that are ‘linked’ such that app A can open one port, app B can open the other,
and anything app A sends will be received by app B, and vice versa. I’m doing
this in order to emulate (with a user-mode program) a device that would normally
connect to the PC over a serial port (the real device has a usermode program
that requires a serial port in order to run). The idea is this: emulator opens
port A, device’s app opens port B, and they talk to each other this way – the
device’s app doesn’t need to be changed. Plus, it’s a flexible driver that
could be used for dozens of purposes (with the amount of work it takes to write
these things, I want it to be as reusable as possible!)

Because of this, it makes sense that these pairs of ports behave somewhat like
siamese twins – if one is disabled, the other should also be disabled; if one
is removed, the other should also be removed.

It appears now that the proper thing for removal is to handle the
RemovalRelations IRP (and possibly EjectionRelations?). However, when I tried
pairing disables together, my pile of bugs^W^W^Wdriver indicates that
something’s going seriously wrong.

What I did was, when one device gets a REMOVE irp (so that it is disabled), I
call IoInvalidateDeviceState(). When it gets a START irp, I again call
IoInvalidateDeviceState(). Whenever a QUERY_PNP_DEVICE_STATE irp comes in, I
check to see if the peer is started; if not, I set the PNP_DEVICE_DISABLED bit.
(If the peer has been completely removed from the system, I set
PNP_DEVICE_REMOVED.)

However, a slight catch-22 presented itself when I tested this version of the
driver. Here’s how things seem to go (based on my debug output):

The first device is started (IRP_MN_START_DEVICE). This causes an
IoInvalidateDeviceState() on the peer (device 2).

The first device, as part of win2k’s initialization, gets a
QUERY_PNP_DEVICE_STATE irp automatically. Since the peer had not been started
yet, it sets the PNP_DEVICE_DISABLED bit.

Th 2nd device is started (IRP_MN_START_DEVICE). The code calls
IoInvalidateDeviceState() on device 1.

The 2nd device, as part of the init, gets a QUERY_PNP_DEVICE_STATE. Since
device 1 *HAS* been started at this point, we leave with the PNP_DEVICE_DISABLED
bit cleared. [Note: I’m thinking that this automatically clears the
IoInvalidateDeviceState that occurred when device 1 was started.]

Device 1 now gets a 2nd QUERY_PNP_DEVICE_STATE irp (presumably due to the
invalidation requested by dev2’s START_DEVICE handler). Now that device 2 has
been started, we complete the irp with the PNP_DEVICE_DISABLED flag cleared to 0.


Woo! I hope everybody (or at least one person) is still following… because
this is about where I get confused.

I get a RemovalRelations irp for dev1 which I fail with a STATUS_NOT_SUPPORTED.

I then get a SURPRISE_REMOVAL irp for dev1. I don’t know why, because I didn’t
seem to do anything that indicated that the device was removed… <- This is the
part that i’m getting confused about.

The REMOVE_DEVICE then arrives in for dev1. Since I don’t understand the
intricacies of when to actually call IoDeleteDevice, I delete dev1 here.
Just before I delete the device, I call IoInvalidateDeviceState() on dev2.

Dev2 now gets a QUERY_PNP_DEVICE_STATE irp. dev2 sees that dev1 has left the
building, and sets the PNP_DEVICE_REMOVED flag.


Beyond this is stuff that I hope I can figure out once Walter Oney’s book
finally arrives in the mail :wink:

Now dev2 gets a RemovalRelations irp, followed by SURPRISE_REMOVAL and
REMOVE_DEVICE irps. This I can understand (the ddk doesn’t say HOW Windows
detects a surprise removal, but I suppose that doing it as a result of a
PNP_DEVICE_REMOVED state makes sense.). Again, my ignorance of proper device
removal procedure has me call IoDeleteDevice() on dev2’s PDO.

Now I get two consecutive EjectionRelations queries for dev1, two
RemovalRelations queries for dev1, one EjectionRelations query for dev2, one
RemovalRelations query for dev2.

The bus itself then receives a BusRelations query (which should report no
devices, as they’ve been deleted, though I haven’t directly verified this).

Now dev1 gets an EjectionRelations, dev2 gets an EjectionRelations, dev1 gets a
REMOVE_DEVICE, dev2 gets another EjectionRelations, and dev2 gets a
REMOVE_DEVICE where it incorrectly IoDeleteDevice()s itself a 2nd time and blows
up with an IRQL_NOT_LESS_OR_EQUAL.

So, in summary, my questions are these: Why does marking a device as disabled
(by setting state=PNP_DEVICE_DISABLED) send me a IRP_MN_SURPRISE_REMOVAL irp,
and what’s the proper way to indicate that a particular device is present yet
disabled?

  • Stevie-O

Real Programmers use COPY CON PROGRAM.EXE

> Because of this, it makes sense that these pairs of ports behave somewhat
like

siamese twins – if one is disabled, the other should also be disabled; if
one
is removed, the other should also be removed.

I wouldn’t have one port disable or remove on account of another. If you
had two real COM ports on a machine connected by NULL modem cable, disabling
one port wouldn’t disable the other. In fact, disconnecting the cable and
rendering the ports useless wouldn’t have any affect on the ports either. I
would just leave the orphaned port enabled, and dump any data in the bit
bucket.


Bill McKenzie
Compuware Corporation
http://www.compuware.com/products/driverstudio/

“Stevie-O” wrote in message news:xxxxx@ntdev…
>
> Bill M: Thanks for the pointer to III, it worked like a charm.
>
> Alas, now I’m suffering from another problem.
>
> To reiterate, I’m working on a virtual bus that creates pairs of serial
ports
> that are ‘linked’ such that app A can open one port, app B can open the
other,
> and anything app A sends will be received by app B, and vice versa. I’m
doing
> this in order to emulate (with a user-mode program) a device that would
normally
> connect to the PC over a serial port (the real device has a usermode
program
> that requires a serial port in order to run). The idea is this: emulator
opens
> port A, device’s app opens port B, and they talk to each other this way –
the
> device’s app doesn’t need to be changed. Plus, it’s a flexible driver
that
> could be used for dozens of purposes (with the amount of work it takes to
write
> these things, I want it to be as reusable as possible!)
>
>
> Because of this, it makes sense that these pairs of ports behave somewhat
like
> siamese twins – if one is disabled, the other should also be disabled; if
one
> is removed, the other should also be removed.
>
> It appears now that the proper thing for removal is to handle the
> RemovalRelations IRP (and possibly EjectionRelations?). However, when I
tried
> pairing disables together, my pile of bugs^W^W^Wdriver indicates that
> something’s going seriously wrong.
>
> What I did was, when one device gets a REMOVE irp (so that it is
disabled), I
> call IoInvalidateDeviceState(). When it gets a START irp, I again call
> IoInvalidateDeviceState(). Whenever a QUERY_PNP_DEVICE_STATE irp comes in,
I
> check to see if the peer is started; if not, I set the PNP_DEVICE_DISABLED
bit.
> (If the peer has been completely removed from the system, I set
> PNP_DEVICE_REMOVED.)
>
> However, a slight catch-22 presented itself when I tested this version of
the
> driver. Here’s how things seem to go (based on my debug output):
>
> The first device is started (IRP_MN_START_DEVICE). This causes an
> IoInvalidateDeviceState() on the peer (device 2).
>
> The first device, as part of win2k’s initialization, gets a
> QUERY_PNP_DEVICE_STATE irp automatically. Since the peer had not been
started
> yet, it sets the PNP_DEVICE_DISABLED bit.
>
> Th 2nd device is started (IRP_MN_START_DEVICE). The code calls
> IoInvalidateDeviceState() on device 1.
>
> The 2nd device, as part of the init, gets a QUERY_PNP_DEVICE_STATE. Since
> device 1 HAS been started at this point, we leave with the
PNP_DEVICE_DISABLED
> bit cleared. [Note: I’m thinking that this automatically clears the
> IoInvalidateDeviceState that occurred when device 1 was started.]
>
> Device 1 now gets a 2nd QUERY_PNP_DEVICE_STATE irp (presumably due to the
> invalidation requested by dev2’s START_DEVICE handler). Now that device 2
has
> been started, we complete the irp with the PNP_DEVICE_DISABLED flag
cleared to 0.
>
> ------------------------------------
> Woo! I hope everybody (or at least one person) is still following…
because
> this is about where I get confused.
> ------------------------------------
>
> I get a RemovalRelations irp for dev1 which I fail with a
STATUS_NOT_SUPPORTED.
>
> I then get a SURPRISE_REMOVAL irp for dev1. I don’t know why, because I
didn’t
> seem to do anything that indicated that the device was removed… <- This
is the
> part that i’m getting confused about.
>
> The REMOVE_DEVICE then arrives in for dev1. Since I don’t understand the
> intricacies of when to actually call IoDeleteDevice, I delete dev1 here.
> Just before I delete the device, I call IoInvalidateDeviceState() on dev2.
>
> Dev2 now gets a QUERY_PNP_DEVICE_STATE irp. dev2 sees that dev1 has left
the
> building, and sets the PNP_DEVICE_REMOVED flag.
>
> ------------------------------------
> Beyond this is stuff that I hope I can figure out once Walter Oney’s book
> finally arrives in the mail :wink:
> ------------------------------------
>
> Now dev2 gets a RemovalRelations irp, followed by SURPRISE_REMOVAL and
> REMOVE_DEVICE irps. This I can understand (the ddk doesn’t say HOW
Windows
> detects a surprise removal, but I suppose that doing it as a result of a
> PNP_DEVICE_REMOVED state makes sense.). Again, my ignorance of proper
device
> removal procedure has me call IoDeleteDevice() on dev2’s PDO.
>
> Now I get two consecutive EjectionRelations queries for dev1, two
> RemovalRelations queries for dev1, one EjectionRelations query for dev2,
one
> RemovalRelations query for dev2.
>
> The bus itself then receives a BusRelations query (which should report no
> devices, as they’ve been deleted, though I haven’t directly verified
this).
>
> Now dev1 gets an EjectionRelations, dev2 gets an EjectionRelations, dev1
gets a
> REMOVE_DEVICE, dev2 gets another EjectionRelations, and dev2 gets a
> REMOVE_DEVICE where it incorrectly IoDeleteDevice()s itself a 2nd time and
blows
> up with an IRQL_NOT_LESS_OR_EQUAL.
>
>
> So, in summary, my questions are these: Why does marking a device as
disabled
> (by setting state=PNP_DEVICE_DISABLED) send me a IRP_MN_SURPRISE_REMOVAL
irp,
> and what’s the proper way to indicate that a particular device is present
yet
> disabled?
>
>
> –
> - Stevie-O
>
> Real Programmers use COPY CON PROGRAM.EXE
>
>
>
>