Programming for portable systems
Troy runs Entertainment Software Partners and can be reached via the Internet at [email protected].
The Personal Computer Memory Card International Association (PCMCIA) 1.0 specification defined a standard for adding 68-pin memory cards to a computer system. PCMCIA 2.0 added support for I/O devices such as modems, pagers, LAN cards, and hard disks. Unlike their ISA and MCA cousins, system- and CPU-independent PCMCIA cards don't have jumpers or dip switches. Instead, they're configured via software and can be inserted and extracted with the system powered.
Nearly all portable DOS machines now include PCMCIA slots. Additionally, both the IBM DOS and OS/2 operating systems include PCMCIA support (and it's rumored that forthcoming versions of Microsoft MS-DOS and Windows will, too). This article examines what's involved in writing PCMCIA software for DOS-based systems.
PCMCIA Hardware Architecture
The first layer of PCMCIA hardware is the socket, a receptacle for a PCMCIA card. There's no limit to the number of sockets a system can have, although one, two, and four are the most common. Two or more sockets are usually joined to an adapter, which is hooked into the system bus. Unlike hard disks or modems, there are no standard I/O addresses used by adapters. Nor are there any standard mappings for the registers on the adapters. All of this is left to the system designer. The adapter allows the card's memory and I/O resources to be mapped into the system under software control. For example, a modem card can be made to appear at any available COM port by executing the appropriate code. PCMCIA cards are about the size of a credit card. Type I cards are the thinnest and are mainly used for memory cards. Type IIs are thicker and used mostly for modems and LANs. Type IIIs are the thickest and are used by rotating hard drives.
PCMCIA cards have both common and attribute on-board memory. Common memory stores data and is usually present only in memory cards. Attribute memory, present on all 2.0-compliant cards, is where information about the card is kept. The card information structure (CIS), beginning at address 0 in attribute memory, consists of a linked list of data blocks called "tuples" which hold information about the type of card and the information the software needs to configure it.
PCMCIA Software Architecture
PCMCIA software consists of socket and card services. Socket services, the lowest layer (similar to BIOS), is written specifically to support a single type of PCMCIA adapter. It is intended to be small and needs minimal RAM, allowing it to be ROMable. Card services, on the other hand, is more like DOS. It never deals directly with the hardware, using socket services, instead. Card services is the API most important to programmers. While it is possible to bypass card services and go directly to socket services, doing so could be dangerous. Card and socket services are tightly synchronized. If this synchronization is lost, a system crash or hardware damage can result. If you make direct calls to socket services, you'd better know what you're doing.
Card Services API
There are 54 card-services functions, divided into five types: client services, resource management, bulk memory, client utilities, and advanced client services. Client-service functions get information from card services. Resource-management functions allocate and free system resources. (This is how PCMCIA card resources are made visible to the rest of the system.) Bulk-memory services are for accessing common memory, and client utilities parse tuple information. Advanced client-service functions get even lower level information from card services.
All of the functions are called with a similar argument. In C, a card-services call has the form: results=CardServices(function, handle, pointer, arglength, argpointer); in which function is a byte that indicates the card-services function number, handle is a word used to send or receive a client handle, and pointer is a far pointer to code or data (for most functions, pointer is NULL). arglength holds the size of the buffer that argpointer points to. argpointer is a far pointer to an argument packet which sends/receives information from card services. If a card-services call is successful, results will be 0; otherwise it holds an error number. Some functions return additional error information in the argument packet.
Since a bad PCMCIA program can damage the hardware, card services does a lot of checking on parameters passed to it. If any of the values are determined to be bad, the function call will fail before any action is taken. It is critical to always check the values card services returns because, unlike normal DOS programming, PCMCIA is dynamic. Hardware can be added and removed from the system at any time. You should never make assumptions about the state of the system.
The Cardinfo Program
The Cardinfo program presented in Listing One (page 152) returns information about cards plugged into the first or second socket of the system. It is written mostly in C, although two functions are written in inline assembly. I've tested Cardinfo on Toshiba, IBM, AST, and Compuadd notebooks, using both Phoenix and SystemSoft PCMCIA software.
The most important function in this program is CardServices, which encapsulates the call to card services with a C-callable function. Under DOS, card services is called via interrupt 0x1A, function 0xAF. The value in AL holds the number of the desired card-services function. Other registers hold the other arguments. Putting this call into a C function and its argument packet in a structure is easier to manage than manipulating bytes in assembly.
GET_CARDSERVICES_INFO is the first call every PCMCIA program should make, since nothing will work without card services. If card services is installed, the return value will be SUCCESS (or 0) and the signature word of the argument packet will be set to CS. Both values must be checked, since other functions use interrupt 0x1A, and any of those can accidentally zero the AX register which holds the return value.
Besides confirming card-services installation, GET_CARDSERVICES_INFO returns the number of PCMCIA sockets, the vendor revision number, and the version of card services. All this information is displayed before the program enters its main loop. In the 2.0 version of card services, sockets were numbered beginning with 1. In version 2.01 and later, socket numbering begins with 0. Cardinfo always begins its numbering with 1. It determines what the base socket is by checking the PCMCIA version.
Since PCMCIA is a dynamic environment, card services provides a callback mechanism for notifying you of card events as they occur. To use the callback, a call is made to REGISTER_CLIENT. The argument packet first contains the attribute which tells card services what kind of client your program is. This allows card services to set your priority level, since there may be more than one client in the system. Priority is given first to I/O clients, then to memory-technology drivers, and finally to memory clients. The Cardinfo program case assumes a memory client which will get notification after all other clients. The next piece of information is the event mask, which tells card services what kind of events you are interested in. We are only interested in detecting changed events, indicated by setting bit 7. The last piece of information is the version of card services the program was written to be compliant with. Since it's compliant with both version 2.0 and 2.01, the program returns the version number it gave us to card services.
If the request for a callback is granted, card services returns with SUCCESS, and ClientHandle will hold the client handle, which is used later to free the callback. Callbacks are a limited system resource, so not all requests will be granted. Be sure to check the value that is returned.
If the callback request is granted, the program enters the information-display loop. The first pass through the loop will force an update by initially setting fCardEvent to True. Afterwards, fCardEvent will be True only after an event occurs.
Card services makes getting informa-tion from a card simple. A call to GET_STATUS will return the state of a socket. If a card is in the socket, the CARD_DETECT_FLAG of the CardState word will be set. The version 1.0 information tuple (CISTPL_VERS_1) contains the manufacturer and product-name strings. The GET_FIRST_TUPLE and GET_TUPLE_DATA card services calls returns these strings in the GetLevel1Info function. The strings themselves will be in a buffer with NULLs separating them and a double NULL marking the end of the buffer.
A call to GetDeviceType is made to determine the type of card. It makes calls to GET_FIRST_TUPLE and GET_TUPLE_DATA also, except the desired tuple is CISTPL_DEVICE. Only memory types are defined. If it is an I/O card, the device type will be DTYPE_NULL or DTYPE_FUNCSPEC. Memory devices also return their size in bytes. If the memory type is DTYPE_SRAM, the state of the device's battery is contained in wState.
The information-display loop continues to wait for card events until the user presses a key. At that point, the callback is free, and the program returns to DOS. If the callback isn't free, card services will call it after the next card event and crash the system.
Conclusion
There are many ways to enhance Cardinfo. For instance, you could have it tell the user what kind of I/O card is being used by comparing the I/O addresses used by the card with those of known PC devices.
Finally, keep in mind that the PCMCIA specification is an open standard. If you'd like more information on it, contact the Personal Computer Memory Card International Association, 1030G E. Duane Ave., Sunnyvale, CA 94086 (408-720-0107).
[LISTING ONE]
//*** CARDINFO.C -- by Troy-Anthony Miles -- A PCMCIA Information Utility *** #include <dos.h> #include <conio.h> #include <stdio.h> #include <ctype.h> #include <string.h> #include "cardinfo.h" #define MAX_VENDOR_STRING (80-10) #define MAX_SOCKET 2 BYTE buffer[256], heap[256]; WORD ClientHandle=0, fCardEvent=TRUE, bEventType=0; WORD wPCMCIAVersion, wVendorVersion, wSockets, wBaseSocket; DWORD SizeCodes[] = { 0x200, //* 512 bytes 0x800, //* 2k 0x2000, //* 8k 0x8000, //* 32k 0x20000, //* 128k 0x80000, //* 512k 0x200000 //* 2m }; char *OldCard = "Information Unavailable"; char *strLines[] = {" product:", " line 3:", " line 4:"}; char *strDevices[] = {"ROM","OTPROM","EPROM","EEPROM","FLASH","SRAM","DRAM"}; WORD CardServices(WORD, WORD, DWORD, WORD, void far *); WORD GetLevel1Info(WORD socket, char *buffer); WORD GetDeviceType(WORD socket, DWORD *size, char *buffer); WORD SetCallbackHandler(void); void far Callback(void); char *CreateMemoryString(DWORD value); void FilterString(char *string); void SetCursorPos(WORD x, WORD y); void ClearScreen(void); //******************************************************************* void main() { STATUS_INFO *statInfo = (STATUS_INFO *)heap; CS_INFO *csInfo = (CS_INFO *)heap; DWORD dwSize; WORD wState, wStatus, wType; ClearScreen(); SetCursorPos(0, 0); puts("CardInfo - A PCMCIA Information Utility"); //* See if CS is installed and get basic information csInfo->Signature = 0; wStatus = CardServices(GET_CARDSERVICES_INFO,0,0L,sizeof(CS_INFO),csInfo); if(wStatus != SUCCESS || csInfo->Signature != SIGNATURE) { puts("ERROR: Card Services not installed."); return; } SetCursorPos(0, 2); wSockets = csInfo->Count; wVendorVersion = csInfo->Revision; wPCMCIAVersion = csInfo->CSLevel; //* after v2.0 socket numbering began at 0, before it was 1 wBaseSocket = (wPCMCIAVersion > 0x200)? 0: 1; //* Display the basic information printf("PCMCIA version: %X.%02X, Vendor version: %X.%02X, Sockets: %d", HIBYTE(wPCMCIAVersion), LOBYTE(wPCMCIAVersion), HIBYTE(wVendorVersion), LOBYTE(wVendorVersion), wSockets); //* Try to get a callback handle, exit if can't get one if(SetCallbackHandler()) { puts("ERROR: Unable to allocate a callback handle."); return; } //* Display the Vendor Information string SetCursorPos(0, 3); if(csInfo->VStrLen) { FilterString(csInfo->VendorString); printf("Vendor: [%s]", csInfo->VendorString); } //* This is the information display loop do { //* Wait until a card event occurs if(fCardEvent) { WORD ndx, wCurrSocket; fCardEvent = FALSE; //* Display the event SetCursorPos(0, 4); printf("Last Card Event: 0x%02X", bEventType); //* Display info for each socket for(wCurrSocket=wBaseSocket, ndx=1; ndx <= wSockets && ndx <= MAX_SOCKET; wCurrSocket++, ndx++) { int iYLine; //* Erase the socket information for(iYLine=0; iYLine < D_LINES; iYLine++) { SetCursorPos(0, (ndx-1)*D_LINES + D_Y + iYLine); printf(" "); } //* Get the socket state information statInfo->Socket = wCurrSocket; CardServices(GET_STATUS, 0, 0L, sizeof(STATUS_INFO), statInfo); wState = statInfo->CardState; //* Is there a card in the socket? if(wState & CARD_DETECT_FLAG) { WORD wOffset, wNumLines, wCurrLine; //* Display level 1 information wNumLines = GetLevel1Info(wCurrSocket, buffer) - 1; SetCursorPos(0, (ndx-1)*D_LINES + D_Y); printf(" Socket %d: %s", ndx, buffer); //* Loop through and display each line of level 1 info for(wOffset=0, wCurrLine=1; wCurrLine < 4; wCurrLine++) { SetCursorPos(D_X, (ndx-1)*D_LINES + D_Y + wCurrLine); printf("%s", strLines[wCurrLine-1]); wOffset = strlen(&buffer[wOffset]) + wOffset + 1; if(wNumLines) { printf(" %s", &buffer[wOffset]); wNumLines--; } else printf(" %s", "UNDEFINED"); } //* Display type information wType = GetDeviceType(wCurrSocket, &dwSize, buffer); SetCursorPos(D_X, (ndx-1)*D_LINES + D_Y2 + 0); printf("Device Type: %s", buffer); //* Only memory cards have size information if(wType && wType != DTYPE_FUNCSPEC) { SetCursorPos(D_X, (ndx-1)*D_LINES + D_Y2 + 1); printf(" Size: %s", CreateMemoryString(dwSize)); printf(", Write Protect: %s",(wState & WRITE_PROTECT)?"ON": "OFF"); //* Only an SRAM will have a battery if(wType == DTYPE_SRAM) { printf(", Battery: "); if(wState & BATTERY_DEAD_FLAG) printf("Dead"); else if(wState & BATTERY_LOW_FLAG) printf("Low"); else printf("Okay"); } } } else { SetCursorPos(0, (ndx-1)*D_LINES + D_Y); printf(" Socket %d: Empty", ndx); } } } }while(!_kbhit()); //* Flush the keyboard buffer while(_kbhit()) _getch(); //* Deallocate the callback else CS will crash later if(ClientHandle) if(CardServices(DEREGISTER_CLIENT, ClientHandle, 0L, 0, 0L)) puts("ERROR: Unable to free the callback handle."); SetCursorPos(0, 23); } //***** Call CS via its interrupt ***** WORD CardServices(WORD Function, WORD Handle, DWORD Pointer, WORD ArgLength, void far *ArgPointer) { WORD wStatus, wHandle; DWORD pointer; _asm { push es push di push si mov bx, WORD PTR ArgPointer mov ax, WORD PTR ArgPointer+2 mov es, ax mov dx, Handle mov si, WORD PTR Pointer mov di, WORD PTR Pointer+2 mov cx, ArgLength mov al, BYTE PTR Function mov ah, CARD_SUBFUNCTION int CARD_INTERRUPT mov wStatus, ax mov wHandle, dx mov WORD PTR pointer, si mov WORD PTR pointer+2, di pop si pop di pop es } if(Handle && Function == REGISTER_CLIENT) *((WORD *)Handle) = wHandle; return(wStatus); } //*** Receives Card Services callback when a card event occurs sets the flag //* ENTRY: AL - holds the event type //* EXIT: AX - is set to zero to indicate no errors void far Callback() { _asm { push ds mov bx, SEG fCardEvent ;get the data segment mov ds, bx inc fCardEvent ;set the event flag mov BYTE PTR bEventType, al ;save the event type xor ax, ax pop ds clc } } //*** Get Level one information (manufacturer, product name, etc) from card. //* ENTRY: socket - the socket to retrieve the information from //* buffer - a pointer to a buffer which will recieve the info //* it should be at least 256 bytes which is the max tuple length //* EXIT: buffer - hold the info strings, separated by '\0' //* RETURN: the number of strings in buffer WORD GetLevel1Info(WORD socket, char *buffer) { WORD len, ndx1, ndx2, wStatus1, wStatus2, strCount; TUPLE_INFO *pTup; TUPLE_DATA_INFO *pTupData; strCount = 0; //* We want the level 1 info tuple. Find it with GET_FIRST_TUPLE //* Then retrieve its data with GET_TUPLE_DATA pTup = (TUPLE_INFO *)heap; pTupData = (TUPLE_DATA_INFO *)heap; pTup->Socket = socket; pTup->Attributes = 0; pTup->DesiredTuple= CISTPL_VERS_1; pTup->Reserved = 0; wStatus1 = CardServices(GET_FIRST_TUPLE, 0, 0L, sizeof(TUPLE_INFO), pTup); wStatus2 = CardServices(GET_TUPLE_DATA, 0, 0L, sizeof(TUPLE_DATA_INFO), pTupData); //* If data was retrieved successfully, sort it out if(wStatus1 == SUCCESS && wStatus2 == SUCCESS) { ndx1 = 2; ndx2 = 0; while(pTupData->TupleData[ndx1] != 0xFF && strCount < 4) { strCount++; strcpy(&buffer[ndx2], &pTupData->TupleData[ndx1]); len = strlen(&pTupData->TupleData[ndx1]) + 1; ndx2 += len; ndx1 += len; } } //* If an error occurred, Information Unavailable else { strCount++; strcpy(buffer, OldCard); } return(strCount); } //*** Retrieves information from the Device Information tuple. If the //* card is memory, its size is also returned //* ENTRY: socket - the socket to retrieve the information from //* dwSize - a pointer to a DWORD which will receive the device's size //* buffer - a pointer to a buffer which will receive the info //* it should be at least 256 bytes which is the max tuple length //* EXIT: buffer - hold the type string //* RETURN: the device type WORD GetDeviceType(WORD socket, DWORD *dwSize, char *buffer) { WORD wStatus1, wStatus2, wType; TUPLE_INFO *pTup; TUPLE_DATA_INFO *pTupData; *dwSize = 0; //* We want the device tuple. Find it with GET_FIRST_TUPLE //* Then retrieve its data with GET_TUPLE_DATA pTup = (TUPLE_INFO*)heap; pTupData = (TUPLE_DATA_INFO*)heap; pTup->Socket = socket; pTup->Attributes = 0; pTup->DesiredTuple= CISTPL_DEVICE; pTup->Reserved = 0; wStatus1 = CardServices(GET_FIRST_TUPLE, 0, 0L, sizeof(TUPLE_INFO), pTup); wStatus2 = CardServices(GET_TUPLE_DATA, 0, 0L, sizeof(TUPLE_DATA_INFO), pTupData); //* If data was retrieved successfully, sort it out if(wStatus1 == SUCCESS && wStatus2 == SUCCESS) { wType = (WORD)((pTupData->TupleData[0] & 0xF0) >> 4); if(wType != DTYPE_NULL) { if(wType >= DTYPE_ROM && wType <= DTYPE_DRAM) { strcpy(buffer, strDevices[wType - 1]); strcat(buffer, " Memory"); if(pTupData->TupleData[1] != 0xFF) { WORD wSizeCode, wUnits; wSizeCode = (WORD)(pTupData->TupleData[1] & 0x07); wUnits = (WORD)(((pTupData->TupleData[1] & 0xF8) >> 3) + 1); *dwSize = (DWORD)(wUnits * SizeCodes[wSizeCode]); } } else if(wType == DTYPE_FUNCSPEC) { strcpy(buffer, "FUNCTION SPECIFIC"); } } else { strcpy(buffer, "Input/Output"); } } else { strcpy(buffer, OldCard); wType = 0; } return(wType); } //*** Sets the callback handler to our callback function. If status //* is !0, an error occurred. //* ENTRY: none //* EXIT: none WORD SetCallbackHandler(void) { WORD wStatus; REGISTER_CLIENT_INFO pRC; //* We are memory client device driver. We only want card detect events //* We want the version, CS it told us it was pRC.Attributes = 0x0001; pRC.EventMask = 0x0080; pRC.Version = wPCMCIAVersion; wStatus = CardServices(REGISTER_CLIENT, (WORD)&ClientHandle, (DWORD)(void far *)Callback, sizeof(REGISTER_CLIENT_INFO), &pRC); if(wStatus) ClientHandle = 0; return(wStatus); } //*** Converts a DWORD byte value into an ASCII string containing the //* value in normalized byte form //* ENTRY: value - holds the byte value to be converted //* EXIT: //* RETURN: a pointer to the size string. This string will be overwritten //* the next time this function is called char *CreateMemoryString(DWORD value) { static char line[40]; if(value < 0x400L) sprintf(line, "%ld bytes", value); else if(value < 0x100000L) sprintf(line, "%ld KB", value / 0x400L); else sprintf(line, "%ld MB", value / 0x100000L); return(line); } //*** This function limits a string to MAX_VENDOR_STRING characters and //* changes any non-printable characters to '.' //* ENTRY: szString - the string to be filtered //* EXIT: szString - the string filtered string void FilterString(char *szString) { int ndx; for(ndx=0; ndx < MAX_VENDOR_STRING && *szString; ndx++, szString++) { if(!isprint(*szString)) *szString = '.'; } *szString = '\0'; } //*** Set the cursor position using BIOS *** void SetCursorPos(WORD x, WORD y) { _asm { mov ax, 0200h mov bh, 00 mov dl, BYTE PTR x mov dh, BYTE PTR y int 10h } } //*** Clear the screen using BIOS *** void ClearScreen() { _asm { mov ax, 0600h mov bx, 0700h xor cx, cx mov dx, (25-1)*100h+(80-1) int 10h ;Scroll up using BIOS } } //*** CARDINFO.H -- by Troy-Anthony Miles *** //* GENERAL DEFINITIONS typedef unsigned char BYTE; typedef unsigned short int WORD; typedef unsigned long DWORD; #define LOBYTE(w) ((BYTE)(w)) #define HIBYTE(w) ((BYTE)(((WORD)(w) >> 8) & 0xFF)) #define LOWORD(l) ((WORD)(DWORD)(l)) #define HIWORD(l) ((WORD)((((DWORD)(l)) >> 16) & 0xFFFF)) #define FALSE 0 #define TRUE 1 #define CARD_INTERRUPT 0x1A #define CARD_SUBFUNCTION 0xAF #define SIGNATURE 'SC' //* POSITIONS #define D_LINES 7 #define D_Y 6 #define D_Y2 (D_Y + 4) #define D_X 0 //* FUNCTION CODES #define GET_CARDSERVICES_INFO 0x0B #define REGISTER_CLIENT 0x10 #define DEREGISTER_CLIENT 0x02 #define GET_STATUS 0x0C #define RESET_CARD 0x11 #define SET_EVENT_MASK 0x31 #define GET_EVENT_MASK 0x2E #define REQUEST_IO 0x1F #define RELEASE_IO 0x1B #define REQUEST_IRQ 0x20 #define RELEASE_IRQ 0x1C #define REQUEST_WINDOW 0x21 #define RELEASE_WINDOW 0x1D #define MODIFY_WINDOW 0x17 #define MAP_MEM_PAGE 0x14 #define REQUEST_SOCKET_MASK 0x22 #define RELEASE_SOCKET_MASK 0x2F #define REQUEST_CONFIGURATION 0x30 #define GET_CONFIGURATION_INFO 0x04 #define MODIFY_CONFIGURATION 0x27 #define RELEASE_CONFIGURATION 0x1E #define OPEN_MEMORY 0x18 #define READ_MEMORY 0x19 #define WRITE_MEMORY 0x24 #define COPY_MEMORY 0x01 #define REGISTER_ERASE_QUEUE 0x0F #define CHECK_ERASE_QUEUE 0x26 #define DEREGISTER_ERASE_QUEUE 0x25 #define CLOSE_MEMORY 0x00 #define GET_FIRST_TUPLE 0x07 #define GET_NEXT_TUPLE 0x0A #define GET_TUPLE_DATA 0x0D #define GET_FIRST_REGION 0x06 #define GET_NEXT_REGION 0x09 #define GET_FIRST_PARTITION 0x05 #define GET_NEXT_PARTITION 0x08 #define RETURN_SS_ENTRY 0x23 #define MAP_LOG_SOCKET 0x12 #define MAP_PHY_SOCKET 0x15 #define MAP_LOG_WINDOW 0x13 #define MAP_PHY_WINDOW 0x16 #define REGISTER_MTD 0x1A #define REGISTER_TIMER 0x28 #define SET_REGION 0x29 #define VALIDATE_CIS 0x2B #define REQUEST_EXCLUSIVE 0x2C #define RELEASE_EXCLUSIVE 0x2D #define GET_FIRST_CLIENT 0x0E #define GET_NEXT_CLIENT 0x2A #define GET_CLIENT_INFO 0x03 #define ADD_SOCKET_SERVICES 0x32 #define REPLACE_SOCKET_SERVICES 0x33 #define VENDOR_SPECIFIC 0x34 #define ADJUST_RESOURCE_INFO 0x35 //* FLAGS #define WRITE_PROTECT 0x01 #define BATTERY_DEAD_FLAG 0x10 #define BATTERY_LOW_FLAG 0x20 #define CARD_DETECT_FLAG 0x80 //* RETURN CODES #define SUCCESS 0x00 #define BAD_ADAPTER 0x01 #define BAD_ATTRIBUTE 0x02 #define BAD_BASE 0x03 #define BAD_EDC 0x04 #define BAD_IRQ 0x06 #define BAD_OFFSET 0x07 #define BAD_PAGE 0x08 #define READ_FAILURE 0x09 #define BAD_SIZE 0x0A #define BAD_SOCKET 0x0B #define BAD_TYPE 0x0D #define BAD_VCC 0x0E #define BAD_VPP 0x0F #define BAD_WINDOW 0x11 #define WRITE_FAILURE 0x12 #define NO_CARD 0x14 #define UNSUPPORTED_FUNCTION 0x15 #define UNSUPPORTED_MODE 0x16 #define BAD_SPEED 0x17 #define BUSY 0x18 #define GENERAL_FAILURE 0x19 #define WRITE_PROTECTED 0x1A #define BAD_ARGS_LENGTH 0x1B #define BAD_ARGS 0x1C #define CONFIGURATION_LOCKED 0x1D #define IN_USE 0x1E #define NO_MORE_ITEMS 0x1F #define OUT_OF_RESOURCE 0x20 #define BAD_HANDLE 0x21 //* TUPLES #define CISTPL_NULL 0x00 #define CISTPL_DEVICE 0x01 #define CISTPL_CHECKSUM 0x10 #define CISTPL_LONGLINK_A 0x11 #define CISTPL_LONGLINK_C 0x12 #define CISTPL_LINKTARGET 0x13 #define CISTPL_NO_LINK 0x14 #define CISTPL_VERS_1 0x15 #define CISTPL_ALTSTR 0x16 #define CISTPL_DEVICE_A 0x17 #define CISTPL_JEDEC_C 0x18 #define CISTPL_JEDEC_A 0x19 #define CISTPL_CONFIG 0x1A #define CISTPL_CFTABLE_ENTRY 0x1B #define CISTPL_DEVICE_OC 0x1C #define CISTPL_DEVICE_OA 0x1D #define CISTPL_VERS_2 0x40 #define CISTPL_FORMAT 0x41 #define CISTPL_GEOMETRY 0x42 #define CISTPL_BYTEORDER 0x43 #define CISTPL_DATE 0x44 #define CISTPL_BATTERY 0x45 #define CISTPL_ORG 0x46 #define CISTPL_END 0xFF //* DEVICE TYPES #define DTYPE_NULL 0x0 #define DTYPE_ROM 0x1 #define DTYPE_OTPROM 0x2 #define DTYPE_EPROM 0x3 #define DTYPE_EEPROM 0x4 #define DTYPE_FLASH 0x5 #define DTYPE_SRAM 0x6 #define DTYPE_DRAM 0x7 #define DTYPE_FUNCSPEC 0xD #define DTYPE_EXTEND 0xE //* EVENTS #define PM_RESUME 0x0B #define PM_SUSPEND 0x0C #define BATTERY_DEAD 0x01 #define BATTERY_LOW 0x02 #define CARD_INSERTION 0x40 #define CARD_LOCK 0x03 #define CARD_READY 0x04 #define CARD_REMOVAL 0x05 #define CARD_RESET 0x11 #define CARD_UNLOCK 0x06 #define EJECTION_COMPLETE 0x07 #define EJECTION_REQUEST 0x08 #define ERASE_COMPLETE 0x81 #define EXCLUSIVE_COMPLETE 0x0D #define EXCLUSIVE_REQUEST 0x0E #define INSERTION_COMPLETE 0x09 #define INSERTION_REQUEST 0x0A #define REGISTRATION_COMPLETE 0x82 #define RESET_COMPLETE 0x80 #define RESET_PHYSICAL 0x0F #define RESET_REQUEST 0x10 #define MTD_REQUEST 0x12 #define CLIENT_INFO 0x14 #define TIMER_EXPIRED 0x15 #define SS_UPDATED 0x16 //* STRUCTURES typedef struct { WORD InfoLen; WORD Signature; WORD Count; WORD Revision; WORD CSLevel; WORD VStrOff; WORD VStrLen; BYTE VendorString[80]; }CS_INFO; typedef struct{ WORD Socket; WORD CardState; WORD SocketState; }STATUS_INFO; typedef struct { WORD Socket; WORD Attributes; BYTE DesiredTuple; BYTE Reserved; WORD Flags; DWORD LinkOffset; DWORD CISOffset; BYTE TupleCode; BYTE TupleLink; }TUPLE_INFO; typedef struct { WORD Socket; WORD Attributes; BYTE DesiredTuple; BYTE TupleOffset; WORD Flags; DWORD LinkOffset; DWORD CISOffset; WORD TupleDataMax; WORD TupleDataLen; BYTE TupleData[]; }TUPLE_DATA_INFO; typedef struct { WORD Attributes; WORD EventMask; BYTE ClientData[8]; WORD Version; }REGISTER_CLIENT_INFO; //# END
Copyright © 1994, Dr. Dobb's Journal