3 PCMCIA Drivers
This chapter explains the fundamentals associated with writing a PCMCIA driver for GEOS . Life for a PCMCIA driver— as with a human being— begins and ends with traumatic events. The driver’s birth begins with the insertion of a card into the device; its death is marked by the removal of that card. When writing a PCMCIA driver, you may want to contemplate your own end and handle the removal case elegantly, for the driver’s sake if not for your own.
When a card is first inserted into a PCMCIA “socket” (or “slot”) GEOS will load all drivers for that card that it finds, one after the other. Those that are found to be compatible with the card remain loaded as long as the card remains within the chosen socket. The removal of a card is the other big event in the PCMCIA driver’s life. At that point, if anything is actively using the card, the driver can raise an objection to that removal and attempt to resolve any conflicts that arise.
A PCMCIA driver is a complex driver; there are many other events besides insertion and removal that a typical driver will need to handle. These events are usually functions of CardServices, a third-party library licensed to Geoworks, and, more specifically, the GEOS PCMCIA library interface to CardServices. This chapter is not meant to document all of these complex cases because drivers, whether file system or serial, may exhibit vastly different characteristrics. You will want to consult the pcmcia.def file on the SDK for more information on handling these functions.
A PCMCIA sample driver that you may use as a template is available in \OMNIGO\DRIVER\DDK\PCMCIA\SAMPLE . Other (functioning) drivers are located within \OMNIGO\DRIVER\DDK\PCMCIA.
3.1 PCMCIA Drivers Basics
Writing a GEOS device driver for PCMCIA cards is somewhat different than writing a driver for other devices. The driver acts not only with the device, but with the PCMCIA library (a GEOS library) and CardServices.
A PCMCIA driver must handle the four basic DriverFunction calls and must also handle the specific PCMCIAFunction calls defined in pcmciaDr.def. PCMCIA drivers are not extended, so they do not need to handle the DriverExtendedFunction calls.
Because your driver must interact with CardServices, it must also define a callback routine handling the specific CardServicesEventCode types. It will also need to send function calls to CardServices using the CardServicesFunction type.
3.1.1 State Information
A PCMCIA driver needs to maintain information about the card(s) and socket(s) that it is driving. The “socket” refers to the physical slot of the PCMCIA hardware interface. (This should not be confused with the GEOS Socket library API .) Each driver should contain the capability to support multiple sockets since hardware platforms may contain multiple PCMCIA ports. Also, CardServices may map multiple logical sockets to a single physical socket; this allows CardServices to mimic multi-function cards.
A driver should keep information about each socket in some structure table. The information stored in this table is, of course, up to the driver. Examples of information that may be necessary:
- The socket number.
- Whether the card was removed while it was in use.
- How the card was configured. This depends on the requirements of the device.
3.1.2 Handling Basic Functions
DR_INIT, DR_EXIT
Your PCMCIA driver will need to handle the basic DR_INIT and DR_EXIT routines defined in driver.def. (There usually is not any need to handle DR_SUSPEND and DR_UNSUSPEND .) Because PCMCIA drivers are not extended, there is no need to handle DRE_TEST_DEVICE and DRE_SET_DEVICE.
3.1.2.1 Insertion
As noted, the insertion or removal of a PCMCIA card are the two most important events in the driver’s life. When a driver is first loaded, it registers with CardServices. Among other things, this registration allows the driver to be told when a card is inserted. (Be patient; this is not as non-sensical as it seems.)
Of course, inserting a card prompted the registration in the first place! This only means that the driver is guaranteed to receive notification that at least one card was inserted, after registration with CardServices is complete. Once informed of this event (or additional insertion events), the driver must examine the card (now within a “socket”) to discern whether it can support the card. If the card is compatible, the driver configures the card according to its own specifications and makes its devices and/or memory available to GEOS.
3.1.2.2 Removal
The removal of a PCMCIA card is a difficult event for a PCMCIA driver. The driver may be writing a file to the card; it may be communicating something over the serial line. No one wants to “go” when confronted with the tasks still remaining, but unlike us, a PCMCIA driver has the option of objecting to its removal.
If something is actively using the card, the driver must tell the PCMCIA library to object to the card’s removal. The library will inform the user that the removal was effected under hostile protest. The user then has the option of reinserting the card and either leaving it in (and allowing whatever objected to the removal finish its business) or force GEOS to stop accessing the card (for example, by closing applications). The user always has the option of rebooting the system if the card is no longer available.
The last thing a driver will do is unregister itself with CardServices. Afterward, the socket is closed and the driver is unloaded.
DR_INIT
This function is sent to the driver by the kernel when the driver is first loaded. A PCMCIA device driver should handle this call by registering as a CardServices client. It does this by invoking the CardServicesFunction CSF_REGISTER_CLIENT event. (See “PCMCIA Library Functions” on page 49.) This registration is a separate issue than registration with the PCMCIA library itself; that should take place in your DR_PCMCIA_CHECK_SOCKET routine. (At that time, the driver will know into which socket the card was inserted.)
The driver should return carry set (failure) only if it is incompatible with CardServices or the current environment in some way. The driver cannot consult the card to see if it supports it yet; it does not yet know into what socket the card was inserted. (That information is provided at a later time by the CSEC_CARD_INSERTION event from CardServices.)
This function is guaranteed to occur before a DR_PCMCIA_CHECK_SOCKET event; that function must wait for the registration (initiated by this handler) to be complete before checking whether the card is supported.
Pass:
- di -> DR_INIT (= 0).
- cx -> value of di passed to GeodeLoad. If the driver was not loaded through GeodeLoad, the value in this register is undefined.
- dx -> value of bp passed to GeodeLoad. If the driver was not loaded through GeodeLoad, the value in this register is undefined.
Returns:
- CF -> Set if initialization failed; the system will then automatically unload the driver.
Destroyed:
- Allowed to destroy ax, cx, dx, ds, es, di, si, bp
Include:
driver.def
Code Display 3-1 Sample DR_INIT Routine
SampleInit proc far
.enter
; If you will need another driver to get your work done, retrieve its
; strategy routine here and store it. Fetching the strategy routine of
; another driver involves loading in the core block, which we can’t do at
; interrupt time.
; Register as a CardServices client. We do this by retrieving the address
; of the callback routine (which must be in fixed memory) and sending
; CSF_REGISTER_CLIENT to CardServices.
mov di, segment SampCardServicesCallback
mov si, offset SampCardServicesCallback
mov cx, size regArgList
segmov es, cs
mov bx, offset regArgList
CallCS CSF_REGISTER_CLIENT
jc fail
;
; ds is assumed loaded by the strategy routine ...
;
mov ds:[csHandle], CS_HANDLE_REG
clc
done:
.leave
ret
fail:
stc
jmp done
SampleInit endp
;
; Set this thing up appropriate to the card you’re driving.
; In some cases, your driver may manage an I/O device but register as
; a memory client to make sure the automatic configuration client
; gets called to configure the card before this driver gets called.
; It depends on who does the actual configuration for the cards you
; manage.
;
regArgList CSRegisterClientArgs <
mask CSRCAA_ARTIFICIAL_EXCLUSIVE or mask CSRCAA_ARTIFICIAL_SHARED or \
mask CSRCAA_MCD,
mask CSEM_CARD_DETECT_CHANGE,
< 0, segment dgroup, 0, 0>,
0201h
Init ends
DR_EXIT
This function is sent to a driver by the kernel when it is being unloaded. A PCMCIA device driver should handle this call by at least de-registering as a CardServices client. It does this by sending the CSF_DEREGISTER_CLIENT event defined in the PCMCIA library. The driver should also release any resources it may have requested from CardServices.
Pass:
- di -> DR_EXIT (= 2).
Returns:
- Nothing.
Destroyed:
- Allowed to destroy ax, bx, cx, dx, ds, es, di, si.
Include:
driver.def
Code Display 3-2 Sample DR_EXIT Routine
SampleExit proc far
uses ax, cx, dx
.enter
clr cx
mov dx, ds:[csHandle]
CallCS CSF_DEREGISTER_CLIENT
clc
.leave
ret
SampleExit endp
3.1.3 PCMCIA Driver Functions
DR_PCMCIA_CHECK_SOCKET, DR_PCMCIA_OBJECTION_RESOLVED, DR_PCMCIA_CLOSE_SOCKET, DR_PCMCIA_DEVICE_ON, DR_PCMCIA_DEVICE_OFF
In addition to handling the basic functions, a PCMCIA driver must be able to handle the functions defined by PCMCIAFunction, a special enumerated type defined in pcmciaDr.def.
The first of these function names is an enumerated value equal to 8 (or two past the last DriverFunction), and the constants increase by two thereafter. ___
DR_PCMCIA_CHECK_SOCKET
The PCMCIA library calls this function when a card has been inserted into the device. This function will only occur after a DR_INIT has already occurred; that function should register the socket as a CardServices client.
In your handler for this function, make sure to wait for the registration with CardServices to complete. (This is typically done with a wait loop; the loop checks the state of a driver flag indicating whether notification has been completed.) After this has occurred, the PCMCIA driver should also check the socket for compatibility with the card inserted.
This function typically takes place after a CSEC_CARD_INSERTION event has occurred. Drivers should respond to a CSEC_CARD_INSERTION event by setting a driver flag indicating whether the indicated driver supports the card.
A driver responding to a DR_PCMCIA_CHECK_SOCKET should wait until registration is complete (by receipt of a CSEC_REGISTRATION_COMPLETE event). At that point, they can test whether the CSEC_CARD_INSERTION event occurred smoothly.
If the card is supported, the driver should then register with the PCMCIA library using PCMCIARegisterDriver. This will register your driver with the PCMCIA library for the particular card in the particular socket . Note that this is a separate registration than that with CardServices.
Pass:
- cx -> Socket number.
- di -> DR_PCMCIA_CHECK_SOCKET
Returns:
- CF -> Set if PCMCIA card in the socket is supported by the driver.
Destroyed:
- di
Include:
pcmciaDr.def
Code Display 3-3 Sample DR_PCMCIA_CHECK_SOCKET Routine
SampIeCheckSocket proc far
socket local word push cx
uses ax, bx, cx, dx, si
.enter
;
; Wait to make sure we’ve received all the artificial insertion
; events so we know whether we support the card.
;
waitForRegistrationLoop:
tst ds:[amRegistered]
jz waitForRegistrationLoop
;
; See if we support the thing.
;
call SampUDerefSocket
CheckHack <SCS_NO eq 0>
tst ds:[bx].SSI_support
jnz processIt
fail:
clc ; not our card
done:
.leave
ret
processIt:
;
; If the card has been configured by someone outside of this driver,
; here’s where you’d fetch the configuration info from Card Services
; and tell other people about it so the thing can be used within
; GEOS.
;
PrintMessage <INSERT CODE HERE>
;
; Register with the library, finally.
; bx <- geode handle
; cx <- socket
; dx <- cs handle
; es:di <- CSRegisterClientArgs
; ax:si <- cs callback
;
mov bx, vseg regArgList
call MemLockFixedOrMovable
mov es, ax
mov di, offset regArgList
mov bx, handle 0
mov cx, ss:[socket]
segmov ds, dgroup, ax
mov dx, ds:[csHandle]
mov ax, segment SampCardServicesCallback
mov si, offset SampCardServicesCallback
call PCMCIARegisterDriver
mov bx, vseg regArgList
call MemUnlockFixedOrMovable
stc ; happy happy happy
jmp done
SampIeCheckSocket endp
- ``DR_PCMCIA_OBJECTION_RESOLVED’’
The PCMCIA library calls this function when the user has answered an objection raised to the removal of a card. The function passes a PCMCIAObjectionResolution type in dx indicating the nature of the resolution. If that value is PCMOR_CLEAN_UP , then the user has asked that the card be ejected. The driver should attempt to remove any references to the card.PCMCIAObjectionResolution etype word, 0, 1 PCMOR_CLEAN_UP enum PCMCIAObjectionResolution PCMOR_USER_CANCELED enum PCMCIAObjectionResolution PCMOR_SYSTEM_CANCELED enum PCMCIAObjectionResolution
If the user of system cancelled the removal, the driver should simply note that the objection has been resolved.
A driver should always respond to the PCMOR_CLEAN_UP event, even if it did not raise an objection, as the user may not have actually removed the card. This allows the driver to clean up before a card is removed (for example if the user is initiating an ejection of the card through software control).
Pass:
- cx -> Socket number.
- dx -> PCMCIAObjectionResolution.
- di -> DR_PCMCIA_OBJECTION_RESOLVED
Returns:
- CF -> (Only meaningful if PCMOR_CLEAN_UP was passed) Clear if the driver was able to remove all references to the card; set otherwise.
Destroyed:
- di
Include:
pcmciaDr.def
Code Display 3-4 Sample DR_PCMCIA_OBJECTION_RESOLVED Routine
SampleObjectionResolved proc far
.enter
CheckHack <PCMOR_CLEAN_UP eq 0>
tst_clc dx
jz attemptCleanUp
;
; In theory, since the removal was canceled and the card is back, we’d
; release any access blocks we might have placed, allowing the card to
; be reached again.... we currently set no blocks, though, so this
; is a nop.
;
PrintMessage <MAYBE INSERT CODE HERE>
done:
.leave
ret
attemptCleanUp:
;
; Here’s where you’d try to get the things that are using the card to
; stop using them. Sometimes you can’t do that, in which case you
; return carry set if the card is still in-use. If you can, though,
; try for a bit and occasionally call SampCSCheckCardInUse to see if
; the card’s still in use.
;
PrintMessage <INSERT CODE HERE>
call SampCSCheckCardInUse
jmp done
SampleObjectionResolved endp
DR_PCMCIA_CLOSE_SOCKET
The PCMCIA library calls this function when it is about to close a socket; the driver should respond by cleaning up any auxiliary structures created during DR_PCMCIA_CHECK_SOCKET . The PCMCIA library only sends this function if no one has objected to the removal of the card. The driver, at this point, is about to be unloaded.
Pass:
- cx -> Socket nuber.
- di -> DR_PCMCIA_CLOSE_SOCKET
Returns:
- Nothing
Destroyed:
- di
Include:
- pcmciaDr.def
Code Display 3-5 Handling DR_PCMCIA_CLOSE_SOCKET
SampleCloseSocket proc far
uses ax, di, ds, bx, cx
.enter
;
; Here you’d tell the rest of the world that the thing no longer
; exists. At this point, we know the card wasn’t being used, and
; things should have been done in SampleHandleRemoval to ensure that
; no one could start using the card after that routine returned.
;
PrintMessage <INSERT CODE HERE>
.leave
ret
SampleCloseSocket endp
DR_PCMCIA_DEVICE_ON
The PCMCIA library calls this function in response to a request (by the power management driver) to turn power on to an indicated socket. This may occur when someone wishes to turn on a socket and the library believes that power is off (for example, after a DR_PCMCIA_DEVICE_OFF function or a CSEC_CARD_INSERTION event). The driver may either call PCMCIASocketOn or its own custom function in response to this request.
Only drivers that invoke the Card Services CSF_REQUEST_CONFIGURATION function will receive this function. (Drivers may also steal configuration ownership through PCMCIAChangeConfigurationOwner.)
Pass:
- cx -> Socket number.
- di -> DR_PCMCIA_DEVICE_ON
Returns:
- Nothing.
Destroyed:
- di
Include: pcmciaDr.def
DR_PCMCIA_DEVICE_OFF
The PCMCIA library calls this function in response to a request to turn power off to the indicated socket. (The library ensures that a sufficient time elapses without a subsequent request to turn the power on.) The driver may either call PCMCIASocketOff or its own custom function in response to this request.
Only drivers that invoke the Card Services CSF_REQUEST_CONFIGURATION function will receive this function. (Drivers may also steal configuration ownership through PCMCIAChangeConfigurationOwner.)
Pass:
- cx -> Socket number.
- di -> DR_PCMCIA_DEVICE_OFF
Returns:
- Nothing.
Destroyed:
- di
Include: pcmciaDr.def
3.2 PCMCIA Library Functions
As noted, a PCMCIA driver will interact with both a PCMCIA library and, through that library, CardServices. The PCMCIA library provides a number of routines to aid in communicating with CardServices. ___
PCMCIARegisterDriver
This routine registers a PCMCIA device driver in the indicated socket. This routine is usually called after the driver is first called with DR_PCMCIA_CHECK_SOCKET for each supported card.
Pass:
- cx -> The socket number.
- bx -> The driver’s GeodeHandle.
- es:di -> CSRegisterClientArgs passed to CardServices.
- ax:si -> The CardServices callback routine.
- dx -> The CardServices client handle for the driver.
Returns:
- Nothing.
Destroyed:
- ax
Include: pcmcia.def
PCMCIAObjectToRemoval
This routine notes a driver’s objection to the removal of a card from the device. In calling this routine, the driver is dedicated to wait for a DR_PCMCIA_OBJECTION_RESOLVED function before taking further action with the card.
Pass:
- cx -> The socket number.
- dx -> Set (non-zero) if the card is non-removable.
- bp -> Handle of the driver.
Returns:
- Nothing.
Destroyed:
- ax, di
Include: pcmcia.def
PCMCIAExclusiveGranted
This routine should be called to acknowledge that a driver has received a CSEC_EXCLUSIVE_COMPLETE.
Pass:
- bx -> Handle of the driver geode.
Returns:
- Nothing.
Destroyed:
- Nothing.
Include: pcmcia.def
3.3 CardServices Functions
A driver must contact CardServices through use of CardServicesFunction types defined in pcmcia.def. Consult that file for a complete list of all possible function calls, as well as pass and return information for those calls.
The following definitions are for those function types that a driver must use in registering and deregistering your driver with CardServices. Registration with CardServices should be accomplished in your driver’s DR_INIT handler. Deregistration should be performed within your driver’s DR_EXIT handler.
All CardServicesFunction types return the carry flag set if they encounter an error and return a CardServicesReturnCode in ax. These return codes are enumerated below:
CardServicesReturnCode etype word, 0, 1
CSRC_SUCCESS enum CardServicesReturnCode
CSRC_BAD_ADATPER enum CardServicesReturnCode
; (sic)
CSRC_BAD_ATTRIBUTE enum CardServicesReturnCode
CSRC_BAD_BASE enum CardServicesReturnCode
CSRC_BAD_EDC enum CardServicesReturnCode
CSRC_RESERVED_1 enum CardServicesReturnCode
CSRC_BAD_IRQ enum CardServicesReturnCode
CSRC_BAD_OFFSET enum CardServicesReturnCode
CSRC_BAD_PAGE enum CardServicesReturnCode
CSRC_READ_FAILURE enum CardServicesReturnCode
CSRC_BAD_SIZE enum CardServicesReturnCode
CSRC_BAD_SOCKET enum CardServicesReturnCode
CSRC_RESERVED_2 enum CardServicesReturnCode
CSRC_BAD_TYPE enum CardServicesReturnCode
CSRC_BAD_VCC enum CardServicesReturnCode
CSRC_BAD_VPP enum CardServicesReturnCode
CSRC_RESERVED_3 enum CardServicesReturnCode
CSRC_BAD_WINDOW enum CardServicesReturnCode
CSRC_WRITE_FAILURE enum CardServicesReturnCode
CSRC_RESERVED_4 enum CardServicesReturnCode
CSRC_NO_CARD enum CardServicesReturnCode
CSRC_UNSUPPORTED_FUNCTION enum CardServicesReturnCode
CSRC_UNSUPPORTED_MODE enum CardServicesReturnCode
CSRC_BAD_SPEED enum CardServicesReturnCode
CSRC_BUSY enum CardServicesReturnCode
CSRC_GENERAL_FAILURE enum CardServicesReturnCode
CSRC_WRITE_PROTECTED enum CardServicesReturnCode
CSRC_BAD_ARG_LENGTH enum CardServicesReturnCode
CSRC_BAD_ARGS enum CardServicesReturnCode
CSRC_CONFIGURATION_LOCKED enum CardServicesReturnCode
CSRC_IN_USE enum CardServicesReturnCode
CSRC_NO_MORE_ITEMS enum CardServicesReturnCode
CSRC_OUT_OF_RESOURCE enum CardServicesReturnCode
CSRC_BAD_HANDLE enum CardServicesReturnCode
CSF_REGISTER_CLIENT
This function instructs CardServices to register the driver. This function must be passed a structure of CSRegisterClientArgs containing (among other things) the address of the callback routine with which CardServices should contact the driver. CardServices will send CardServicesEventCode types to this callback routine. (For more information on defining your callback routine see “CardServices Events”.)
This registration should occur when the driver is first loaded, upon receipt of DR_INIT. ~~~ CSRegisterClientArgs struct CSRCA_attributes CSRegisterClientArgsAttributes CSRCA_eventMask CSEventMask CSRCA_clientData CSClientData CSRCA_version word CSRegisterClientArgs ends
CSRegisterClientArgsAttributes record :11 CSRCAA_ARTIFICIAL_EXCLUSIVE:1 ; want artificial INSERTION events ; after exclusive access released CSRCAA_ARTIFICIAL_SHARED:1 ; want artificial INSERTION events for all ; cards resident when client registers CSRCAA_IO:1 ; I/O cards CSRCAA_MTD:1 ; Memory Technologie Driver CSRCAA_MCD:1 ; Memory cards CSRegisterClientArgsAttributes
CSEventMask record :5 CSEM_SOCKET_SERVICES_UPDATED:1 CSEM_RESET:1 CSEM_POWER_MANAGEMENT_CHANGE:1 CSEM_CARD_DETECT_CHANGE:1 CSEM_READY_CHANGE:1 CSEM_BATTERY_LOW:1 CSEM_BATTERY_DEAD:1 CSEM_INSERTION_REQUEST:1 CSEM_EJECTION_REQUEST:1 CSEM_CARD_LOCK_CHANGE:1 CSEM_WRITE_PROTECT_CHANGE:1 CSEventMask end
CSClientData struct CSCD_data word ; DI for callback CSCD_segment word ; DS for callback CSCD_offset word ; SI for callback CSCD_extra word ; reserved word that’s not ; loaded into anything… CSClientData ends
__Pass:__
- al -> CSF_REGISTER_CLIENT
- cx -> Argument length
- es:bx -> CSRegisterClientArgs
- di:si -> Entry point (callback routine) of the driver
__Returns:__
- CF -> Set if failure
- ax -> CardServicesReturnCode
- dx -> Client handle
__Include:__
pcmcia.def
___
+ ``CSF_DEREGISTER_CLIENT``
This function instructs CardServices to deregister the driver. This function
must be passed the client handle returned when the driver first registered
with CardServices.
This deregistration should occur when the driver is unloaded, upon receipt of
DR_EXIT.
__Pass:__
- al -> CSF_DEREGISTER_CLIENT
- dx -> Client handle.
- cx -> No arguments.
__Returns:__
- CF -> Set if failure
- ax -> CardServicesReturnCode
__Include:__
pcmcia.def
___
+ ``CallCS``
``CallCS <command, options>``
This macro issues a call to CardServices. It must be passed a
CardServicesFunction to invoke.
Due to interrupt timing concerns, if the macro is called from within a
CardServices callback procedure (or from a routine that is called by such a
procedure), DONT_LOCK_BIOS must be passed as an option. At all other
times you must not pass DONT_LOCK_BIOS (unless you call SysLockBIOS
yourself) as CardServices is not re-entrant.
### 3.4 CardServices Events
The interface between CardServices and your driver occurs not only through
use of the PCMCIA library; your driver must also handle events sent by
CardServices as well. This is performed through use of a callback routine.
CardServices will send these events using either a timer interrupt or a
status-change interrupt (such as a physical card insertion or removal).
When your driver registers with CardServices, it must pass the address of a
callback routine. Your driver should respond to CardServicesEventCode
types sent to this callback routine and return appropriate
CardServicesReturnCode types.
The CardServicesEventCode functions pass the following arguments to
your callback routine:
- al -> CardServicesEventCode
- cx -> Socket number
- dx -> Info
- di -> di register for callback routine
- ds -> ds register for callback routine
- si -> si register for callback routine
- ss -> MTD request segment
- bp -> MTD request offset
- es -> Buffer segment
- bx -> Buffer offset or miscellaneous register
As with any CardServices functions, you should return the carry flag set if
you encounter an error and a CardServicesReturnCode in ax.
___
+ ``CSEC_CARD_INSERTION``
A driver receives this event when CardServices determines that a card has
been inserted in a PCMCIA socket. The driver should respond by configuring
its card in whatever way it sees fit. A driver may also receive this event if
some other client received exclusive access to the card and is now
relinquishing it. This is transparent to the driver receiving this event.
If a driver receives this event while an unresolved objection to a previous
removal is currently active, it should reconfigure the card to its previous
state before the objection to removal was noted, if possible; it must also wait
to release any blocks (containing the “unresolved” information) until a
DR_PCMCIA_OBJECTION_RESOLVED function is received.
Code Display 3-6 Sample CSEC_CARD_INSERTION Handler
SampleHandleInsertion proc near uses bx, dx .enter
;
; Point to our data record for the socket.
;
call SampUDerefSocket
;
; Here’s where you’d examine the card’s CIS to see if it’s something
; you support, then attempt to set it to one of its configurations.
; If all that succeeds, you’d set ds:[bx].SSI_support to SCS_YES.
; If any of that fails, you’d set ds:[bx].SSI_support to SCS_NO.
;
PrintMessage <INSERT CODE HERE>
setYes: mov ds:[bx].SSI_support, SCS_YES
;
; See if the card was removed under protest.
;
tst ds:[bx].SSI_conflict
jz clearConflict
;
; If this card is coming back in after having been removed while
; in-use you may need to tell another driver to restore the state of the
; card (this is what happens in the CIDSer driver, for example, where the
; baud rate and other parameters need to be restored here).
;
; If you block people’s access to the card while it’s in conflict,
; this is the time to wake them all up using code like this:
;
; call SysEnterCritical
; VAllSem ds, [bx].SSI_conflictSem
; mov ds:[bx].SSI_conflictSem.Sem_value, 0
; call SysEnterCritical
;
; The Enter/ExitCritical prevents other threads from running so we can
; reliably set the Sem_value to 0 (it ends up at 1) to cause people
; to block immediately the next time the card is in conflict.
;
PrintMessage <INSERT CODE HERE>
clearConflict: mov ds:[bx].SSI_conflict, 0
done: .leave ret
setNo: mov ds:[bx].SSI_support, SCS_NO jmp done
SampleHandleInsertion endp
___
+ ``CSEC_CARD_REMOVAL``
A driver receives this event when Card Services determines that the card has
been removed. If this removal is acceptable, the driver should release all
Card Services-related resources that it had allocated and make sure not to
access the card in the future.
A client may also receive this event if another client has been granted
exclusive access to the card. When the other driver relinquishes exclusive
access, the previously contacted drivers will receive CSEC_CARD_INSERTION
events.
If a driver has been granted exclusive access to a card and receives a
CSEC_CARD_REMOVAL event, it should call
PCMCIAExclusiveCardRemoved. If instead, the driver wishes to raise an
objection to this card removal (for example, a serial port was in use or a file
is currently open on the card) it should call the PCMCIAObjectToRemoval
library utility routine. The driver must then wait for the objection to be
resolved (through a DR_PCMCIA_OBJECTION_RESOLVED event). If the
driver receives a fresh CSEC_CARD_INSERTION event, it should reconfigure
the card if it is able. It should not grant access to the card until
DR_PCMCIA_OBJECTION_RESOLVED is received.
Code Display 3-7 Sample CSEC_CARD_REMOVAL Handler
SampleHandleRemoval proc near uses bx, dx, bp .enter
call SampUDerefSocket
;
; If card not supported, we’re happy to see it go (why are we here?)
;
tst ds:[bx].SSI_support
jz resetSupport
;
; See if the card is being used by GEOS using whatever means are
; available/appropriate.
;
PrintMessage <INSERT CODE HERE>
call SampCSCheckInUse
jne resetSupport
;
; It is in-use, so mark the port conflicted and tell the PCMCIA library
; of our objections.
;
mov ds:[bx].SSI_conflict, TRUE
mov dx, TRUE ; card may NOT be removed
mov bp, handle 0
call PCMCIAObjectToRemoval
resetSupport: ; ; Always set SSI_support back to SCS_NO, as it reflects our opinion of ; the current state of the socket. ;
mov ds:[bx].SSI_support, SCS_NO
clc
mov ax, CSRC_SUCCESS
.leave
ret
SampleHandleRemoval endp
___
+ ``CSEC_EXCLUSIVE_COMPLETE``
A driver receives this event when CardServices grants a client driver
exclusive access to the PCMCIA socket. The driver should acknowledge that
it has received the event by calling PCMCIAExclusiveGranted.
___
+ ``CSEC_EXCLUSIVE_REQUEST``
A driver receives this event when CardServices requests, at the behest of
another driver, exclusive access to the card. The driver should react
negatively to this event if it objects to this exclusive access. The criteria for
this objection should be much the same as if it had received a
CSEC_CARD_REMOVAL event.
Code Display 3-8 Sample CSEC_EXCLUSIVE_REQUEST Handler
SampCSHandleExclusiveRequest proc near uses bx, di .enter
call SampCSCheckCardInUse
jnc done
;
; Card is in use - don’t allow the exclusive access.
;
mov ax, CSRC_IN_USE
stc
done: .leave ret SampCSHandleExclusiveRequest endp
___
+ ``CSEC_CLIENT_INFO``
A driver receives this event when CardServices requests standard client
information.
Code Display 3-9 Sample CSEC_CLIENT_INFO Handler
; ; Remember that this is not a complete routine. ;
doInfo: test es:[bx].CSGCIA_attributes, mask CSGCIAA_INFO_SUBFUNCTION jnz unsupported ; only handle function 0
;
; Return info about this client to whomever is asking.
;
mov cx, cs:[clientInfo].CSGCIA_infoLen
cmp cx, es:[bx].CSGCIA_maxLen
jbe copyInfo
mov cx, es:[bx].CSGCIA_maxLen
copyInfo: segmov ds, cs mov si, offset clientInfo.CSGCIA_infoLen lea di, es:[bx].CSGCIA_infoLen sub cx, offset CSGCIA_infoLen ; not copying all stuff ; up to here rep movsb jmp success
The following is a complete list of CardServicesEventCode routines
defined in pcmcia.def.
CardServicesEventCode etype word CSEC_PM_BATTERY_DEAD (001h) CSEC_PM_BATTERY_LOW (002h) CSEC_CARD_LOCK (003h) CSEC_CARD_READY (004h) CSEC_CARD_REMOVAL (005h) CSEC_CARD_UNLOCK (006h) CSEC_EJECTION_COMPLETE (007h) CSEC_EJECTION_REQUEST (008h) CSEC_INSERTION_COMPLETE (009h) CSEC_INSERTION_REQUEST (00ah) CSEC_PM_RESUME (00bh) CSEC_PM_SUSPEND (00ch) CSEC_EXCLUSIVE_COMPLETE (00dh) CSEC_EXCLUSIVE_REQUEST (00eh) CSEC_RESET_PHYSICAL (00fh) CSEC_RESET_REQUEST (010h) CSEC_CARD_RESET (011h) CSEC_MTD_REQUEST (012h) CSEC_RESERVED_1 (013h) CSEC_CLIENT_INFO (014h) CSEC_TIMER_EXPIRED (015h) CSEC_SS_UPDATED (016h)
CSEC_CARD_INSERTION (040h)
CSEC_RESET_COMPLETE (080h) CSEC_ERASE_COMPLETE (081h) CSEC_REGISTRATION_COMPLETE (082h)
Your driver will need to create a table to map these event codes to the
handlers to invoke for each.
Note that the CSEC_CARD_INSERTION , CSEC_RESET_COMPLETE ,
CSEC_ERASE_COMPLETE and CSEC_REGISTRATION_COMPLETE events do
not follow the simple incremental numbering of the previous events. You will
need to check for these events individually, rather than through a simple
jump table.
Your handler should respond with an appropriate CardServicesReturnCode.
For example, the sample PCMCIA driver included on the SDK defines the
following table:
Code Display 3-10 A Sample CardServices Event Table
; It is usually convenient to define such a table within the Callback
; routine itself.
DefCSEvent macro event, handler .assert ($-eventRoutineTable)/2 eq (event-1) nptr.near handler endm
eventRoutineTable label nptr DefCSEvent CSEC_PM_BATTERY_DEAD, doIgnore DefCSEvent CSEC_PM_BATTERY_LOW, doIgnore DefCSEvent CSEC_CARD_LOCK, doIgnore DefCSEvent CSEC_CARD_READY, doIgnore DefCSEvent CSEC_CARD_REMOVAL, doRemoval DefCSEvent CSEC_CARD_UNLOCK, doIgnore DefCSEvent CSEC_EJECTION_COMPLETE, doIgnore DefCSEvent CSEC_EJECTION_REQUEST, doIgnore DefCSEvent CSEC_INSERTION_COMPLETE, doIgnore DefCSEvent CSEC_INSERTION_REQUEST, doIgnore DefCSEvent CSEC_PM_RESUME, doIgnore DefCSEvent CSEC_PM_SUSPEND, doIgnore DefCSEvent CSEC_EXCLUSIVE_COMPLETE, doIgnore DefCSEvent CSEC_EXCLUSIVE_REQUEST, doExclusiveReq DefCSEvent CSEC_RESET_PHYSICAL, doIgnore DefCSEvent CSEC_RESET_REQUEST, doIgnore DefCSEvent CSEC_CARD_RESET, doIgnore DefCSEvent CSEC_MTD_REQUEST, unsupported DefCSEvent CSEC_RESERVED_1, unsupported DefCSEvent CSEC_CLIENT_INFO, doInfo DefCSEvent CSEC_TIMER_EXPIRED, doIgnore DefCSEvent CSEC_SS_UPDATED, doIgnore endEventRoutineTable label nptr
Code Display 3-11 A Sample PCMCIA CardServices Callback Routine
COMMENT @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% SampCardServicesCallback %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SYNOPSIS: Callback routine for Card Services events
CALLED BY: Card Services
PASS: al -> function cx -> socket dx -> info di -> 1st word in RegisterClient ds -> dgroup (2nd word in RegisterClient) si -> 3rd word in RegisterClient ss:bp -> MTDRequest es:bx -> buffer bx -> Misc (when no buffer returned)
RETURN: ax <- status to return carry set on error, carry clear on success.
DESTROYED: nothing
SIDE EFFECTS: None
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% SampCardServicesCallback proc far uses cx, dx .enter
; We need to check for the events which can’t be included in a linear
; sequential jump table
cmp al, CSEC_CARD_INSERTION
je doInsertion
cmp al, CSEC_REGISTRATION_COMPLETE
jne handleEvent
; We’re registered, so we should note this in our driver’s state variable
mov ds:[amRegistered], TRUE
jmp success
; Now we handle the other events
handleEvent:
clr ah
mov di, ax
shl di
cmp di, endEventRoutineTable - eventRoutineTable
ja unsupported
; We need to subtract 2 from the value of di since the events are
; one-based, not zero-based.
jmp cs:[eventRoutineTable][di-2]
; For each “routine” mentioned in the table, a label should appear
; following this jump
;
; Example:
doExclusiveReq: call SampCSHandleExclusiveRequest jmp done
;-------------------- doInsertion:
call SampHandleInsertion
jmp success
doIgnore:
success: mov ax, CSRC_SUCCESS clc
done: .leave ret
;--------------------
;
; The description of what this client supports, when it was created,
; etc.
;
clientInfo CSGetClientInfoArgs < 0, ; CSGCIA_maxLen size clientInfo, mask CSGCIAA_EXCLUSIVE_CARDS or
mask CSGCIAA_SHARABLE_CARDS or
mask CSGCIAA_MEMORY_CLIENT_DEVICE_DRIVER, < ; CSGCIA_clientInfo 0100h, ; CSCI_revision 0201h, ; CSCI_csLevel < 29, ; CSDI_YEAR 9, ; CSDI_MONTH 22 ; CSDI_DAY >, ; CSCI_revDate clientInfoName - clientInfo, ; CSCI_nameOffset length clientInfoName, ; CSCI_nameLength vendorString - clientInfo, ; CSCI_vStringOffset length vendorString ; CSCI_vStringLength >
org clientInfo.CSGCIA_clientInfo.CSCI_data ; go back into the ; middle of the struct ; to place these ; strings in the right ; place
clientInfoName char “Sample PCMCIA Driver”, 0 vendorString char “Geoworks”, 0
; Your event table should appear here...
SampCardServicesCallback endp ~~~