On our (4.2) platform, we programmatically load/unload (dismount) the
\Storage Card and the ATADISK driver, based on the state of a switch on
our CompactFlash card door.

Most of the time, this works fine. However, it seems that if I unload,
then reload in quick succession (caused by someone opening then closing
the card door), then the \Storage Card filesystem doesn't always
reappear, even though ATADISK gets reloaded.

The unload sequence we follow is:
1. DismountStore() to dismount DSK1: (we have the reg configured so that
\Storage Card is always DSK1:). We don't (currently) wait for any
notification that the store has unloaded.
2. DeactivateDevice() for ATADISK.
3. Wait for a BLOCK_DRIVER_GUID notification that tells us that DSK1:
(ATADISK driver) has been removed.
4. Unload sequence is now complete, and we allow reloading (eg if the
door closes again).

The load sequence is:
1. ActivateDeviceEx() for ATADISK.
2. When ATADISK load is complete, it sends a BLOCK_DRIVER_GUID
notification, for the benefit of Storage Manager.
3. Storage Manager loads a FAT filesystem for each partition (we have
only one normally).
4. We wait for notification (FindFirstChangeNotification() for root
directory changes) that the \Storage Card directory has appeared.
5. Sequence complete - filesystem is now ready to use.

This all works nicely, as long as we don't unload, then reload too
quickly. If we do, the (re)load sequence fails at step 3 above.

What seems to happen (based on stepping through the code with the
debugger) is that Storage Manager's PnP thread receives the
BLOCK_DRIVER_GUID attach notification, and calls its MountStore()
function. That function then searches through its list of stores, and
finds a matching one that it marked as detached. It therefore marks it
as attached again (rather than its more usual call to AddStore()), and
then returns (without sending a STORE_MOUNT_GUID attach notification -
perhaps that is significant?).

In any case, the end result is that the \Storage Card filesystem doesn't
seem to be reloaded, as I don't get a filesystem notification for it (or
a STORE_MOUNT_GUID PnP notification - I've tried that approach too), and
I can't see it with the remote file viewer, or from an application.

Does anyone have any idea what might be going on here? Better still, how
can I get it to work (ie to always load the filesystem when ATADISK (re)
loads up)?

Re: \Storage Card doesn't always reload when ATADISK reloaded by Steve

Steve
Wed Jun 29 08:45:43 CDT 2005

Qfe's applied? There was a known issue with this that we worked with PSS to
come up with a workaround for in V4.2. The use of xxx_Preclose and
xxx_PreDeinit in V5.0 resolves the problem.

--
Steve Maillet
EmbeddedFusion
www.EmbeddedFusion.com
smaillet at EmbeddedFusion dot com



Re: \Storage Card doesn't always reload when ATADISK reloaded by Ten

Ten
Wed Jun 29 09:20:23 CDT 2005

Andrew,

The door switch is not in the CF spec and it does not really indicate
the actual insertion state of the CF card. Usually this is used as an
additional signal in the system to disable access to the card. I've
seen this used extensively on digital cameras.

The card detect signals (which are defined in the PCMCIA/CF spec) are
already handled in PCMCIA.DLL. This is what the OS is monitoring for
insertion/removal notification.

If your CF card is modeled after a PCMCIA card, then you can get
notification of an insertion and removal by registering for PCMCIA event
notifications. Here is how I did it.

Also, at startup, if there is a CF in the slot, I think the OS generates
an insertion signal artificially.

Nick.


PCMCIAHelper.H file:
#include <cardserv.h>

// define the function prototypes.
// define PCMCIA_CARD_REGISTER_CLIENT as a pointer to a function that
returns
// a CARD_CLIENT_HANDLE and takes CLIENT_CALLBACK and
PCARD_REGISTER_PARMS as
// parameters.
typedef CARD_CLIENT_HANDLE (CALLBACK* PCMCIA_CARD_REGISTER_CLIENT)
(CLIENT_CALLBACK, PCARD_REGISTER_PARMS);

// define PCMCIA_CARD_DEREGISTER_CLIENT as a pointer to a function that
returns
// a STATUS and takes CARD_CLIENT_HANDLE as a parameter.
typedef STATUS (CALLBACK* PCMCIA_CARD_DEREGISTER_CLIENT)
(CARD_CLIENT_HANDLE);

