pcram.c revision 5f9e250aa611c12bbaccc0be612e5b97ccca2762
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#if defined(DEBUG)
#define PCRAM_DEBUG
#endif
/*
*
*
* The PCMCIA memory card driver will be used to support disk-like
* I/O access to any standard PCMCIA memory cards such as:
*
* - Non-Volatile Static RAM (SRAM)
* - Non-Volatile Dynamic RAM (DRAM)
* - Mask ROM (MROM)
*
* The PCMCIA memory cards can be used as pseudo-floppy disks.
*
* Features:
*
* - DO NOT support the FLASH, EEPROM, and OTP memory card.
* - modeling after the ramdisk pseudo-device.
* - currently supporting character device and block device.
* - supporting only single partition.
*
* Support Utility:
*
* The fdformat(1) utility has to use PCRAM_PROBESIZE ioctl
* to request the card size information.
*
* If a memory card has a Card Information Structure (CIS)
* then the card size is from a Common Memory CISTPL_DEVICE
* tuple. If there is no CIS the driver must use
* block of data to determine the total card size.
* Refer to pcram_card_sizing().
*
* We do not need to support set geometry (DKIOCSGEOM) since we
* provide PCRAM_PROBESIZE ioctl to probe the memory card size.
* PCRAM_PROBESIZE ioctl is a private interface which is
* used by the SunVTS and fdformat(1) utility.
*
* SS2 CACHE+ problem note:
*
* Refer to card_byte_wr() for the problem of the SS2 CACHE+
* double word write to the 16-bit slave device.
*
*/
/*
* needed for: S_IFBLK - block special
* S_IFCHR - character special
*/
/* supporting eject(1) command (struct fd_drive) */
/* The next headers may not be DDI-compliant */
/* DOS label */
/*
* PCMCIA and DDI related header files
*/
/*
* pcram-related header files
*/
/*
*/
static int pcram_getinstance(dev_t);
int *lengthp);
/*
* Device Operations (dev_ops) Structure
*/
/*
* Called from pcram_strategy
*/
/*
* Misc functions
*/
static uint32_t pcram_softintr();
static int pcram_card_insertion(pcram_state_t *);
static int pcram_card_removal(pcram_state_t *);
static int pcram_build_region_lists(pcram_state_t *);
static int pcram_build_region_list(pcram_state_t *,
mem_region_t **, uint32_t);
static void pcram_destroy_region_lists(pcram_state_t *);
static void pcram_destroy_region_list(mem_region_t **, int *);
#ifdef PCRAM_DEBUG
int pcram_debug = 0;
int pcram_debug_events = 1;
static void pcram_display_card_status(get_status_t *);
int priority);
#endif
static struct cb_ops pcram_cb_ops = {
pcram_open, /* open */
pcram_close, /* close */
pcram_strategy, /* strategy */
pcram_print, /* print */
nodev, /* dump */
pcram_read, /* read */
pcram_write, /* write */
pcram_ioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
ddi_segmap, /* segmap */
nochpoll, /* poll */
pcram_prop_op, /* prop_op */
NULL, /* streamtab */
};
/* Device Operations (dev_ops) Structure */
DEVO_REV, /* devo_rev */
0, /* refcnt */
pcram_getinfo, /* info */
nulldev, /* identify */
nulldev, /* probe */
pcram_attach, /* attach */
pcram_detach, /* detach */
nodev, /* reset (currently not supported) */
&pcram_cb_ops, /* cb_ops pointer for leaf driver */
};
/* Module linkage information for the kernel */
extern struct mod_ops mod_driverops;
&mod_driverops, /* Type of module. This is a driver */
&pcram_ops, /* Device Operation Structure */
};
static struct modlinkage modlinkage = {
&md,
};
/*
* Local driver data
*/
static void *pcram_soft_state_p = NULL;
/* Determine PCMCIA memory card size */
/* Updating window size */
/* Byte write to the memory card */
/* SPARC UNIX File System label checksum */
/* Writing disk label */
/* Update drive characteristic structure */
/* external data of interest to us */
char *pcram_name = PCRAM_NAME;
/*
* Module Initialization functions.
* 1. _init() routine
* 2. _info() routine
* 3. _fini() routine
*/
int
_init(void)
{
int error;
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_TRACE)
#endif
sizeof (pcram_state_t),
1 /* n_items */);
if (error) {
return (error);
}
if (error) {
return (error);
}
return (error);
}
int
{
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_TRACE)
#endif
}
int
_fini(void)
{
int error;
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_TRACE)
#endif
if (error)
return (error);
return (error);
}
/*
* Autoconfiguration Routines
* pcram_attach()
* pcram_detach()
* pcram_getinfo()
*/
/*
* Wait for minor nodes to be created before returning from attach,
* with a 5 sec. timeout to avoid hangs should an error occur.
*/
static void
{
break;
}
}
/*
* pcram_attach() - performs board initialization
*
* This routine initializes the PCMCIA memory card driver
* and the board.
*
* Returns: DDI_SUCCESS, if able to attach.
* DDI_FAILURE, if unable to attach.
*/
static int
{
int instance;
/* CardServices variables */
int ret;
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_TRACE)
"pcram_attach: instance %d cmd 0x%x\n",
#endif
/* resume from a checkpoint */
if (cmd == DDI_RESUME) {
return (DDI_SUCCESS);
/* NOTREACHED */
}
/*
* make sure we're only being asked to do an attach
*/
if (cmd != DDI_ATTACH) {
"cmd != DDI_ATTACH\n", instance);
return (DDI_FAILURE);
/* NOTREACHED */
}
instance) != DDI_SUCCESS) {
"state structure for instance %d.", instance);
return (DDI_FAILURE);
/* NOTREACHED */
}
"state structure for instance %d.",
instance);
goto out;
}
/* Remember dev_info structure for getinfo */
/*
* clear the per-unit flags field
*/
/*
* Initialize the card event; initially say that the card
* is removed; when we get a card insertion event and
* validate the card, this will change.
*/
rs->card_event = 0;
/*
* Clear the memory region pointers.
*/
/*
* Initialize to 0 until it is incremented in pcram_check_media
*/
rs->checkmedia_flag = 0;
rs->ejected_media_flag = 0;
/*
* Continueing to return EIO if the card is ejected while
* it is mounted until the LAST layered close is called
*/
rs->ejected_while_mounting = 0;
/* initialize isit_pseudofloppy flag in each card struct */
rs->isit_pseudofloppy = 0;
/*
* Allocate hard drive characteristic structure
*/
/* allocate transfer list header */
"could not allocate transfer list header",
instance);
goto out;
}
/* queue is empty */
/* Add Medium priority soft interrupt to the system */
"could not add soft interrupt",
instance);
goto out;
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_TRACE) {
"calling RegisterClient for instance %d\n",
instance);
}
#endif
/*
* Register with Card Services
* Note that we set CS_EVENT_CARD_REMOVAL_LOWP so that we get
* low priority CS_EVENT_CARD_REMOVAL events as well.
*/
&client_reg)) != CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"RegisterClient failed %s (0x%x)\n",
goto out;
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_TRACE) {
"RegisterClient client_handle 0x%x\n",
rs->client_handle);
}
#endif
/* Get logical socket number and store in pcram_state_t */
&map_log_socket)) != CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"MapLogSocket failed %s (0x%x)\n",
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_TRACE) {
}
#endif
/* Setup the event handler hi-level mutex */
*(client_reg.iblk_cookie));
/* set up the mutex to protect the pcram_state_t */
/* strategy(): waiting for I/O to complete */
/* write(): waiting for I/O to complete */
/* read(): waiting for I/O to complete */
/* for DKIOCSTATE ioctl() */
/* Init firstopenwait_cv */
/* mutex to protect region lists */
(void *)(rs->soft_blk_cookie));
/*
* After the RequestSocketMask call,
* we can start receiving events
*/
&sockmask)) != CS_SUCCESS) {
(void) csx_Error2Text(&cft);
goto out;
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_TRACE)
"pcram_attach: RequestSocketMask OK\n");
#endif
/*
* Wait for minor node creation before continuing
*/
/*
* Check to see if the card is inserted and if this
* attach is triggered by an open, that open
* will wait until pcram_card_insertion() is
* completed
*/
/* XXX function return value ignored */
if (!PCRAM_CARD_PRESENT(rs)) {
#ifdef PCRAM_DEBUG
if (!PCRAM_CARD_PRESENT(rs)) {
} else {
}
#endif
}
}
/*
* set a flag that pcram_open() can look at so that
* if this board doesn't make it all the way through
* pcram_attach(), we won't try to open it
*/
return (DDI_SUCCESS);
/* NOTREACHED */
out:
return (DDI_FAILURE);
}
static int
{
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_TRACE)
"pcram_detach: instance %d cmd 0x%x\n",
#endif
/* suspend */
if (cmd == DDI_SUSPEND) {
return (DDI_SUCCESS);
/* NOTREACHED */
}
if (cmd != DDI_DETACH) {
return (DDI_FAILURE);
/* NOTREACHED */
}
"could not get state structure "
"for instance %d.", instance);
return (DDI_FAILURE);
/* NOTREACHED */
}
/*
* Clear the PCRAM_ATTACHOK so that other layers of the code
* will know that we're going away.
*/
/*
* Call pcram_card_removal to do any final card cleanup
*/
(void) pcram_card_removal(rs);
/*
* Release our socket mask - note that we can't do much
* if we fail these calls other than to note that
* the system will probably panic shortly. Perhaps
* we should fail the detach in the case where these
* CS calls fail?
*/
!= CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"ReleaseSocketMask failed %s (0x%x)\n",
}
}
/*
* Deregister with Card Services - we will stop getting
* events at this point.
*/
!= CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"DeregisterClient failed %s (0x%x)\n",
}
}
}
if (rs->hdrv_chars) {
}
/* Free transfer list header */
}
/* unregister the softinterrupt handler */
}
/* free the various mutexii */
/* strategy(): waiting for I/O to complete */
/* write(): waiting for I/O to complete */
/* read(): waiting for I/O to complete */
/* for DKIOCSTATE ioctl() */
/* Free firstopenwait_cv */
}
return (DDI_SUCCESS);
}
/*
* pcram_getinfo() this routine translates the dip
* info dev_t and vice versa.
*
* Returns: DDI_SUCCESS, if successful.
* DDI_FAILURE, if unsuccessful.
*/
/*ARGSUSED*/
static int
void **result)
{
int error = DDI_SUCCESS;
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
case DDI_INFO_DEVT2INSTANCE:
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_TRACE)
#endif
return (DDI_FAILURE);
/* NOTREACHED */
}
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
if (!(rs = ddi_get_soft_state(
cs_ddi_info.instance))) {
} else {
}
break;
case DDI_INFO_DEVT2INSTANCE:
break;
} /* switch */
break;
default:
error = DDI_FAILURE;
break;
} /* switch */
return (error);
}
static int
{
int rval;
(void) csx_Error2Text(&cft);
"pcram[%d]: csx_CS_DDI_Info failed - %s\n",
return (-1);
}
}
/*
* User context (system call request)
*
*
* pcram_open()
* pcram_close()
* pcram_prop_op()
* pcram_print()
*
* Unique to character drivers:
*
* pcram_read()
* pcram_write()
* pcram_ioctl()
* xxxxx_segmap() ( ddi_segmap )
* xxxxx_chpoll() ( nochpoll )
*/
/*ARGSUSED*/
static int
{
int instance;
int err;
/* get instance number */
"pcram_open: pcram_getinfo failed\n");
return (ENXIO);
}
"could not get state for instance %d\n",
instance);
return (ENXIO);
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_TRACE)
#endif
return (ENXIO);
/* NOTREACHED */
}
/*
* Do a CS call to see if the card is present
*/
!= CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"GetStatus failed %s (0x%x)\n",
return (ENXIO);
/* NOTREACHED */
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_CARD_STATUS) {
"pcram_open: socket %d GetStatus returns:\n",
}
#endif
/*
* Check to see if the card is present.
* If there is no card in the socket,
* then return ENXIO.
*/
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_CARD_STATUS) {
}
#endif
return (ENXIO);
/* NOTREACHED */
}
if (!rs->batter_dead_posted) {
"Battery & Data integrity "
/* Display once on the system console */
rs->batter_dead_posted++;
}
return (ENXIO);
/* NOTREACHED */
}
}
if (!rs->batter_low_posted) {
"Battery should be replaced; "
/* Display once on the system console */
rs->batter_low_posted++;
}
}
/* Next check for read only file system */
return (EROFS);
/* NOTREACHED */
}
/*
* Only honor FEXCL. If a regular open or a layered open
* is still outstanding on the device, the exclusive open
* must fail.
*/
return (EAGAIN);
/* NOTREACHED */
}
switch (otyp) {
case OTYP_BLK:
break;
case OTYP_CHR:
break;
case OTYP_LYR:
break;
default:
return (EINVAL);
/* NOTREACHED */
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_TRACE) {
}
#endif
/*
* For cards without attribute memory, probe the card to
* determine the memory size.
*/
err = 0;
if (rs->default_size_flag) {
/* Setup for default maximum size of 64MB */
!= UNRECOGNIZED_MEDIA) {
/*
* Actual card size is determined
* so disable default_size_flag
*/
rs->default_size_flag = 0;
} else {
/*
* Found unregconized PCMCIA Static RAM media
* so treat it as an unlabeled memory card
* with a maximum size of 64MB (PCMCIA 2.0
* specification)
*/
"Unregconized PCMCIA Static RAM media",
}
}
return (err);
}
/*ARGSUSED*/
static int
{
int instance;
"pcram_close: pcram_getinfo failed\n");
return (ENXIO);
}
"could not get state for instance %d\n",
instance);
return (ENXIO);
/* NOTREACHED */
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_TRACE)
#endif
switch (otyp) {
case OTYP_BLK:
break;
case OTYP_CHR:
break;
case OTYP_LYR:
break;
default:
return (EINVAL);
/* NOTREACHED */
}
/* not done yet */
return (0);
/* NOTREACHED */
}
/*
* Continueing to return EIO if the card is ejected
* while it is mounted until the LAST layered close
* is called
*/
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_TRACE) {
"Reset ejected_while_mounting flag\n");
}
#endif
rs->ejected_while_mounting = 0;
}
return (0);
}
static int
{
int instance;
"pcram_print: pcram_getinfo failed\n");
return (ENXIO);
}
"could not get state for instance %d.", instance);
return (ENXIO);
/* NOTREACHED */
}
return (0);
}
/*
* Character driver routines
* pcram_read()
* pcram_write()
*/
/*ARGSUSED*/
static int
{
int instance;
int error;
int nbytes;
int offset, next_offset;
int remainder_wsize;
int rval;
int err;
"pcram_read: pcram_getinfo failed\n");
return (ENXIO);
}
"could not get state for instance %d.",
instance);
return (ENXIO);
/* NOTREACHED */
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_TRACE)
#endif
/*
* Do a CS call to see if the card is present
*/
&get_status)) != CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"GetStatus failed %s (0x%x)\n",
return (ENXIO);
/* NOTREACHED */
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_CARD_STATUS) {
}
#endif
/*
* Check to see if the card is present.
* If the memory card has been removed or
* was ever removed, return an I/O error (EIO)
* not "No such device or address" (EMXIO).
*
* Continueing to return EIO if the card is ejected
* while it is mounted until the LAST layered close
* is called
*/
if (!rs->card_eject_posted) {
/* XXX WARNING - card is ejected */
rs->card_eject_posted++;
"Card is ejected & "
"Data integrity is not guaranteed",
}
return (EIO);
/* NOTREACHED */
}
/*
* Wait for the current request to finish. We can
* safely release the mutex once we complete the write
* operation, because anyone else calling pcram_read
* will wait here until we release it with a cv_signal.
*/
}
goto out;
}
while (nbytes > 0) {
int copybytes;
if (next_offset < 0) {
/* something wrong with MapMemPage function */
goto out;
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_SIZE) {
"READ: size not on window boundary\n"
"\toffset 0x%x, rs->win_size 0x%x\n"
"\tnbytes 0x%x, remainder_wsize 0x%x\n",
}
#endif
if (PCRAM_CARD_PRESENT(rs)) {
/*
* Transfer block in between the two windows
*/
/*
* We cannot use uiomoveto xfer directly
* between pcram device to user area because
* 64 byte * block xfers may be done in copyout.
* PCMCIA memory cards are cannot be read
* thru block move instructions.
* just allocate pbuf
* buffer and to use csx_RepGet8()
* call to transfer from the
* memory card to pbuf then use
* uiomove() to move from pbuf
* to the buffer(s) described by
* uiop structure.
*/
/* Card access handle */
/* base dest addr */
/* card window offset */
/* num_bytes xfer */
/* flag */
/* now free csbuf */
if (error != 0) {
goto out;
}
} else {
/*
* stop to read the card when
* there is a card removal event
*/
goto out;
}
} /* while */
rval = 0;
out:
/*
* End of read operation, release the
* cv_wait() for the next thread
*/
return (rval);
}
/*ARGSUSED*/
static int
{
int instance;
int error;
int nbytes;
int offset;
int rval;
int err;
"pcram_write: pcram_getinfo failed\n");
return (ENXIO);
}
"could not get state for instance %d.",
instance);
return (ENXIO);
/* NOTREACHED */
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_TRACE)
#endif
/*
* Do a CS call to see if the card is present
*/
!= CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"GetStatus failed %s (0x%x)\n",
return (ENXIO);
/* NOTREACHED */
}
/*
* Check to see if the card is present.
* If the memory card has been removed or
* was ever removed, return an I/O error (EIO)
* not "No such device or address" (EMXIO).
*
* Continueing to return EIO if the card is ejected
* while it is mounted until the LAST layered close
* is called
*/
if (!rs->card_eject_posted) {
/* XXX WARNING - card is ejected */
rs->card_eject_posted++;
"Card is ejected & "
"Data integrity is not guaranteed",
}
return (EIO);
/* NOTREACHED */
}
/*
* Wait for the current request to finish. We can
* safely release the mutex once we complete the write
* operation, because anyone else calling pcram_write
* will wait here until we release it with a cv_signal.
*/
}
goto out;
}
/* Save offset and byte count from uiop structure */
/*
* Start to transfer 1KB data to kernel buffer at a time
* The 1MB window offset is handled in card_byte_wr()
*/
while (nbytes > 0) {
int copybytes;
int next_offset;
int remainder_wsize;
if (next_offset < 0) {
/* something wrong with MapMemPage function */
goto out;
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_SIZE) {
"WRITE: size not on window boundary\n"
"\toffset 0x%x, rs->win_size 0x%x\n"
"\tnbytes 0x%x, remainder_wsize 0x%x\n",
}
#endif
if (PCRAM_CARD_PRESENT(rs)) {
/*
* Transfer block size is in between
* the two windows
*/
if (error != 0) {
goto out;
}
} else {
/*
* stop to write to the card when
* there is a card removal event
*/
goto out;
}
} /* while */
rval = 0;
out:
/*
* End of write operation, release the
* cv_wait() for the next thread
*/
return (rval);
}
/*ARGSUSED*/
static int
int *rvalp)
{
int i;
int instance;
int err;
int fdchange;
enum dkio_state state;
"pcram_ioctl: pcram_getinfo failed\n");
return (ENXIO);
}
"could not get state for instance %d.",
instance);
return (ENXIO);
/* NOTREACHED */
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_TRACE) {
"cmd 0x%x arg 0x%lx mode 0x%x\n",
}
#endif
switch (cmd) {
case DKIOCEJECT:
/*
* Since we do not have hardware support for ejecting
* a memory card, we must not support the generic eject
* ioctl (DKIOCEJECT) which is used for eject(1) command
* because it leads the user to expect behavior that is
* not present.
*/
return (ENOSYS);
/* NOTREACHED */
case DKIOCGGEOM:
/*
* newfs does this first
* return dk_geom structure
*/
if (!rs->default_size_flag) {
/*
* If the card is built with DOS_BPB or
* Solaris VTOC or CIS info.
* we just return the disk geometry
* information
*/
return (EFAULT);
/* NOTREACHED */
}
return (0);
} else {
/*
* Return error when we can not find
* the actual card size.
*/
return (EFAULT);
/* NOTREACHED */
}
/* NOTREACHED */
case DKIOCGVTOC:
/*
* newfs does this second
* return vtoc structure. 1 partion.
*/
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32: {
return (EFAULT);
break;
}
case DDI_MODEL_NONE:
return (EFAULT);
break;
}
#else /* ! _MULTI_DATAMODEL */
return (EFAULT);
#endif /* _MULTI_DATAMODEL */
return (0);
/* NOTREACHED */
case DKIOCINFO:
/*
* newfs does this third
* return dk_cinfo structure.
*/
/*
* SunVTS uses PCRAM_DKC_CNAME "pcram"
* for checking if it is a pcram controller
*/
/*
* For pseudo floppy disk setup (pcfs file system)
* dkc.dki_flags = DKI_PCMCIA_PFD;
*/
/*
* volmgt will use this dki_unit as a PCMCIA
* socket info. during a static mode
* During a dynamic mode (physically
* remove and insert a card), the socket
* info. is from PCMCIA User Daemon
*/
return (EFAULT);
/* NOTREACHED */
}
return (0);
/* NOTREACHED */
case DKIOCGAPART:
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32: {
/*
* XXX - support only one partition now
* should be NDKMAP later
*/
for (i = 0; i < 1; i++) {
}
return (EFAULT);
}
break;
case DDI_MODEL_NONE:
for (i = 0; i < 1; i++) {
}
return (EFAULT);
}
#else /* ! _MULTI_DATAMODEL */
for (i = 0; i < 1; i++) {
}
return (EFAULT);
#endif /* _MULTI_DATAMODEL */
return (0);
case DKIOCSTATE:
return (EFAULT);
/* NOTREACHED */
}
/*
* This function is used by the volume management
* to check the memory card state
*/
return (err);
/* NOTREACHED */
}
sizeof (int), mode)) {
return (EFAULT);
/* NOTREACHED */
}
return (0);
/* NOTREACHED */
case DKIOCSVTOC:
/*
* fdformat(1) uses this ioctl() to ask the driver
* to construct the disk label.
*/
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32: {
return (EFAULT);
break;
}
case DDI_MODEL_NONE:
return (EFAULT);
break;
}
#else /* _MULTI_DATAMODEL */
return (EFAULT);
#endif /* _MULTI_DATAMODEL */
return (err);
/* NOTREACHED */
}
return (0);
/* NOTREACHED */
case DKIOCREMOVABLE:
/*
* Supporting volmgt by returning a constant
* since PCMCIA is a removable media.
* Refer to PSARC/1996/004.
*/
i = 1;
return (EFAULT);
/* NOTREACHED */
}
return (0);
/* NOTREACHED */
case PCRAM_GETMEDIA:
/*
*/
return (EFAULT);
/* NOTREACHED */
}
return (0);
/* NOTREACHED */
case PCRAM_PROBESIZE:
/*
* After getting an error return from calling
* DKIOCGGEOM ioctl, fdformat(1) *must* use
* this ioctl() to get get the memory card size
* before formatting the memory card. This
* allows the driver to do some destructive
* write and verify operation (backup data
* before writing to the card).
*
* If the card is built with DOS_BPB or Solaris VTOC
* or CIS info., we do not need to probe card
* size. If the card is built with a default
* size of 64MB then we need to probe the actual
* card size
*/
if (!rs->default_size_flag) {
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_CIS) {
"\tPCRAM_PROBESIZE: card size "
"is already\n\t\tdetermined from "
}
#endif
return (EFAULT);
/* NOTREACHED */
}
return (0);
/* NOTREACHED */
}
/* Setup for default maximum size of 64MB */
/*
* Do a CS call to see if the card is present
*/
!= CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"GetStatus failed %s (0x%x)\n",
return (ENXIO);
/* NOTREACHED */
}
!= UNRECOGNIZED_MEDIA) {
/*
* Actual card size is determined
* so disable default_size_flag
*/
rs->default_size_flag = 0;
err = 0;
} else {
/*
* Found unregconized PCMCIA Static RAM media
* so treat it as an unlabeled memory card
* with a maximum size of 64MB (PCMCIA 2.0
* specification)
*/
"Unregconized PCMCIA Static RAM media",
}
return (EFAULT);
/* NOTREACHED */
}
return (err);
/* NOTREACHED */
case FDGETDRIVECHAR:
/* supporting eject(1) command */
return (EFAULT);
/* NOTREACHED */
}
return (EFAULT);
/* NOTREACHED */
}
return (0);
/* NOTREACHED */
case FDGETCHANGE:
/*
* supporting volcheck(1) command
*
* FDGC_HISTORY disk has changed since last i/o
* FDGC_CURRENT current state of disk change
* FDGC_CURWPROT current state of write protect
* FDGC_DETECTED previous state of DISK CHANGE
*
*/
return (EFAULT);
/* NOTREACHED */
}
/* GetStatus */
!= CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"socket %d GetStatus failed %s (0x%x)\n",
return (ENXIO);
/* NOTREACHED */
}
/*
* See eject.c or fdformat.c for bit definition
*/
/* Simulating - floppy is present */
fdchange &= ~FDGC_CURRENT;
if (get_status.CardState &
/*
* Simulating
* floppy is write protected
*/
} else {
/*
* Simulating
* floppy is NOT write protected
*/
fdchange &= ~FDGC_CURWPROT;
}
} else {
/* Simulating - floppy is NOT present */
fdchange |= FDGC_CURRENT;
}
return (EFAULT);
/* NOTREACHED */
}
return (0);
/* NOTREACHED */
default:
return (ENOTTY);
/* NOTREACHED */
}
}
/*
* pcram_prop_op() - Property Management
*
* All block drivers must support the "Nblocks" property,
* and all character drivers must support the "size" property.
* Since we support hot plugging for many different memory
* card size and the Nblocks and the size property are changed
* dynamically, so these property should be maintained by the
* driver.
*/
static int
{
int instance;
/*
* Our dynamic properties are all device specific and size oriented.
* Requests issued under conditions where size is valid are passed
* to ddi_prop_op_size with the size information, otherwise the
* request is passed to ddi_prop_op.
*/
if (dev == DDI_DEV_T_ANY) {
} else {
"pcram_getinfo failed\n");
goto pass;
}
"no state for instance %d", instance);
goto pass;
}
/* get size in bytes */
}
}
/*
* Block driver routines
* pcram_strategy()
* pcram_start()
*/
static int
{
int instance;
int err;
"pcram_strategy: pcram_getinfo failed\n");
goto out;
}
"could not get state for instance %d.",
instance);
goto out;
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_TRACE)
#endif
/* Do a CS call to see if the card is present */
!= CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"GetStatus failed %s (0x%x)\n",
goto out;
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_CARD_STATUS) {
}
#endif
/*
* Check to see if the card is present.
* If the memory card has been removed or
* was ever removed, return an I/O error (EIO)
* not "No such device or address" (EMXIO).
*
* Continueing to return EIO if the card is ejected
* while it is mounted until the LAST layered close
* is called
*/
if (!rs->card_eject_posted) {
/* XXX WARNING - card is ejected */
rs->card_eject_posted++;
"Card is ejected & Data integrity is not guaranteed",
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_CARD_STATUS) {
}
#endif
goto out;
}
/*
* Do not need the mutex around rs->card_size, since it
*
* XXXX: Is it still true for PCMCIA memory card driver?
* XXX Was this the cause of a tar causing a panic on too
* small a card?
*/
goto out;
}
/*
* Wait for the current request to finish. We can safely
* release the mutex once we complete the write operation,
* because anyone else calling pcram_strategy will wait here
* until we release it with a cv_signal.
*/
}
/* nothing on queue */
/* start it */
} else {
/* put on work list */
}
return (0);
out:
return (0);
}
static void
{
int offset;
int next_offset;
int remainder_wsize;
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_TRACE)
#endif
while (nbytes > 0) {
int copybytes;
if (next_offset < 0) {
/* something failed so abort */
break;
}
/* get partial block size if not on window boundary */
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_SIZE) {
"%s: size not on window boundary\n"
"\toffset 0x%x, rs->win_size 0x%x\n"
"\tnbytes 0x%x, remainder_wsize 0x%x\n",
"READ" : "WRITE",
}
}
#endif
/* Read direct from PC Card memory */
csx_RepGet8(/* Card access handle */
/* base dest addr */
/* card window offset */
/* num_bytes xfer */
/* flag */
} else { /* WRITE operation */
/*
* Start to transfer 1KB data
* to kernel buffer at a time
*/
/*
* Update PC Card memory from the kernel buffer
*/
/*
* does not need mutex because it is already
* handled in pcram_strategy and pcram_softintr
*/
}
if (!PCRAM_CARD_PRESENT(rs)) {
/* stop when there is a card removal event */
break;
}
} /* while (nbytes) */
}
/*
* Software Interrupt Handler
* pcram_softintr()
*/
static uint32_t
{
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_TRACE)
#endif
return (DDI_INTR_UNCLAIMED);
}
/* got work to do */
} else {
/*
* End of write operation, release the
* cv_wait() for the next thread
*/
}
} /* rs->busy */
return (DDI_INTR_CLAIMED);
/* NOTREACHED */
}
/*
* SPARCstation-2 CACHE+ bug
*
* bcopy() with double-word write in SS2 machine only
* supports only word acknowledge. The DoRight PCMCIA
* ASIC is a 16-bit device so it will not work correctly
* during a write from SS2 host to the PCMCIA memory card.
* Therefore we have to write *one* byte at a time.
* In order to avoid to have two version of the driver
* for SS2 and other platforms, we can use the same
* byte write mechanism for all platform.
*
* bcopy() is commonly used in pcram_start and pcram_strategy.
*
* There is no major performance impact between
* using bcopy() for writing to card and card_byte_wr().
*
* Using double buffer write to transfer data from host
* memory (allocate 1KB) to the memory card.
*
*/
static void
{
int i;
for (i = 0; i < xfer_size; i++) {
if (PCRAM_CARD_PRESENT(rs)) {
"Write-Protect is enabled",
}
/*
* stop writing when
* write-protect is enabled
*/
break;
} else {
cardoffset, *hostmempt);
hostmempt++;
cardoffset++;
}
} else {
/*
* stop to write to the card when
* there is a card removal event
*/
break;
}
}
}
/*
* Updating window size
*/
static int
{
int ret;
int i;
int err;
/*
* Do a CS call to see if the card is present
*/
!= CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"GetStatus failed %s (0x%x)\n",
/* Let caller knows that there is some thing wrong */
return (-1);
/* NOTREACHED */
}
#ifdef PCRAM_DEBUG
#endif
/* Let caller knows that there is no card */
return (-1);
/* NOTREACHED */
}
/*
* Do following setup AFTER checking if card is inserted
* or we will get "zero divide trap panic"
*/
/* We do not support page mode */
map_mem_page.Page = 0;
"Found zero rs->win_size %d\n",
/* To avoid zero divide problem */
return (-1);
/* NOTREACHED */
} else {
/* setup for CardOffset of MapMemPage */
}
/*
* Now map the offset to the card
*/
!= CS_SUCCESS) {
(void) csx_Error2Text(&cft);
!= CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"ReleaseWindow failed %s (0x%x)\n",
}
/* Let caller knows that there is some thing wrong */
return (-1);
/* NOTREACHED */
}
return (offset);
/* NOTREACHED */
}
/*
* pcram_card_sizing - Determine memory card size
* by writing every block of window size.
*
* The window size must be a multiple of 1KB size?.
*
* returns: n - card size of n bytes
* -1 - if we can not read after writing
*/
{
int ret;
int offset;
int next_offset;
offset = 0;
if (ret < 0) {
/* something failed so abort */
return (-1);
/* NOTREACHED */
}
/* Select test data pattern */
if (cm_addr_zero != PATTERN_1) {
} else {
}
/* Select block size sample */
} else {
}
} else {
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_SIZE) {
"\tBlock size sample [%d bytes]\n",
}
#endif
while (offset < MAX_CARD_SIZE) {
if (ret < 0) {
/* something failed so abort */
return (-1);
/* NOTREACHED */
}
next_offset = ret;
/* Save data information */
/* Write this location with test_pattern */
/*
* Write verification
* If it is not a writen data,
* this could be a ROM or FLASH card.
*/
if (cm_next_addr != test_pattern) {
return (UNRECOGNIZED_MEDIA);
/* NOTREACHED */
}
if (ret < 0) {
/* something failed so abort */
return (-1);
/* NOTREACHED */
}
if (cm_addr_zero == test_pattern) {
/* Restore location 0 data */
break;
}
if (ret < 0) {
/* something failed so abort */
return (-1);
/* NOTREACHED */
}
/* Restore previous write data */
} /* while */
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_SIZE) {
"\tFound card_size [%d bytes]\n",
}
#endif
return (offset);
/* NOTREACHED */
}
/*
* SPARC UFS label checksum
*/
static int
{
int i;
unsigned char value;
}
return (value);
/* NOTREACHED */
}
/*
*/
static int
{
int err;
/*
* Do a CS call to see if the card is present
*/
!= CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"GetStatus failed %s (0x%x)\n",
return (ENXIO);
/* NOTREACHED */
}
/* Register rs->media_state */
} else {
} else {
}
}
/*
* XXXX - In order not to modify the volume management
* we have to follow the current SCSI CDROM model
* for checking media state (broken way, sigh!)
* start with state = DKIO_NONE
* wait until mediastate = DKIO_INSERTED
* wait until mediastate = DKIO_EJECTED
* if DKIOCSTATE ioctl() is called second time
* with state = DKIO_EJECTED,
* return state = DKIO_NONE
* restart with state = DKIO_NONE
*
*/
if (rs->ejected_media_flag &&
rs->ejected_media_flag = 0;
return (0);
/* NOTREACHED */
}
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_VOLD) {
"\tWaiting state change: rs->media_state %d state %d\n"
"\tDKIO_NONE %d DKIO_EJECTED %d DKIO_INSERTED %d\n",
}
#endif
/*
* wait for Card Detect Change Interrupt handler
* see either pcram_card_insertion/pcram_card_removal
* for cv_broadcast
*/
rs->checkmedia_flag++;
return (EINTR);
/* NOTREACHED */
}
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_VOLD) {
"\tAfter state change: rs->media_state %d state %d\n"
"\tDKIO_NONE %d DKIO_EJECTED %d DKIO_INSERTED %d\n",
}
#endif
if (!rs->ejected_media_flag &&
rs->ejected_media_flag++;
}
}
return (0);
/* NOTREACHED */
}
/*
* Constructing disk label
*/
static int
{
int i;
#if defined(__sparc)
#ifdef _SYSCALL32
#else
#endif
#endif /* __sparc */
/* Sanity-check the vtoc */
return (EINVAL);
/* NOTREACHED */
}
sizeof (vtoc->v_bootinfo));
#endif
sizeof (vtoc->v_reserved));
for (i = 0; i < NDKMAP; i++) {
#ifdef XX
/* XXX - does not compile for x86 ??? */
#endif
#endif /* XX */
lpart++;
}
#endif
#if defined(__sparc)
for (i = 0; i < NDKMAP; i++) {
lmap++;
vpart++;
}
#endif /* __sparc */
sum = 0;
i = sizeof (struct dk_label)/sizeof (short);
while (i--) {
}
#ifdef PCRAM_DEBUG
if (pcram_debug > 1) {
"\tncyl %d, nhd %d, nsec %d, pcyl %d\n",
}
#endif
return (0);
/* NOTREACHED */
}
/*
* Writing disk label to the memory card
*/
static void
{
/*
* Update the kernel buffer with the dk_label structure
*/
/* Write to the memory card */
}
/*
* pcram_event - this is the event handler
*/
static int
{
int retcode = CS_UNSUPPORTED_EVENT;
#ifdef DEBUG
if (pcram_debug_events) {
}
#endif
if (priority & CS_EVENT_PRI_HIGH) {
}
#ifdef PCRAM_DEBUG
if (pcram_debug > 1) {
(void) csx_Event2Text(&event2text);
"\tevent %s (0x%x) priority 0x%x\n",
}
#endif
/*
* Find out which event we got and do the appropriate thing
*/
switch (event) {
break;
case CS_EVENT_CARD_INSERTION:
if (priority & CS_EVENT_PRI_LOW) {
}
break;
case CS_EVENT_BATTERY_LOW:
break;
case CS_EVENT_BATTERY_DEAD:
break;
case CS_EVENT_WRITE_PROTECT:
if (priority & CS_EVENT_PRI_LOW) {
}
} else {
}
if (priority & CS_EVENT_PRI_LOW) {
}
break;
/*
* Note that we get two CS_EVENT_CARD_REMOVAL events -
* one at high priority and the other at low priority.
* This is determined by the setting of the
* CS_EVENT_CARD_REMOVAL_LOWP bit in either of the
* event masks.
* (See the call to RegisterClient).
*/
case CS_EVENT_CARD_REMOVAL:
if (priority & CS_EVENT_PRI_HIGH) {
} else {
}
break;
case CS_EVENT_CLIENT_INFO:
} /* CS_CLIENT_INFO_SUBSVC_CS */
break;
}
if (priority & CS_EVENT_PRI_HIGH) {
}
return (retcode);
/* NOTREACHED */
}
/*
* pcram_card_insertion - handles card insertion events
*/
static int
{
int ret;
int rval = CS_SUCCESS;
rs->batter_dead_posted = 0;
rs->batter_low_posted = 0;
/* XXXX - for Volume Manager */
if (rs->checkmedia_flag) {
rs->checkmedia_flag = 0;
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_VOLD) {
"\tdoing cv_broadcast - "
}
#endif
}
/*
* used by PCRAM_PROBESIZE ioctl() to determine if
* there is a DOS_BPB, Solaris VTOC, or CIS info
*/
rs->default_size_flag = 0;
/*
* XXX need more work search further for CIS tuple
* level-2 and level-3 to determine the partition info.
*/
rs->card_eject_posted = 0;
/*
* Do a CS call to check the card state
*/
!= CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"GetStatus failed %s (0x%x)\n",
return (ret);
/* NOTREACHED */
}
/* Make sure that there is a card in the socket */
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_CARD_STATUS) {
}
#endif
return (CS_NO_CARD);
/* NOTREACHED */
}
/*
* Set up the client event mask to give us WP and battery
* events as well as what other events we have already
* registered for.
* Note that since we set the global event mask in the call
* to RegisterClient in pcram_attach, we don't have to
* duplicate those events in this event mask.
*/
!= CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"GetEventMask failed %s (0x%x)\n",
return (ret);
/* NOTREACHED */
}
!= CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"SetEventMask failed %s (0x%x)\n",
return (ret);
/* NOTREACHED */
}
/*
* If we had a window, make sure to release
* before we request for the new window
*/
!= CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"pcram_card_insertion: socket %d "
"ReleaseWindow failed %s (0x%x)\n",
}
}
/*
* Try to get a memory window to CM space
*/
/* window it finds */
(void) csx_ConvertSpeed(&convert_speed);
/* XXX - set to 0x32 until cis_convert_devspeed is fixed */
(void) csx_Error2Text(&cft);
"RequestWindow failed %s (0x%x)\n",
return (ret);
/* NOTREACHED */
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_TRACE) {
"\tRequestWindow successful handle 0x%x\n"
"\tAttributes 0x%x Base 0x%x \n"
"\tSize 0x%x AccessSpeed 0x%x\n",
}
#endif
/*
* Now map the offset to the start of the card
*/
map_mem_page.CardOffset = 0;
map_mem_page.Page = 0;
&map_mem_page)) != CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"MapMemPage failed %s (0x%x)\n",
!= CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"pcram_card_insertion: socket %d "
"ReleaseWindow failed %s (0x%x)\n",
}
return (ret);
/* NOTREACHED */
}
/* Store rs->access_handle */
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_TRACE) {
"\tBase 0x%x rs->access_handle 0x%p\n"
"\tSize 0x%x rs->win_size 0x%x\n",
}
#endif
/*
* Build the memory region lists. This function will build
* the lists whether or not there is a CIS on the card
*/
/* error */
if (ret == 0) {
"pcram_card_insertion: socket %d \n"
"\tERROR - pcram_build_region_lists - "
"AM[%d], CM[%d]\n",
rs->num_cm_regions);
} else if (ret == -2) {
/*
* Found unsupported Device error
* Specified socket is invalid
*/
}
} else {
/* no error */
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_CIS) {
"\tRegion number - AM[%d], CM[%d]\n",
}
#endif
/*
* Set "first" to one before you call the function if
* you want it to behave as a "get first" function; the
* function will automatically manipulate "first" from
* then on and it will behave as a "get next" function.
* Don't forget to set "first" back to one if you want
* a "get first" function again.
*/
/* XXX - Need more work on how to handle */
/* multiple regions later */
/* Point to Common Memory region list */
/* Get BUILD_DOS_BPBFAT_LIST list */
first = 1;
&first)) {
/* XXX - For now assuming there is ONLY */
/* one DOS region */
/* Note that rs->hdrv_chars is setup in */
/* pcram_get_bpbfat_info */
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_CIS) {
"pcram_card_insertion: socket %d - "
"BUILD_DOS_BPBFAT_LIST\n\tdevice speed [%d]\n",
}
#endif
/* for VERBOSE mode */
"(MSDOS) socket %d card size %d\n",
} /* while */
/* Get BUILD_SOLARIS_LIST list */
first = 1;
&first)) {
/* XXX - For now assuming there is ONLY */
/* one Solaris region */
/* Note that rs->hdrv_chars is setup in */
/* pcram_get_solaris_info */
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_CIS) {
"pcram_card_insertion: socket %d - BUILD_SOLARIS_LIST\n"
}
#endif
/* for VERBOSE mode */
"(SOLARIS) socket %d card size %d\n",
} /* while */
/* Get BUILD_DEFAULT_LIST list */
first = 1;
&first)) {
/* XXX - For now assuming there is ONLY */
/* one DEFAULT region */
/*
* XXX - check for non-zero for drv_nhead,
* drv_secptrack, drv_sec_size to avoid
* zero divide panic
*/
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_CIS) {
"pcram_card_insertion: socket %d - BUILD_DEFAULT_LIST\n"
}
#endif
/* for VERBOSE mode */
"(DEFAULT) socket %d card size %d\n",
} /* while */
/* Get BUILD_CM_LIST list */
first = 1;
&first)) {
/* XXX - For now assuming there is ONLY */
/* one CM CIS region */
/*
* XXX - check for non-zero for drv_nhead,
* drv_secptrack, drv_sec_size to avoid
* zero divide panic
*/
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_CIS) {
"BUILD_CM_LIST\n\tdevice speed [%d]\n",
}
#endif
/* for VERBOSE mode */
"?pcram: (CIS) socket %d card size %d\n",
} /* while */
/*
* Create the device nodes.
* This is an example that assumes
* that we want to create four devices
* for this instance.
*/
{
int n;
char devname[80];
char *dname;
kmem_zalloc(sizeof (struct devnode_desc) *
/*
* Create only "c" partition for now since
* the driver support only one parttion
*/
for (n = 0; n < (make_device_node.NumDevNodes);
n++) {
if (n&1) {
(((n+4)/2)+'a'));
} else {
(((n+4)/2)+'a'));
}
} /* for */
&make_device_node)) != CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"pcram_card_insertion: socket %d "
"MakeDeviceNode failed %s (0x%x)\n",
}
/*
* We don't need this structure anymore
* since we've created the devices.
* If we need to keep track of the
* devices that we've created for
* some reason, then you' want to keep
* this structure and the
* make_device_node_t structure around
* in a global data area.
*/
sizeof (struct devnode_desc) *
} /* create device nodes */
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_TRACE) {
"\tRegion list - hard drive structure\n"
"\tcsize [%d] ncyl [%d] hd [%d] "
}
#endif
} /* if (!pcram_build_region_lists(rs)) */
/*
* Wake up firstopenwait_cv in pcram_open()
*/
return (rval);
/* NOTREACHED */
}
/*
* pcram_card_removal - handles card removal events; can only
* be called from the low priority card
* removal event
*/
static int
{
int ret;
rs->batter_dead_posted = 0;
rs->batter_low_posted = 0;
/* Misc. flags */
/* XXX - DO NOT need to set win_size to zero */
/* due to "zero divide trap panic" */
/* rs->win_size = 0; */
/* XXXX - for Volume Manager */
if (rs->checkmedia_flag) {
rs->checkmedia_flag = 0;
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_VOLD) {
"pcram_card_removal: socket %d \n"
"\tdoing cv_broadcast - "
"rs->media_state of DKIO_EJECTED\n",
}
#endif
}
rs->card_eject_posted = 0;
/*
* Remove all the device nodes. We don't have to explictly
* specify the names if we want Card Services to remove
* all of the devices.
* Note that when you call MakeDeviceNode with the Action
* argument set to REMOVAL_ALL_DEVICE_NODES, the
* NumDevNodes must be zero.
*/
&make_device_node)) != CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"pcram_card_removal: socket %d "
"MakeDeviceNode failed %s (0x%x)\n",
}
} /* remove device nodes */
/*
* Do a CS call to check the card state
*/
!= CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"GetStatus failed %s (0x%x)\n",
return (ret);
/* NOTREACHED */
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_CARD_STATUS) {
}
#endif
/*
* Destroy the memory region lists.
*/
/*
* Release the window if we allocated one
*/
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_TRACE) {
"PCRAM_HAS_WINDOW [0x%x]\n"
}
#endif
!= CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"pcram_card_removal: socket %d "
"ReleaseWindow failed %s (0x%x)\n",
return (ret);
/* NOTREACHED */
}
}
/*
* Set up the client event mask to clear WP and battery
* events as well as what other events we have already
* registered for.
* Note that since we set the global event mask in the call
* to RegisterClient in pcram_attach, we don't have to
* duplicate those events in this event mask.
*/
!= CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"GetEventMask failed %s (0x%x)\n",
return (ret);
/* NOTREACHED */
}
!= CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"SetEventMask failed %s (0x%x)\n",
return (ret);
/* NOTREACHED */
}
return (CS_SUCCESS);
/* NOTREACHED */
}
#ifdef PCRAM_DEBUG
/*
* pcram_display_card_status - wrapper for GetStatus CS function
*/
static void
{
(void) csx_Event2Text(&event2text);
if (pcram_debug & PCRAM_DEBUG_CARD_STATUS) {
}
(void) csx_Event2Text(&event2text);
if (pcram_debug & PCRAM_DEBUG_CARD_STATUS) {
}
}
#endif
/*
* pcram_build_region_lists - builds a card memory region list for
* the passed card
*
* calling: pcram_state_t *rs - pointer to the
* per-card structure
*
* returns: 0 if error building region list
* 1 if region list built
* -1 if found UNSUPPORTED DEVICE types
*
* Note: There are two lists that can be built - one for AM regions
* and one for CM regions. We can be called whether or not there is
* a CIS on the card. If there is a CIS, we use the inforamtion that
* we find in it to build the lists; if there is no CIS on the card,
* we look for an MS-DOS BPB-FAT pseudo floppy image; if it is found,
* only a CM list is created which describes the one BPB-FAT region
* in CM. No AM list is built in this case.
*
* If neither a CIS nor a BPB-FAT are found, we create only one list.
* That list is for the CM space and specifies one region that is as
* large as the card. In this case, it is expected the the driver
* will provide read-only access to this one region.
*
* If the driver is asked to format the card, the existing lists will
* be destroyed, a new CIS written to the card (if necessary), and both
* lists recreated.
*
* XXX - need to think about the non-CIS non-DOS case
*/
static int
{
int ret;
/*
* Check for a CIS on this card - if there is one, our job
* is very easy.
*/
!= CS_SUCCESS) {
(void) csx_Error2Text(&cft);
"socket %d"
"ValidateCIS failed %s (0x%x)\n",
return (0);
/* NOTREACHED */
/*
* No CIS on card, try to find an MS-DOS BPB-FAT
* filesystem and build our list from that.
*/
} else {
if ((rs->num_cm_regions =
&rs->cm_regions,
BUILD_DOS_BPBFAT_LIST)) <= 0) {
/*
* Couldn't find a BPB-FAT filesystem, so first
* destroy the CM list that was just built and
* build the default CM region list.
*/
if ((rs->num_cm_regions =
BUILD_SOLARIS_LIST)) <= 0) {
if ((rs->num_cm_regions =
BUILD_DEFAULT_LIST)) <= 0) {
"pcram_build_region_lists: "
"socket %d \n"
"\terror building "
"default list\n",
return (0);
/* NOTREACHED */
} /* (BUILD_DEFAULT_LIST) */
} /* (BUILD_SOLARIS_LIST) */
} /* (BUILD_DOS_BPBFAT_LIST) */
} /* CS_NO_CIS */
/*
* There is a CIS - build the lists.
*/
} else {
/*
* Build the AM space list. It is OK to have
* an empty AM space list.
*/
"socket %d \n"
"\terror building AM region list\n",
return (0);
/* NOTREACHED */
}
/*
* Build the CM space list. We need something in here
* for the driver to work at all.
*/
/*
* Found unsupported Device
* Error Return
*/
return (-1);
/* NOTREACHED */
} else {
"pcram_build_region_lists: "
"socket %d \n"
"\terror building CM "
return (0);
/* NOTREACHED */
}
} else if (!rs->num_cm_regions) {
/*
* If we couldn't find any CM regions, the card
* could have a badly-formed CIS.
* Create a default CM region.
*
* XXX - is this the best thing to do, or should
* we just return an error instead?
*/
&rs->cm_regions,
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_CIS) {
"pcram_build_region_lists: socket %d \n"
}
#endif
} /* ValidateCIS */
return (1);
/* NOTREACHED */
}
/*
* pcram_build_region_list - builds a region list for the passed
* memory region
*
* calling: rs - pointer to caller's state structure
* rlist - pointer to a mem_region_t * region
* list pointer
* flags - type of list to build
*
* returns: -1 if region list could not be built; region
* list pointer is not changed
* >=0 number of regions found if region list
* could be built
*
* returns: -2 if card is not supported
*
* Note: If a region list could not be built, the region list must
* be destroyed to prevent any memory leaks. For this
* reason, if an error occurs building the region list,
* the rlist pointer address is not set to NULL since
* it may still have a partial region list.
*/
static int
{
int ret, region_num;
/*
* Make sure that we don't have an existing list hanging
* off of this pointer - if we do, this is an error.
*/
if (*rlist) {
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_CIS) {
}
#endif
return (-1);
/* NOTREACHED */
}
/*
* Do the common setup for a default or DOS partition.
*/
if (flags & (BUILD_DEFAULT_LIST |
mr->region_num = 0;
(void) csx_ConvertSpeed(&convert_speed);
} /* if (BUILD_DEFAULT_LIST | BUILD_DOS_BPBFAT_LIST) */
/*
* See if were being asked to build a default region list.
* If so, build a single region that is as large as the
* max card size and mark it as a default read-only
* region.
* XXX We set the device type to say it's a
* ROM device as well.
*/
if (flags & BUILD_DEFAULT_LIST) {
(void) csx_ConvertSize(&convert_size);
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_CIS) {
"BUILD_DEFAULT_LIST\n"
"\tsize_in_bytes - [%d] size [0x%x]\n",
}
#endif
/*
* Enable default_size_flag so we will probe card
* size when PCRAM_PROBESIZE ioctl() is called.
* This default_size_flag is not enabled when the
* card contains DOS_BPBFAT or Solaris VTOC or
* CIS info.
*/
rs->default_size_flag++;
return (1);
/* NOTREACHED */
}
/*
* Create a list from an MS-DOS BPB-FAT filesystem.
*/
if (flags & BUILD_DOS_BPBFAT_LIST) {
/*
* Check for an MS-DOS BPB-FAT filesystem.
* If it exists, the mem_region_t structure
* will be filled in with the correct values.
*/
return (-1);
/* NOTREACHED */
}
/*
* Convert the device size
* from bytes to a devsize value.
*/
(void) csx_ConvertSize(&convert_size);
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_CIS) {
"BUILD_DOS_BPBFAT_LIST\n"
"\tsize_in_bytes - [%d] size [0x%x]\n",
}
#endif
return (1);
/* NOTREACHED */
}
/*
* Create a list from a Solaris VTOC filesystem.
*/
if (flags & BUILD_SOLARIS_LIST) {
return (-1);
/* NOTREACHED */
}
/*
* Convert the device size
* from bytes to a devsize value.
*/
(void) csx_ConvertSize(&convert_size);
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_CIS) {
"BUILD_SOLARIS_LIST\n"
"\tsize_in_bytes - [%d] size [0x%x]\n",
}
#endif
return (1);
/* NOTREACHED */
}
/*
* We've got a CIS so sort out the correct tuples to look for.
*/
case BUILD_AM_LIST:
break;
case BUILD_CM_LIST:
break;
default:
return (-1);
/* NOTREACHED */
} /* switch (flags) */
/*
* Search for the first device tuple - if it's not found,
* there's no point in searching for anything else.
*/
tuple.Attributes = 0;
!= CS_SUCCESS) {
if (ret != CS_NO_MORE_ITEMS) {
/* this is a real error */
return (-1);
/* NOTREACHED */
} else {
/* XXX - is 0 the right thing to return here? */
return (0);
/* NOTREACHED */
}
}
/*
* Got the device tuple, now parse it.
*/
region_num = 0;
do {
int i;
/*
* We shouldn't ever fail parsing this tuple.
* If we do, there's probably an internal
* error in the CIS parser.
*/
return (-1);
/* NOTREACHED */
}
/*
* We should see at least one region.
* This is definately an error.
*/
if (!cistpl_device.num_devices) {
return (-1);
/* NOTREACHED */
}
for (i = 0; i < cistpl_device.num_devices; i++) {
/*
* IMPORTANT
* setup for next CISTPL_DEVICE tuple
*/
/*
* If this is the first entry in the list,
* then assign it to the head of
* the list pointer.
*/
if (!*rlist) {
} else {
}
}
mrr->size_in_bytes =
/*
* Supporting Common Memory (CM) with
* Masked-ROM(MROM) and Dynamic RAM(DRAM)
*/
if (device_tuple == CISTPL_DEVICE) {
char *unsupported_fmt_string =
"pcram: WARNING - Found unsupported "
"%s device at socket %d\n";
char *supported_fmt_string =
"?pcram: Found %s device at socket %d "
"card size %d\n";
switch (cistpl_device_node->type) {
case CISTPL_DEVICE_DTYPE_SRAM:
/* Support this main device */
break;
case CISTPL_DEVICE_DTYPE_ROM:
/* for VERBOSE mode */
(int)cistpl_device_node->size_in_bytes);
/* Now consider as SRAM type */
break;
case CISTPL_DEVICE_DTYPE_DRAM:
/* for VERBOSE mode */
(int)cistpl_device_node->size_in_bytes);
/* Now consider as SRAM type */
break;
return (-2);
/* NOTREACHED */
return (-2);
/* NOTREACHED */
return (-2);
/* NOTREACHED */
return (-2);
/* NOTREACHED */
default:
return (-2);
/* NOTREACHED */
} /* switch (cistpl_device_node->type) */
} /* if (device_tuple) */
/*
* Initialize the JEDEC information.
* XXX - need to find out what
* reasonable default values are
*/
} /* for (cistpl_device_node->num_devices) */
== CS_SUCCESS);
/*
* If GetNextTuple gave us any error code other than
* CS_NO_MORE_ITEMS, it means that there is probably
* an internal error in the CIS parser.
*/
if (ret != CS_NO_MORE_ITEMS) {
return (-1); /* this is a real error */
/* NOTREACHED */
}
/*
* Now that we've built the region list, search for the
* first JEDEC tuple - if it's not found, that's not
* necessarily an error.
*/
tuple.Attributes = 0;
!= CS_SUCCESS) {
if (ret != CS_NO_MORE_ITEMS) {
/* this is a real error */
return (-1);
/* NOTREACHED */
} else {
return (region_num);
/* NOTREACHED */
}
}
/*
* Got the JEDEC tuple, now parse it.
* We will always get here with a non-NULL
* region list pointer.
*/
do {
int i;
/*
* We shouldn't ever fail parsing this tuple.
* If we do, there's probably an internal
* error in the CIS parser.
*/
return (-1);
/* NOTREACHED */
}
/*
* We should see at least one region definition.
* It is definately an error if we don't see any.
*/
if (!cistpl_jedec.nid) {
return (-1);
/* NOTREACHED */
}
for (i = 0; i < cistpl_jedec.nid; i++) {
/*
* Check the region list pointer;
* if this pointer is NULL, it means
* that we either have an internal
* code error in the way we process
* the device tuples or that the card
* has more JEDEC identifiers than device
* tuple device node entries. We still
* return the number of regions
* found, since this may or may not
* be a serious error.
*/
if (!mr) {
"pcram_build_region_list: socket %d"
"too many JEDEC device entries"
"in %s memory list\n",
"ATTRIBUTE":"COMMON");
return (region_num);
/* NOTREACHED */
}
/*
* Point to the next region in the list.
*/
} /* for (cistpl_jedec.nid) */
&tuple)) == CS_SUCCESS);
/*
* If GetNextTuple gave us any error code other than
* CS_NO_MORE_ITEMS, it means that there is probably
* an internal error in the CIS parser.
*/
if (ret != CS_NO_MORE_ITEMS) {
return (-1); /* this is a real error */
/* NOTREACHED */
}
/*
* Return the number of region entries in this list.
*/
return (region_num);
/* NOTREACHED */
}
/*
* pcram_get_bpbfat_info - scan the CM area looking for an
* MS-DOS BPB-FAT filesystem.
*
* calling: rs - pointer to caller's state structure
* mr - pointer to memory region to fill in
* if BPB found
*
* returns: 0 - if BPB-FAT not found
* 1 - if BPB-FAT found; the mem_region_t struct
* will be initialized with the correct values
*/
static int
{
int tsecvol;
/*
* If there is a BPB-FAT filesystem on the device return 1.
* If there isn't, then just return a 0.
*/
/*
* If there is a way to determine from the BPB-FAT if the
* filesystem is read only, you should set the REGION_READONLY
* flag in the mr->rflags member.
* mr->rflags = (REGION_DOS_BPBFAT | REGION_VALID);
*/
/*
* Note here about the device type - I don't know what
* type to put in here, since it's possible to have a
* BPB-FAT filesystem on a Flash or EEPROM card. You might
* consider doing some sort of testing to see if you can
* determine what type of device is actually in common
* memory.
* I think that you can interrogate many Flash and EEPROM
* devices and they will return their manufacturer and
* device code, or maybe a JEDEC code. Ask Chuck about
* this too. If you can find out that the CM area that
* has the DOS filesystem on it is something other than
* SRAM, you can set the REGION_READONLY flag in
* mr->rflags and possibily also the correct device type
* in the mr->type member.
*
* mr->type = XXX;
*/
/*
* Fill this in with the size of the region after you get this
* information from the BPB-FAT header.
*
* mr->size_in_bytes = XXX;
*/
sizeof (struct bootblock), DDI_DEV_AUTOINCR);
/* Return error if found invalid DOS label */
/* Avoiding zero divide panic when compute drv_ncyl */
/* set to default default size of 64MB */
return (0);
/* NOTREACHED */
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_CIS) {
"\trs->card_size [%d] mr->size_in_bytes [%d]\n",
}
#endif
return (1);
/* NOTREACHED */
}
/* Found NO DOS BPBFAT */
return (0);
/* NOTREACHED */
}
/*
* pcram_get_solaris_info - scan the CM area looking for an
* Solaris partition filesystem.
*
* calling: rs - pointer to caller's state structure
* mr - pointer to memory region to fill in
* if BPB found
*
* returns: 0 - if Solaris VTOC not found
* 1 - if Solaris VTOC found; the mem_region_t struct
* will be initialized with the correct values
*/
static int
{
#ifdef USE_REGION_VALID
#endif
sizeof (struct dk_label), DDI_DEV_AUTOINCR);
/*
* If there is no CIS, try to read dk_label
* to get card size information
*/
/* Return error if found invalid SunOS label */
/* set to default default size of 64MB */
return (0);
/* NOTREACHED */
}
#ifdef PCRAM_DEBUG
if (pcram_debug & PCRAM_DEBUG_CIS) {
"\trs->card_size [%d] mr->size_in_bytes [%d]\n",
}
#endif
return (1);
/* NOTREACHED */
}
/* Found no Solaris partition */
return (0);
/* NOTREACHED */
}
/*
* pcram_destroy_region_lists - destroys all memory region lists
* on the caller's state structure
*
* calling: rs - pointer to caller's state structure
*/
static void
{
&rs->num_am_regions);
&rs->num_cm_regions);
}
/*
* pcram_destroy_region_list - destroys the region list pointed
* to be the rlist pointer
*
* Note: when we return, the region list pointer is set to NULL,
* and the region count is set to zero;
*/
static void
{
return;
/* NOTREACHED */
}
do {
/* LINTED */
*num = 0;
}
/*
* pcram_get_firstnext_region - returns memory region pointer for
* passed region type
*/
static mem_region_t *
{
if (!mrp) {
return (NULL);
/* NOTREACHED */
}
if (*first) {
*first = 0;
do {
return (mrp);
/* NOTREACHED */
}
/* LINTED */
} else {
/*
* This is a get next function.
*/
/* LINTED */
return (mrp);
/* NOTREACHED */
}
}
}
return (NULL);
/* NOTREACHED */
}
static void
{
/*
* XXX - check for non-zero of drv_nhead, drv_secptrack,
* drv_sec_size to avoid zero divide panic
*/
}
#ifdef DEBUG
static void
{
char *event_priority;
char *event_text;
char buf[64];
switch (event) {
event_text = "Registration Complete";
break;
case CS_EVENT_PM_RESUME:
event_text = "Power Management Resume";
break;
case CS_EVENT_CARD_INSERTION:
event_text = "Card Insertion";
break;
case CS_EVENT_CARD_READY:
event_text = "Card Ready";
break;
case CS_EVENT_BATTERY_LOW:
event_text = "Battery Low";
break;
case CS_EVENT_BATTERY_DEAD:
event_text = "Battery Dead";
break;
case CS_EVENT_CARD_LOCK:
event_text = "Card Lock";
break;
case CS_EVENT_PM_SUSPEND:
event_text = "Power Management Suspend";
break;
case CS_EVENT_CARD_RESET:
event_text = "Card Reset";
break;
case CS_EVENT_CARD_UNLOCK:
event_text = "Card Unlock";
break;
event_text = "Ejection Complete";
break;
event_text = "Ejection Request";
break;
case CS_EVENT_ERASE_COMPLETE:
event_text = "Erase Complete";
break;
event_text = "Exclusive Complete";
break;
event_text = "Exclusive Request";
break;
event_text = "Insertion Complete";
break;
event_text = "Insertion Request";
break;
case CS_EVENT_RESET_COMPLETE:
event_text = "Reset Complete";
break;
case CS_EVENT_RESET_PHYSICAL:
event_text = "Reset Physical";
break;
case CS_EVENT_RESET_REQUEST:
event_text = "Reset Request";
break;
case CS_EVENT_MTD_REQUEST:
event_text = "MTD Request";
break;
case CS_EVENT_CLIENT_INFO:
event_text = "Client Info";
break;
case CS_EVENT_TIMER_EXPIRED:
event_text = "Timer Expired";
break;
case CS_EVENT_WRITE_PROTECT:
event_text = "Write Protect";
break;
case CS_EVENT_SS_UPDATED:
event_text = "SS Updated";
break;
case CS_EVENT_STATUS_CHANGE:
event_text = "Status Change";
break;
case CS_EVENT_CARD_REMOVAL:
event_text = "Card Removal";
break;
event_text = "Card Removal Low Power";
break;
default:
event_text = buf;
break;
}
"pcram%d [socket %d]: %s (%s priority)\n",
}
#endif