// define PCMCIA_CARD_GET_STATUS as a pointer to a function that returns
// a STATUS and takes PCARD_STATUS as a parameter.
typedef STATUS (CALLBACK* PCMCIA_CARD_GET_STATUS) (PCARD_STATUS);


PCMCIAHelper.C file:

#include <windows.h>
#include <ceddk.h>

// Variables that are global to this module.
CARD_CLIENT_HANDLE hCard = NULL;
HINSTANCE hLibrary = NULL;

// pointers to the functions we need in PCMCIA.DLL
PCMCIA_CARD_REGISTER_CLIENT fnCardRegisterClient;
PCMCIA_CARD_DEREGISTER_CLIENT fnCardDeregisterClient;
PCMCIA_CARD_GET_STATUS fnCardGetStatus;

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Function:

PCMCIAEvent

Description:

Callback entry point for card insertion/removal notifications.

Arguments:

CardEvent - what happened.
hSocket - which socket/function pair.
pCardEventParms - additional stuff related to CardEvent. This is event
specific.

Return Value:

1

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
STATUS PCMCIAEvent(CARD_EVENT CardEvent, CARD_SOCKET_HANDLE hSocket,
PCARD_EVENT_PARMS pCardEventParms)
{
CARD_STATUS card_status;
card_status.hSocket = hSocket;

// get our device state pointer so we can inform the rest of the system
that something has happened.
PDEVICESTATE pds = (PDEVICESTATE) pCardEventParms->uClientData;

switch(CardEvent)
{
case CE_CARD_REMOVAL: // 0x05

break;

case CE_CARD_INSERTION: // 0x40
// pCardEventParms->Parm1 contains the PC Card PnP ID String
// pCardEventParms->Parm2 contains the driver handle

break;

case CE_REGISTRATION_COMPLETE: // 0x82
// pCardEventParms->Parm1 contains the driver handle
fnCardGetStatus(&card_status);
DEBUGMSG(ZONE_PCMCIA,
(_T("PCMCIAEvent:CE_REGISTRATION_COMPLETE:Socket=%d, Function=%d, Card
State = 0x%08x, Socket State = 0x%08x"),hSocket.uSocket,
hSocket.uFunction, card_status.fCardState, card_status.fSocketState));
break;

case CE_BATTERY_DEAD: // 0x01
case CE_BATTERY_LOW: // 0x02
case CE_CARD_LOCK: // 0x03
case CE_CARD_READY: // 0x04
case CE_CARD_UNLOCK: // 0x06
case CE_EJECTION_COMPLETE: // 0x07
case CE_EJECTION_REQUEST: // 0x08
case CE_INSERTION_COMPLETE: // 0x09
case CE_INSERTION_REQUEST: // 0x0A
case CE_PM_RESUME: // 0x0B
case CE_PM_SUSPEND: // 0x0C
case CE_EXCLUSIVE_COMPLETE: // 0x0D // pCardEventParms->Parm1
contains the exclusive status
case CE_EXCLUSIVE_REQUEST: // 0x0E
case CE_RESET_PHYSICAL: // 0x0F
case CE_RESET_REQUEST: // 0x10
case CE_CARD_RESET: // 0x11
case CE_MTD_REQUEST: // 0x12
case CE_CLIENT_INFO: // 0x14
case CE_TIMER_EXPIRED: // 0x15
case CE_SS_UPDATED: // 0x16
case CE_WRITE_PROTECT: // 0x17
case CE_RESET_COMPLETE: // 0x80
case CE_ERASE_COMPLETE: // 0x81
case CE_STATUS_CHANGE_INTERRUPT:// 0xFE
DEBUGMSG(ZONE_PCMCIA, (_T("PCMCIAEvent:Unhandled Event
0x%08x"),CardEvent));
break;
}

return (STATUS) TRUE;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Function:

RegisterWithCardServices

Description:

Regisers the callback function PCMCIAEvent with card services. When
something
happens to the PCMCIA card (insertion/removal) we'll get notified.

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
BOOL RegisterWithCardServices(PDEVICESTATE pds)
{
BOOL rVal = FALSE;
CARD_REGISTER_PARMS params;
int state = 0;

// Get notified of everything. See EVENT_MASK_XXXX in the PB help for
the flags.
params.fAttributes = CLIENT_ATTR_MTD_DRIVER | CLIENT_ATTR_NOTIFY_SHARED
| CLIENT_ATTR_NOTIFY_EXCLUSIVE;

params.fEventMask = 0xffff;

// when the callback happens we want to have access to our device state
so pass
// a pointer to it here.
params.uClientData = (DWORD) pds;

hLibrary = LoadLibrary(L"PCMCIA.DLL");
if(NULL != hLibrary)
{
state++;
fnCardRegisterClient = (PCMCIA_CARD_REGISTER_CLIENT)
GetProcAddress(hLibrary, L"CardRegisterClient");
if(NULL != fnCardRegisterClient)
{
state++;
fnCardDeregisterClient = (PCMCIA_CARD_DEREGISTER_CLIENT)
GetProcAddress(hLibrary, L"CardDeregisterClient");
if(NULL != fnCardDeregisterClient)
{
state++;
fnCardGetStatus = (PCMCIA_CARD_GET_STATUS) GetProcAddress(hLibrary,
L"CardGetStatus");
if(NULL != fnCardGetStatus)
{
state++;
hCard = fnCardRegisterClient( PCMCIAEvent, &params);
if(NULL != hCard)
rVal = TRUE;
}
}
}
}

// if we failed for some reason, display which call failed and the last
error, and clean up.
if(!rVal)
{
DWORD dwTemp = GetLastError();
DEBUGMSG(ZONE_PCMCIA, (_T("RegisterWithCardServices failed. State =
%d. GLE returned 0x%08x"), state, dwTemp));

if(NULL != hLibrary)
FreeLibrary(hLibrary);

}

return rVal;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Function:

DeregisterWithCardServices

Description:

Deregisers the callback function PCMCIAEvent with card services.
This unhooks
the callback.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
BOOL DeregisterWithCardServices(PDEVICESTATE pds)
{
BOOL rVal = TRUE;
STATUS status;

status = fnCardDeregisterClient(hCard);

if(CERR_SUCCESS != status)
{
DEBUGMSG(ZONE_PCMCIA,
(_T("DeregisterWithCardServices:CardDeregisterClient failed.")));
rVal = FALSE;
}

// even if the deregister call fails, we still need to unload the
library we are using.
if(NULL != hLibrary)
{
if(!FreeLibrary(hLibrary))
{
DEBUGMSG(ZONE_PCMCIA, (_T("DeregisterWithCardServices:FreeLibrary
failed.")));
rVal = FALSE;
}
}

return rVal;
}



Andrew Worsley wrote:
> On our (4.2) platform, we programmatically load/unload (dismount) the
> \Storage Card and the ATADISK driver, based on the state of a switch on
> our CompactFlash card door.
>
> Most of the time, this works fine. However, it seems that if I unload,
> then reload in quick succession (caused by someone opening then closing
> the card door), then the \Storage Card filesystem doesn't always
> reappear, even though ATADISK gets reloaded.
>
> The unload sequence we follow is:
> 1. DismountStore() to dismount DSK1: (we have the reg configured so that
> \Storage Card is always DSK1:). We don't (currently) wait for any
> notification that the store has unloaded.
> 2. DeactivateDevice() for ATADISK.
> 3. Wait for a BLOCK_DRIVER_GUID notification that tells us that DSK1:
> (ATADISK driver) has been removed.
> 4. Unload sequence is now complete, and we allow reloading (eg if the
> door closes again).
>
> The load sequence is:
> 1. ActivateDeviceEx() for ATADISK.
> 2. When ATADISK load is complete, it sends a BLOCK_DRIVER_GUID
> notification, for the benefit of Storage Manager.
> 3. Storage Manager loads a FAT filesystem for each partition (we have
> only one normally).
> 4. We wait for notification (FindFirstChangeNotification() for root
> directory changes) that the \Storage Card directory has appeared.
> 5. Sequence complete - filesystem is now ready to use.
>
> This all works nicely, as long as we don't unload, then reload too
> quickly. If we do, the (re)load sequence fails at step 3 above.
>
> What seems to happen (based on stepping through the code with the
> debugger) is that Storage Manager's PnP thread receives the
> BLOCK_DRIVER_GUID attach notification, and calls its MountStore()
> function. That function then searches through its list of stores, and
> finds a matching one that it marked as detached. It therefore marks it
> as attached again (rather than its more usual call to AddStore()), and
> then returns (without sending a STORE_MOUNT_GUID attach notification -
> perhaps that is significant?).
>
> In any case, the end result is that the \Storage Card filesystem doesn't
> seem to be reloaded, as I don't get a filesystem notification for it (or
> a STORE_MOUNT_GUID PnP notification - I've tried that approach too), and
> I can't see it with the remote file viewer, or from an application.
>
> Does anyone have any idea what might be going on here? Better still, how
> can I get it to work (ie to always load the filesystem when ATADISK (re)
> loads up)?

Re: \Storage Card doesn't always reload when ATADISK reloaded by Andrew

Andrew
Wed Jun 29 16:03:40 CDT 2005

Thanks Steve,

Yes, I have the 2004 yearly QFE and 2005 1st quarter QFE's applied.
Can you give me any details of your workaround please?

Having trolled through the usenet archives, I think my problem may be
related to the PnPUnloadDelay registry setting, which is currently set
to the default value of 0x1000.

As far as I can tell, this setting is used to delay the dismount of a
filesystem after the underlying block driver goes away.

With reference to the following thread (which you participated in...):
http://groups-
beta.google.com/group/microsoft.public.windowsce.talisker.techpreview/br
owse_thread/thread/da42dae49880a1a5/8fd41b032de2ca4f?
q=pnpunloaddelay&rnum=24&hl=en

It seems that this functionality was introduced to allow for a block
driver to be shut down at suspend time, then restarted on resume, with
the associated filesystem "riding through" the change (the caveat being
don't access the files until the block driver has reloaded).

In our case, we don't support suspend/resume (our power down really does
power down, so we cold boot the next time around), so that isn't a
problem for us.

However, our sequence needs to be a little different in that we need to
remove the filesystem before the block driver goes away, in order to
ensure that any filesystem caches are written to the card before someone
removes it from the slot.

I think the problem is that when I call DismountStore(), it doesn't
actually remove its store object until after the PnPUnloadDelay timeout
expires.
In the mean time, if I unload and reload ATADISK (as described in my
original post), the MountStore() function (which gets called when
ATADISK reloads) still finds the "old" store, and just reattaches it,
rather than adding a new one. I beleive the filesystem doesn't reappear
because this code is assuming it never went away (in the suspend/resume
cycle, I guess there is no explicit call to DismountStore(), just a pair
of PnP notifications that ATADISK goes away, then comes back again,
right?).

Anyway, I'm going to try setting the PnPUnloadDelay timeout to zero,
which I beleive will solve the problem. Of course, this is only a
potential solution for us since we don't need to suspend/resume, which
obviously isn't the typical case.

Andrew

In article <e0LEdDLfFHA.3316@TK2MSFTNGP14.phx.gbl>, nospam1
@EntelechyConsulting.com says...
> Qfe's applied? There was a known issue with this that we worked with PSS to
> come up with a workaround for in V4.2. The use of xxx_Preclose and
> xxx_PreDeinit in V5.0 resolves the problem.
>
>

Re: \Storage Card doesn't always reload when ATADISK reloaded by Andrew

Andrew
Wed Jun 29 16:16:41 CDT 2005

Thanks Nick,

Unfortunately, we can't use the method you describe, because our card
door switch is a separate digital input - it does not interact with the
card detect signals. We had a good reason for designing it that way - we
want to support IO cards in the slot also, and the card door needs to be
open for most of those to work properly (since our door is made of
metal...). Hence we need to determine the card type (ie run the normal
PCMCIA detect functions) before deciding whether to load the associated
driver or not.

We get a separate interrupt when the card door opens/closes, and we have
built a separate device driver to handle those interrupts. We use that
driver as a proxy for loading/unloading the standard ATADISK driver (in
the same way the PCMCIA driver does it). We have the registry set up to
use the ATADISK driver's detection function, but (if an ATADISK card is
detected) to then load our "card door driver". The card door driver then
delays the loading of ATADISK until the door is closed, and it will
unload it again after the door is opened.

Having said that, it may be useful for us to get PCMCIA event
notifications as well, so thanks for the info on how to do it!

Andrew

In article <H6ywe.63752$g5.36768@twister.nyroc.rr.com>,
nospam@tentechnologies.com says...
> Andrew,
>
> The door switch is not in the CF spec and it does not really indicate
> the actual insertion state of the CF card. Usually this is used as an
> additional signal in the system to disable access to the card. I've
> seen this used extensively on digital cameras.
>
> The card detect signals (which are defined in the PCMCIA/CF spec) are
> already handled in PCMCIA.DLL. This is what the OS is monitoring for
> insertion/removal notification.
>
> If your CF card is modeled after a PCMCIA card, then you can get
> notification of an insertion and removal by registering for PCMCIA event
> notifications. Here is how I did it.
>
> Also, at startup, if there is a CF in the slot, I think the OS generates
> an insertion signal artificially.
>
> Nick.
>
>
> PCMCIAHelper.H file:
> #include <cardserv.h>
>
> // define the function prototypes.
> // define PCMCIA_CARD_REGISTER_CLIENT as a pointer to a function that
> returns
> // a CARD_CLIENT_HANDLE and takes CLIENT_CALLBACK and
> PCARD_REGISTER_PARMS as
> // parameters.
> typedef CARD_CLIENT_HANDLE (CALLBACK* PCMCIA_CARD_REGISTER_CLIENT)
> (CLIENT_CALLBACK, PCARD_REGISTER_PARMS);
>
> // define PCMCIA_CARD_DEREGISTER_CLIENT as a pointer to a function that
> returns
> // a STATUS and takes CARD_CLIENT_HANDLE as a parameter.
> typedef STATUS (CALLBACK* PCMCIA_CARD_DEREGISTER_CLIENT)
> (CARD_CLIENT_HANDLE);
>
> // define PCMCIA_CARD_GET_STATUS as a pointer to a function that returns
> // a STATUS and takes PCARD_STATUS as a parameter.
> typedef STATUS (CALLBACK* PCMCIA_CARD_GET_STATUS) (PCARD_STATUS);
>
>
> PCMCIAHelper.C file:
>
> #include <windows.h>
> #include <ceddk.h>
>
> // Variables that are global to this module.

Re: \Storage Card doesn't always reload when ATADISK reloaded by Andrew

Andrew
Wed Jun 29 16:55:29 CDT 2005

For anyone who may encounter a similar problem - reducing the
PnPUnloadDelay to zero fixed the problem.

For (typical) systems that need to do suspend/resume operations however,
that setting might not be desirable, since it will always cause the
filesystem to unload then reload again (thus invalidating file handles)
on resume.

To MS folks - couldn't the MountStore() function check to see that the
filesystem is actually loaded during the shutdown delay time, and reload
it if not? Currently, it doesn't seem to do that, which is why I've had
to reduce the delay time to zero so the MountStore() function never
takes that route - else I end up with no filesystem for my loaded
ATADISK driver.

Andrew

Re: \Storage Card doesn't always reload when ATADISK reloaded by Steve

Steve
Wed Jun 29 17:10:54 CDT 2005

PNPUnloadDelay should be 0 to fix the issue you see. It is intended to delay
unloading and invalidating the block driver, file system and handles for the
driver. The idea is to allow a user to re-insert the card if it was
accidentally removed and have all open file handles remain valid.

The other thing we did (but I thought was in the QFEs was to mod the ATADISK
driver to hack up a variation of Pre-DeInit and PreClose that protected the
driver from issues of the system dismounting while it's loading as the rapid
load/unload that can happen due to bounce on that switch (or in our case the
battery contacts causing suspend/resume when inserting a new battery) that
triggers some bad error case handling problems in FileSys.exe crashing
mountable storage devices until reset.

--
Steve Maillet
EmbeddedFusion
www.EmbeddedFusion.com
smaillet at EmbeddedFusion dot com