Contained Within
Find More Documentation
Featured Support Resources
| Descargar este libro en PDF
PCMCIA Parallel Port Driver
9
- This chapter presents the complete code for a PCMCIA parallel port driver (pcepp.c) and shows how a similar driver could be written. The pcepp driver controls a simple interrupt-per-character line printer. This driver is a unidirectional STREAMS driver and has no read-side processing.
- The sample driver has the following main sections:
-
- Driver header files
- Local driver data and system routines and variables
- STREAMS structures
- Character/block, device operations, and modlinkage structures
- Initialization routines
- Autoconfiguration routines
- Card Services routines
- CIS configuration routines
- Device interrupt handler routines
- STREAMS routines
- The pcepp driver source code is included in the Device Driver Developer's Kit.
Include Files and Header Files for Solaris Parallel Port Driver
- The following #include header files and #ifdef preprocessor directives are required for the pcepp driver:
-
-
#include <sys/types.h>
#include <sys/cmn_err.h>
#include <sys/conf.h>
#include <sys/cred.h>
#include <sys/errno.h>
#include <sys/file.h>
#include <sys/termio.h>
#include <sys/termios.h>
#include <sys/kmem.h>
#include <sys/ksynch.h>
#include <sys/modctl.h>
#include <sys/open.h>
#include <sys/stat.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/strtty.h>
#include <sys/uio.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
-
PCMCIA Card Services Header Files
-
-
#include <sys/pccard.h>
-
pcepp Header Files
-
-
#include "pcepp.h"
#if defined(DEBUG)
static intpcepp_debug= 0;
static intpcepp_debug_cis= 0;
static intpcepp_debug_events= 0;
static intpcepp_debug_timeout= 0;
static intpcepp_debug_card_ready= 0;
#definePCEPP_DEBUG(args)if (pcepp_debug) cmn_err args
static void pcepp_display_cftable_list(pcepp_cftable_t *);
static void pcepp_display_card_config(pcepp_state_t *);
#else
#definePCEPP_DEBUG(args)
#endif
Local Driver Data and System Routines and Variables
- The developer defines the local driver data. System routines and variables are defined in the Solaris DDK/DDI. System routines include:
-
- Driver configuration
- Character and block operations
- Card Services interfaces
- Card Services CIS parser interfaces
- STREAMS interfaces
- Interrupt management
-
Local Driver Data
-
-
static void *pcepp_soft_state_p = NULL;
char *pcepp_name = PCEPP_NAME;
-
Autoconfiguration Function Prototypes
-
-
static int pcepp_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd,
void *arg, void **result);
static int pcepp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
static int pcepp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
-
STREAMS Driver open/close Procedures and Function Prototypes
-
-
static int pcepp_open(queue_t *q, dev_t *devp, int flag,
int sflag, cred_t *credp);
static int pcepp_close(queue_t *q, int flag, cred_t *credp);
static int pcepp_ioctl(queue_t *, mblk_t *);
static void pcepp_srvioc(queue_t *, mblk_t *);
-
Card Services Function Prototypes
-
-
static int pcepp_event(event_t, int, event_callback_args_t *);
static int pcepp_card_ready(pcepp_state_t *);
static int pcepp_card_insertion(pcepp_state_t *);
static int pcepp_card_configuration(pcepp_state_t *pps);
static int pcepp_card_removal(pcepp_state_t *);
static void pcepp_card_ready_timeout(pcepp_state_t *);
-
Card Services CIS Parser Function Prototypes
-
-
static int pcepp_parse_cis(pcepp_state_t *, pcepp_cftable_t **);
static void pcepp_destroy_cftable_list(pcepp_cftable_t **);
-
-
static void pcepp_set_cftable_desireability(pcepp_cftable_t *);
static void pcepp_sort_cftable_list(pcepp_cftable_t **);
static void pcepp_swap_cft(pcepp_cftable_t **, pcepp_cftable_t *);
-
STREAMS Driver put Procedures and Function Prototypes
-
-
static int pcepp_rput(queue_t *, mblk_t *);
static int pcepp_wput(queue_t *, mblk_t *);
static void pcepp_start(pcepp_state_t *);
static int pcepp_xmit(pcepp_state_t *);
static void pcepp_strobe_pulse(pcepp_state_t *);
-
Interrupt Handler Function Prototypes
-
-
static u_int pcepp_intr(pcepp_state_t *);
static u_int pcepp_softintr(caddr_t arg);
-
Print Function Prototypes static int pcepp_prtstatus(pcepp_state_t *);
STREAMS Structures
- The pcepp driver is a unidirectional STREAMS driver. Refer to the STREAMS Programmer's Guide for more detailed information.
-
Module Information
-
-
static struct module_info pcepp_minfo = {
PCEPP_IDNUM, /* module id number */
PCEPP_NAME, /* module name */
0, /* min packet size */
INFPSZ, /* max packet size */
PCEPP_HIWAT, /* hi-water mark */
PCEPP_LOWAT /* low-water mark */
};
-
Read QUEUE Structure (qinit)
-
-
static struct qinit pcepp_rinit = {
pcepp_rput, /* put proc */
NULL, /* service proc */
pcepp_open, /* called on startup */
pcepp_close, /* service procedure */
-
-
NULL, /* qadmin */
&pcepp_minfo, /* module information structure */
NULL /* module statistics structure */
};
-
Write QUEUE Structure (qinit)
-
-
static struct qinit pcepp_winit = {
pcepp_wput, /* put proc */
NULL, /* service proc */
NULL, /* qopen */
NULL, /* qclose */
NULL, /* qadmin */
&pcepp_minfo, /* module information structure */
NULL /* module statistics structure */
};
-
STREAMS Entity Declaration Structure (streamtab)
-
-
struct streamtab pcepp_info = {
&pcepp_rinit, /* Read qinit structure */
&pcepp_winit, /* Write qinit structure */
NULL, /* Multiplexor driver - Lower Read */
NULL /* Multiplexor driver - Lower Write */
};
Autoconfiguration and Modlinkage Structures
- The cb_ops(9S) character and block structure defines the entry points for character and block operations of device drivers. Since this is a STREAMS driver, the cb_ops field cd_str must refer to streamtab(9S).
- Along with various autoconfiguration routines, the dev_ops(9S) structure contains a cb_ops(9S) pointer to character or block driver entry points and a bus_ops(9S) pointer to bus operation entry points. The dev_ops(9S) structure allows the kernel to find the autoconfiguration entry points of the device driver.
- The modlinkage(9S) and modldrv(9S) structures are exported to the kernel, enabling drivers to be loadable modules.
-
Character/Block Operations Structure (cb_ops)
-
-
static struct cb_ops pcepp_cb_ops = {
nodev, /* open */
nodev, /* close */
nodev, /* strategy - block devs only */
nodev, /* print - block devs only */
nodev, /* dump - block devs only */
nodev, /* read */
nodev, /* write */
nodev, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* chpoll */
ddi_prop_op, /* prop_op */
&pcepp_info, /* cb_str - STREAMS only */
D_NEW | D_MP, /* driver compatibility flag */
CB_REV, /* cb_rev */
nodev, /* async I/O read entry point */
nodev /* async I/O write entry point */
};
-
Device Operations Structure (dev_ops)
-
-
static struct dev_ops pcepp_ops = {
DEVO_REV, /* driver build version */
0, /* device reference count */
pcepp_getinfo, /* info routine */
nulldev, /* identify routine */
nulldev, /* probe routine */
pcepp_attach, /* attach routine */
pcepp_detach, /* detach routine */
nodev, /* reset routine */
&pcepp_cb_ops, /* cb_ops pntr for leaf dvr */
(struct bus_ops *)NULL /* bus_ops pntr for nexus dvr */
};
-
Module Linkage Information for the Kernel
-
-
extern struct mod_ops mod_driverops;
static struct modldrv modldrv = {
-
-
&mod_driverops, /* Type of module - driver */
PCEPP_DRIVERID, /* Driver identifier string */
&pcepp_ops /* Device operation structure */
};
static struct modlinkage modlinkage = {
MODREV_1, /* rev of loadable modules system */
&modldrv,
NULL
};
Module Initialization Functions
- These routines are focused on resource allocation and resource release. The module initialization functions include:
-
-
_init(9F)
-
_info(9F)
-
_fini(9F)
- Any one-time resource allocation or data initialization should be performed during driver loading in _init(9E). _fini(9E) releases the resources allocated by _init(9E). _info(9E) identifies the loadable module (the device driver).
_init()
-
-
/* _init() is called by modload() */
int
_init(void)
{
register int error;
PCEPP_DEBUG((CE_CONT, "pcepp: _init\n"));
if ((error = ddi_soft_state_init(&pcepp_soft_state_p,
sizeof (pcepp_state_t), 1)) != 0) {
PCEPP_DEBUG((CE_CONT,
"pcepp: _init ddi_soft_state_init failed\n"));
return (error);
}
if ((error = mod_install(&modlinkage)) != 0) {
-
-
PCEPP_DEBUG((CE_CONT,
"pcepp: _init mod_install failed\n"));
ddi_soft_state_fini(&pcepp_soft_state_p);
}
return (error);
}
_info()
-
-
/* _info() is called by modinfo() */
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
_fini()
-
-
/* _fini() is called by modunload() */
int
_fini(void)
{
int error;
PCEPP_DEBUG((CE_CONT, "pcepp: _fini\n"));
if (error = mod_remove(&modlinkage)) {
PCEPP_DEBUG((CE_CONT,
"pcepp: _fini mod_remove failed\n"));
return (error);
}
ddi_soft_state_fini(&pcepp_soft_state_p);
return (error);
}
Autoconfiguration Routines
- Autoconfiguration routines are used to access the soft-state structure of the device, identify the minor number of the device, attach the device to the system, transfer data and commands, and detach the device when it is no longer needed. The autoconfiguration routines are:
-
-
pcepp_getinfo()
-
pcepp_attach()
-
pcepp_detach()
pcepp_getinfo()
-
pcepp_getinfo(9E) is called during module loading and at other times during the life of the driver. It uses csx_CS_DDI_Info(9F) as a mechanism to retrieve the instance information, since PC Card drivers encode the physical socket number as part of the dev_t of device nodes instead of the instance number.
-
-
static int
pcepp_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
void **result)
{
int error = DDI_SUCCESS;
pcepp_state_t *pps;
cs_ddi_info_t cs_ddi_info;
PCEPP_DEBUG((CE_CONT, "pcepp: getinfo\n"));
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
cs_ddi_info.Socket = PCEPP_SOCKET((dev_t)arg);
cs_ddi_info.driver_name = pcepp_name;
if (csx_CS_DDI_Info(&cs_ddi_info) != CS_SUCCESS) {
return (DDI_FAILURE);
}
if (!(pps = ddi_get_soft_state(pcepp_soft_state_p,
cs_ddi_info.instance))) {
*result = NULL;
} else {
*result = pps->dip;
-
-
}
break;
case DDI_INFO_DEVT2INSTANCE:
cs_ddi_info.Socket = PCEPP_SOCKET((dev_t)arg);
cs_ddi_info.driver_name = pcepp_name;
if (csx_CS_DDI_Info(&cs_ddi_info) != CS_SUCCESS) {
return (DDI_FAILURE);
}
*result = (void *)cs_ddi_info.instance;
break;
default:
error = DDI_FAILURE;
break;
} /* switch (cmd) */
return (error);
}
pcepp_attach()
-
attach(9E) is called to attach an instance of the driver. Driver attachment can be specified as a series of steps. These steps are:
-
- Allocate and initialize driver soft-state structure
- Register with Card Services
- Obtain a logical socket number
- Initialize mutex and condition variables
- Install a Card Services event handler and enable event handling
- Check PCMCIA card insertion
- Note that system resource allocation, such as registering device interrupts, is done in the card insertion routine, which is called when the driver receives an insertion event.
-
-
static int
pcepp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int instance;
int ret;
-
-
pcepp_state_t *pps;
/* CardServices variables */
get_status_t get_status;
client_reg_t client_reg;
sockmask_t sockmask;
map_log_socket_t map_log_socket;
instance = ddi_get_instance(dip);
PCEPP_DEBUG((CE_CONT, "pcepp%d: attach\n", instance);
switch (cmd) {
case DDI_ATTACH:
break;
default:
PCEPP_DEBUG((CE_NOTE, "pcepp%d: attach cmd 0x%x\n",
instance, cmd));
return (DDI_FAILURE);
}
PCEPP_DEBUG((CE_CONT, "pcepp%d: attach\n", instance));
-
Allocate and initialize driver soft state associated with this instance
-
-
/* Allocate soft state associated with this instance. */
if (ddi_soft_state_zalloc(pcepp_soft_state_p,
instance) != DDI_SUCCESS) {
PCEPP_DEBUG((CE_CONT, "pcepp%d: attach, "
"could not allocate state structure\n",
instance));
return (DDI_FAILURE);
}
pps = ddi_get_soft_state(pcepp_soft_state_p, instance);
if (pps == NULL) {
PCEPP_DEBUG((CE_CONT, "pcepp%d: attach, "
"could not get state structure\n", instance));
goto out;
}
/* Remember dev_info structure for pcepp_getinfo */
-
-
pps->dip = dip;
pps->instance = instance;
ddi_set_driver_private(dip, (caddr_t)pps);
/* clear driver state flags */
pps->flags = 0;
/*
* Initialize the card event
* when we get a card insertion event
* this card_event will change.
*/
pps->card_event = 0;
-
Register with Card Services
-
-
client_reg.Attributes = INFO_IO_CLIENT |
INFO_CARD_SHARE |
INFO_CARD_EXCL;
client_reg.EventMask = (CS_EVENT_REGISTRATION_COMPLETE |
CS_EVENT_CARD_READY |
CS_EVENT_CARD_INSERTION |
CS_EVENT_CARD_REMOVAL |
CS_EVENT_CARD_REMOVAL_LOWP |
CS_EVENT_CLIENT_INFO);
client_reg.event_handler = (csfunction_t *)pcepp_event;
client_reg.event_callback_args.client_data = pps;
client_reg.Version = CS_VERSION;
client_reg.dip = dip;
(void) strcpy(client_reg.driver_name, PCEPP_NAME);
if ((ret = csx_RegisterClient(&pps->client_handle,
&client_reg)) != CS_SUCCESS) {
error2text_t cft;
cft.item = ret;
csx_Error2Text(&cft);
PCEPP_DEBUG((CE_CONT, "pcepp%d: attach, "
"RegisterClient failed %s (0x%x)\n",
instance, cft.text, ret));
goto out;
}
-
Get logical socket number and store in soft state
-
-
if ((ret = csx_MapLogSocket(pps->client_handle,
&map_log_socket)) != CS_SUCCESS) {
error2text_t cft;
cft.item = ret;
csx_Error2Text(&cft);
PCEPP_DEBUG((CE_CONT, "pcepp%d: attach, "
"MapLogSocket failed %s (0x%x)\n",
instance, cft.text, ret));
}
pps->sn = map_log_socket.PhySocket;
pps->flags |= PCEPP_REGCLIENT;
-
Prepare to receive Card Services events
-
-
/* Set up the event handler hi-level mutex */
mutex_init(&pps->event_hi_mutex, "pps->event_hi_mutex",
MUTEX_DRIVER, *(client_reg.iblk_cookie));
/* set up the mutex to protect the pcepp_state_t */
mutex_init(&pps->event_mutex, "pps->event_mutex",
MUTEX_DRIVER, &pps->softint_cookie);
/* Init readywait_cv */
cv_init(&pps->readywait_cv, "pps->readywait_cv",
CV_DRIVER, (void *)NULL);
pps->flags |= PCEPP_DID_INITMUTEX;
mutex_enter(&pps->event_mutex);
/*
* After the RequestSocketMask call,
* we can start receiving events
*/
sockmask.EventMask = (CS_EVENT_CARD_INSERTION |
CS_EVENT_CARD_REMOVAL);
-
-
if ((ret = csx_RequestSocketMask(pps->client_handle,
&sockmask)) != CS_SUCCESS) {
error2text_t cft;
cft.item = ret;
csx_Error2Text(&cft);
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: attach, "
"RequestSocketMask failed %s (0x%x)\n",
instance, pps->sn, cft.text, ret));
mutex_exit(&pps->event_mutex);
goto out;
}
pps->flags |= PCEPP_REQSOCKMASK;
-
Check card insertion
-
-
/*
* If the card is inserted and this attach is triggered
* by an open, that open will wait until
* pcepp_card_insertion() is completed
*/
pps->card_event |= PCEPP_CARD_WAIT_READY;
while ((pps->card_event &
(PCEPP_CARD_READY | PCEPP_CARD_ERROR)) == 0) {
cv_wait(&pps->readywait_cv, &pps->event_mutex);
}
/*
* PC Card now must be present and fully functional
*/
if (!PCEPP_CARD_IS_READY(pps)) {
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: attach, "
"card not ready\n", instance, pps->sn));
mutex_exit(&pps->event_mutex);
goto out;
}
mutex_exit(&pps->event_mutex);
ddi_report_dev(dip);
return (DDI_SUCCESS);
-
-
out:
(void) pcepp_detach(dip, DDI_DETACH);
return (DDI_FAILURE);
}
pcepp_detach
-
detach(9E) is the inverse operation to attach(9E). It is called for each device instance, receiving a command of DDI_DETACH, when the system attempts to unload a driver module. The system only calls the DDI_DETACH case of detach(9E) for a device instance if the device instance is not open. No calls to other driver entry points for that device instance occur during detach(9E), although interrupts and time-outs may occur.
- The main purpose of detach(9E) is to free resources allocated by attach(9E) for the specified device and to prepare the driver for unloading.
- Driver detachment can be defined as a series of steps. These steps are:
-
- Obtain the soft-state structure
- Call pcepp_card_removal to handle final cleanup
- Release driver socket masks
- Deregister with Card Services
- Free soft state
-
-
static int
pcepp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
int instance;
int ret;
pcepp_state_t *pps;
-
Obtain soft-state structure
-
-
instance = ddi_get_instance(dip);
switch (cmd) {
case DDI_DETACH:
break;
default:
PCEPP_DEBUG((CE_NOTE, "pcepp%d: detach cmd 0x%x\n",
-
-
instance, cmd));
return (DDI_FAILURE);
}
PCEPP_DEBUG((CE_CONT, "pcepp%d: detach\n", instance));
pps = ddi_get_soft_state(pcepp_soft_state_p, instance);
if (pps == NULL) {
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: detach, "
"could not get state structure\n",
instance, pps->sn));
return (DDI_FAILURE);
}
-
Call card removal to do final cleanup
-
-
/*
* Call pcepp_card_removal to do final card cleanup
*/
if (pps->card_event & PCEPP_CARD_READY) {
mutex_enter(&pps->event_mutex);
(void) pcepp_card_removal(pps);
mutex_exit(&pps->event_mutex);
}
-
Release driver socket mask
-
-
/* Release driver socket mask */
if (pps->flags & PCEPP_REQSOCKMASK) {
release_socket_mask_t rsm;
if ((ret = csx_ReleaseSocketMask(pps->client_handle,
&rsm)) != CS_SUCCESS) {
error2text_t cft;
cft.item = ret;
csx_Error2Text(&cft);
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: attach, "
"ReleaseSocketMask failed %s (0x%x)\n",
instance, pps->sn, cft.text, ret));
}
}
-
Deregister with Card Services
-
-
/*
* Deregister with Card Services
* we will stop getting events at this point.
*/
if (pps->flags & PCEPP_REGCLIENT) {
if ((ret = csx_DeregisterClient(pps->client_handle))
!= CS_SUCCESS) {
error2text_t cft;
cft.item = ret;
csx_Error2Text(&cft);
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: attach, "
"DeregisterClient failed %s (0x%x)\n",
instance, pps->sn, cft.text, ret));
}
}
-
Free mutex and condition variables
-
-
/* Finally free the various mutex and condition variables */
if (pps->flags & PCEPP_DID_INITMUTEX) {
mutex_destroy(&pps->event_hi_mutex);
mutex_destroy(&pps->event_mutex);
cv_destroy(&pps->readywait_cv);
}
-
Free soft state
-
-
ddi_soft_state_free(pcepp_soft_state_p, instance);
return (DDI_SUCCESS);
}
Card Services Routines
- Card Services routines are used to allocate resources and manage events for PC Card drivers. The pcepp sample driver shows the use of the following routines:
-
- Event handler - pcepp_event()
- Card readiness - pcepp_card_ready()
- Card insertion - pcepp_card_insertion()
- Card configuration - pcepp_card_configuration()
- Card ready time-out - pcepp_card_ready_timeout()
- Card removal - pcepp_card_removal()
pcepp_event()
- Card Services calls the csx_event_handler(9E) driver entry point to notify the driver of an event, such as card insertion, card ready, or card removal.
-
-
int
pcepp_event(event_t event, int priority,
event_callback_args_t *eca)
{
int retcode = CS_UNSUPPORTED_EVENT;
pcepp_state_t *pps = eca->client_data;
client_info_t *ci = &eca->client_info;
#if defined(DEBUG)
if (pcepp_debug_events) {
event2text_tevent2text;
event2text.event = event;
csx_Event2Text(&event2text);
cmn_err(CE_CONT, "pcepp%d.%d: %s (%s priority)\n",
pps->instance, (int)pps->sn,
event2text.text, (priority & CS_EVENT_PRI_HIGH) ?
"high" : "low");
}
#endif
/*
* If the priority argument indicates CS_EVENT_PRI_HIGH,
* the callback handler is being called at the nexus
-
-
* event interrupt priority which is above lock-level.
* You cannot use a mutex defined at a lower priority
* for the high priority event. At the same time,
* you do not want to use the high priority mutex
* when it is not necessary since it blocks out other
* interrupts while held.
*/
if (priority & CS_EVENT_PRI_HIGH) {
/*
* Use pps->event_hi_mutex mutex for a high priority
* event to modify the card event state.
*/
mutex_enter(&pps->event_hi_mutex);
} else {
/*
* Use pps->event_mutex for a low priority event
* to modify the driver state structure.
*/
mutex_enter(&pps->event_mutex);
}
-
Determine event received and act accordingly
-
-
/*
* Find out which event we got and do the right thing
*/
switch (event) {
/*
* When the driver registers with CardServices
* through csx_RegisterClient() function,
* CardServices saves the client information
* and immediately returns to the client.
* CardServices then performs the registration
* in the background. When the registration
* processing is completed, CS notifies
* the request client through pcepp_event
* with CS_EVENT_REGISTRATION_COMPLETE event.
*/
case CS_EVENT_REGISTRATION_COMPLETE:
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: event, "
"Registration is completed\n",
pps->instance, pps->sn));
-
-
retcode = CS_SUCCESS;
break;
/*
* This event indicates that a ready/busy signal,
* +RDY/-BSY, has transitioned from the busy
* state to ready state.
* It is available as a low priority event.
*/
case CS_EVENT_CARD_READY:
ASSERT(priority & CS_EVENT_PRI_LOW);
retcode = pcepp_card_ready(pps);
break;
/*
* This event indicates a PC Card has been inserted
* in a socket or a client has just registered
* for insertion events.
* It is available as a low priority event.
*/
case CS_EVENT_CARD_INSERTION:
ASSERT(priority & CS_EVENT_PRI_LOW);
retcode = pcepp_card_insertion(pps);
break;
/*
* This event indicates a PC Card has been removed
* from a socket.
* It is available as both a low priority
* and high priority event. As a high priority
* event the client is expected to set the
* internal state the card has been removed,
* and that the event at low priority will be
* used to release any resources allocated
* by the client.
*/
case CS_EVENT_CARD_REMOVAL:
case CS_EVENT_CARD_REMOVAL_LOWP:
if (priority & CS_EVENT_PRI_HIGH) {
pps->card_event &= ~PCEPP_CARD_INSERTED;
retcode = CS_SUCCESS;
} else {
-
-
retcode = pcepp_card_removal(pps);
}
break;
/*
* This event requests that the client return its
* client information data such as the driver
* revision level and date.
*/
case CS_EVENT_CLIENT_INFO:
if (GET_CLIENT_INFO_SUBSVC(ci->Attributes) ==
CS_CLIENT_INFO_SUBSVC_CS) {
ci->Attributes |= CS_CLIENT_INFO_VALID;
ci->Revision = PCEPP_REV_LEVEL;
ci->CSLevel = CS_VERSION;
ci->RevDate = PCEPP_REV_DATE;
strcpy(ci->ClientName,
PCEPP_CLIENT_DESCRIPTION);
strcpy(ci->VendorName,
PCEPP_VENDOR_DESCRIPTION);
/*
* Note that PC Card driver does not
* need to initialize DriverName
* field since Card Services will
* copy the name of PC card driver
* into this space after the PC
* Card driver has sucessfully
* processed this event.
*/
retcode = CS_SUCCESS;
} /* CS_CLIENT_INFO_SUBSVC_CS */
break;
default:
retcode = CS_UNSUPPORTED_EVENT;
break;
} /* switch(event) */
if (priority & CS_EVENT_PRI_HIGH) {
mutex_exit(&pps->event_hi_mutex);
} else {
mutex_exit(&pps->event_mutex);
-
-
}
return (retcode);
}
pcepp_card_ready
-
pcepp_card_ready() is called from the event handler in response to a card ready event.
-
-
static int
pcepp_card_ready(pcepp_state_t *pps)
{
int ret;
modify_config_t modify_config;
ASSERT(mutex_owned(&pps->event_mutex));
#if defined(DEBUG)
/* Debugging to verify card ready timeout handling */
if (pcepp_debug_timeout) {
cmn_err(CE_CONT, "pcepp%d.%d: ignoring card_ready\n",
pps->instance, (int)pps->sn);
return (CS_SUCCESS);
}
#endif
/* Remove any pending card ready timer */
if (pps->ready_timeout_id) {
int id = pps->ready_timeout_id;
pps->ready_timeout_id = 0;
mutex_exit(&pps->event_mutex);
untimeout(id);
mutex_enter(&pps->event_mutex);
}
/*
* If our card is just now becoming ready,
* perform basic card configuration and initialization
*/
if ((pps->card_event & PCEPP_CARD_READY) == 0) {
-
-
if (pcepp_card_configuration(pps) == 0) {
pps->card_event |= PCEPP_CARD_ERROR;
if (pps->card_event & PCEPP_CARD_WAIT_READY) {
pps->card_event &= ~PCEPP_CARD_WAIT_READY;
ASSERT(mutex_owned(&pps->event_mutex));
cv_broadcast(&pps->readywait_cv);
}
return (CS_SUCCESS);
}
/*
* CONF_ENABLE_IRQ_STEERING is used to enable the
* PC Card interrupt, and CONF_IRQ_CHANGE_VALID
* is used to request the IRQ steering enable to
* be changed.
*/
modify_config.Attributes =
(CONF_IRQ_CHANGE_VALID |
CONF_ENABLE_IRQ_STEERING);
if ((ret = csx_ModifyConfiguration(pps->client_handle,
&modify_config)) != CS_SUCCESS) {
error2text_t cft;
cft.item = ret;
csx_Error2Text(&cft);
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: card_ready, "
"ModifyConfiguration (IRQ) failed %s\n",
pps->instance, (int)pps->sn, cft.text));
pps->card_event |= PCEPP_CARD_ERROR;
} /* ModifyConfiguration */
pps->card_event |= PCEPP_CARD_READY;
if (pps->card_event & PCEPP_CARD_WAIT_READY) {
pps->card_event &= ~PCEPP_CARD_WAIT_READY;
ASSERT(mutex_owned(&pps->event_mutex));
cv_broadcast(&pps->readywait_cv);
}
}
return (CS_SUCCESS);
}
pcepp_card_insertion
- This function is called from the event handler in response to a card insertion event.
-
-
static int
pcepp_card_insertion(pcepp_state_t *pps)
{
int ret = CS_OUT_OF_RESOURCE;
get_status_t get_status;
ASSERT(mutex_owned(&pps->event_mutex));
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: card_insertion, "
pps->instance, pps->sn));
pps->card_event &= ~(PCEPP_CARD_READY | PCEPP_CARD_ERROR);
pps->card_event |= PCEPP_CARD_INSERTED;
/* Get card state status */
if ((ret = csx_GetStatus(pps->client_handle, &get_status))
!= CS_SUCCESS) {
error2text_t cft;
cft.item = ret;
csx_Error2Text(&cft);
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: card_insertion, "
"csx_GetStatus failed %s (0x%x)\n",
pps->instance, (int)pps->sn, cft.text, ret));
}
if ((get_status.CardState & CS_EVENT_CARD_READY) == 0) {
#if defined(DEBUG)
/*
* Debugging to verify card ready timeout handling
*/
if (!(pcepp_debug_timeout || pcepp_debug_card_ready)) {
(void) pcepp_card_ready(pps);
}
#else
(void) pcepp_card_ready(pps);
#endif
-
-
}
if ((pps->card_event &
(PCEPP_CARD_READY | PCEPP_CARD_ERROR)) == 0) {
/*
* If the card isn't ready yet, we set up a
* timeout handler to trap cards that never
* do give us a Card Ready event, and then
* return to wait for either the Card Ready
* event or the timeout to expire.
*/
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: card_insertion, "
"PC Card is NOT ready\n",
pps->instance, pps->sn));
pps->ready_timeout_id =
timeout(pcepp_card_ready_timeout, (caddr_t)pps,
(long)(drv_usectohz(1000000) *
PCEPP_READY_TIMEOUT));
}
return (CS_SUCCESS);
}
pcepp_card_configuration
- This function is called from the event handler to allocate system resources. Card configuration can be thought of as a series of steps. These steps include:
-
- Parse CIS to configure the card
- Allocate IO and IRQ resources
- Add the driver's soft interrupt handler
- Set up the client event mask
- Call csx_RequestConfiguration(9F) to make the resources active
- Create the minor device
- Note that these resources are deallocated when the card is removed and reallocated when the card is inserted again.
-
-
static int
pcepp_card_configuration(pcepp_state_t *pps)
{
int ret;
char devname[16];
char *dname;
io_req_t io_req;
irq_req_t irq_req;
get_status_t get_status;
sockevent_t sockevent;
config_req_t config_req;
make_device_node_t make_device_node;
devnode_desc_t *dnd;
pcepp_cftable_t *cftable = NULL;
pcepp_cftable_t *cft;
pcepp_cis_vars_t *cis_vars = &pps->cis_vars;
ASSERT(mutex_owned(&pps->event_mutex));
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: card_configuration\n",
pps->instance, pps->sn));
-
Determine whether card is ready
-
-
/* Get card state status */
if ((ret = csx_GetStatus(pps->client_handle, &get_status))
!= CS_SUCCESS) {
error2text_t cft;
cft.item = ret;
csx_Error2Text(&cft);
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: card_insertion, "
"csx_GetStatus failed %s (0x%x)\n",
pps->instance, (int)pps->sn, cft.text, ret));
}
if ((get_status.CardState & CS_EVENT_CARD_READY) == 0) {
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: card_insertion, "
"PC Card is NOT ready (0x%x)\n",
pps->instance, pps->sn));
return (0);
}
-
Parse PC Card CIS information
-
-
/* Get PC Card CIS information */
if ((ret = pcepp_parse_cis(pps, &cftable)) != CS_SUCCESS) {
cmn_err(CE_CONT, "pcepp%d.%d: card_insertion, "
"unable to get CIS information (0x%x)\n",
pps->sn, ret);
(void) pcepp_destroy_cftable_list(&cftable);
return (0);
}
-
Allocate I/O resources
-
-
/*
* Try to allocate IO resources; if we fail to get
* an IO range from the system, then we exit
* since there's not much we can do.
*/
cft = cftable;
while (cft) {
io_req.BasePort1.base = cft->p.card_base1;
io_req.NumPorts1 = cft->p.length1 + 1;
io_req.Attributes1 = (IO_DATA_PATH_WIDTH_8 |
WIN_ACC_NEVER_SWAP |
WIN_ACC_STRICT_ORDER);
io_req.BasePort2.base = 0;
io_req.NumPorts2 = 0;
io_req.Attributes2 = 0;
io_req.IOAddrLines = cft->p.addr_lines;
if ((ret = csx_RequestIO(pps->client_handle, &io_req))
== CS_SUCCESS) {
/*
* We found a good IO range, so save the
*information and break out of this loop
*/
cis_vars->card_base1 = cft->p.card_base1;
cis_vars->length1 = cft->p.length1;
cis_vars->card_base2 = cft->p.card_base2;
cis_vars->length2 = cft->p.length2;
cis_vars->addr_lines = cft->p.addr_lines;
-
-
cis_vars->card_vcc = cft->p.card_vcc;
cis_vars->card_vpp1 = cft->p.card_vpp1;
cis_vars->card_vpp2 = cft->p.card_vpp2;
cis_vars->pin = cft->p.pin;
cis_vars->config_index = cft->p.config_index;
break;
} /* RequestIO */
cft = cft->next;
} /* while (cft) */
/*
* Now destroy the config table entries list since
* we don't need it anymore.
*/
pcepp_destroy_cftable_list(&cftable);
/*
* If we weren't able to get an IO range, then
* report that to the user and return.
*/
if (!cft) {
cmn_err(CE_CONT, "pcepp%d.%d: card_insertion, "
"socket %d unable to get IO range\n",
(int)pps->sn);
return (0);
}
/* Normal eight contiguous registers */
pps->card_handle = io_req.BasePort1.handle;
pps->flags |= PCEPP_REQUESTIO;
-
Allocate an IRQ
-
-
/*
* Allocate an IRQ
*/
irq_req.Attributes = IRQ_TYPE_EXCLUSIVE;
irq_req.irq_handler = (csfunction_t *)pcepp_intr;
irq_req.irq_handler_arg = (caddr_t)pps;
-
-
if ((ret = csx_RequestIRQ(pps->client_handle, &irq_req))
!= CS_SUCCESS) {
error2text_t cft;
cft.item = ret;
csx_Error2Text(&cft);
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: card_insertion, "
"RequestIRQ failed %s (0x%x)\n",
pps->instance, (int)pps->sn, cft.text, ret));
return (0);
} /* RequestIRQ */
-
Initialize the Card Register mutex
-
-
/*
* Initialize the interrupt mutex for protecting
* the card registers.
*/
mutex_init(&pps->irq_mutex, "pps->irq_mutex", MUTEX_DRIVER,
*(irq_req.iblk_cookie));
pps->flags |= PCEPP_REQUESTIRQ;
-
Add soft interrupt handler
-
-
/*
* Add low level soft interrupt handler
* It is used in pcepp_intr() when ddi_trigger_softintr()
* is called to start pcepp_softintr() for transferring
* next data.
*/
ret = ddi_add_softintr(pps->dip, PCEPP_SOFT_PREF,
&pps->softint_id, &pps->softint_cookie,
(ddi_idevice_cookie_t *)0, pcepp_softintr,
(caddr_t)pps);
if (ret != DDI_SUCCESS) {
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: card_insertion, "
"ddi_add_softintr failed\n",
pps->instance, pps->sn));
return (0);
}
pps->flags |= PCEPP_SOFTINTROK;
-
Prepare for card ready and other registered events
-
-
/*
* Set up the client event mask to give us card ready
* 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 pcepp_attach, we don't have to
* duplicate those events in this event mask.
*/
sockevent.Attributes = CONF_EVENT_MASK_CLIENT;
if ((ret = csx_GetEventMask(pps->client_handle,
&sockevent)) != CS_SUCCESS) {
error2text_t cft;
cft.item = ret;
csx_Error2Text(&cft);
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: card_insertion, "
"GetEventMask failed %s (0x%x)\n",
pps->instance, (int)pps->sn, cft.text, ret));
return (0);
} /* GetEventMask */
sockevent.EventMask |= CS_EVENT_CARD_READY;
if ((ret = csx_SetEventMask(pps->client_handle, &sockevent))
!= CS_SUCCESS) {
error2text_t cft;
cft.item = ret;
csx_Error2Text(&cft);
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: card_insertion, "
"SetEventMask failed %s (0x%x)\n",
pps->instance, (int)pps->sn, cft.text, ret));
return (0);
} /* SetEventMask */
-
Configure the PC Card
-
-
/*
* Configure the PC Card
*/
config_req.Attributes = 0;
-
-
config_req.Vcc = cis_vars->card_vcc;
config_req.Vpp1 = cis_vars->card_vpp1;
config_req.Vpp2 = cis_vars->card_vpp2;
config_req.IntType = SOCKET_INTERFACE_MEMORY_AND_IO;
config_req.ConfigBase = cis_vars->config_base;
config_req.Status = 0;
config_req.Pin = cis_vars->pin;
config_req.Copy = 0;
config_req.ConfigIndex = cis_vars->config_index;
config_req.Present = cis_vars->present;
if ((ret = csx_RequestConfiguration(pps->client_handle,
&config_req)) != CS_SUCCESS) {
error2text_t cft;
cft.item = ret;
csx_Error2Text(&cft);
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: card_insertion, "
"RequestConfiguration failed %s (0x%x)\n",
pps->instance, (int)pps->sn, cft.text, ret));
return (0);
} /* RequestConfiguration */
pps->flags |= PCEPP_REQUESTCONFIG;
#if defined(DEBUG)
if (pcepp_debug_cis) {
pcepp_display_card_config(pps);
}
#endif
-
Create minor devices for this instance
-
-
/*
* Create the minor devices for this instance
* The minor number is the socket number
*/
dname = devname;
sprintf(dname, "pcepp");
make_device_node.Action = CREATE_DEVICE_NODE;
make_device_node.NumDevNodes = 1;
-
-
make_device_node.devnode_desc =
kmem_zalloc(sizeof (struct devnode_desc) *
make_device_node.NumDevNodes, KM_SLEEP);
dnd = &make_device_node.devnode_desc[0];
dnd->name = dname;
dnd->spec_type = S_IFCHR;
dnd->minor_num = pps->sn;
dnd->node_type = PCEPP_NT_PARALLEL;
if ((ret = csx_MakeDeviceNode(pps->client_handle,
&make_device_node)) != CS_SUCCESS) {
error2text_t cft;
cft.item = ret;
csx_Error2Text(&cft);
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: card_insertion, "
"MakeDeviceNode failed %s (0x%x)\n",
pps->instance, (int)pps->sn, cft.text, ret));
return (0);
} /* SetEventMask */
/*
* 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'll want to keep this structure and the
* make_device_node_t structure around in a per-instance
* data area.
*/
kmem_free(make_device_node.devnode_desc,
sizeof (struct devnode_desc) *
make_device_node.NumDevNodes);
make_device_node.devnode_desc = NULL;
pps->flags |= PCEPP_MAKEDEVICENODE;
return (1);
}
pcepp_card_ready_timeout()
- This function detects a card that fails to become ready within a reasonable amount of time.
-
-
static void
pcepp_card_ready_timeout(pcepp_state_t *pps)
{
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: card ready timeout\n",
pps->instance, pps->sn));
mutex_enter(&pps->event_mutex);
if (pps->ready_timeout_id) {
pps->ready_timeout_id = 0;
pps->card_event |= PCEPP_CARD_READY | PCEPP_CARD_ERROR;
}
if (pps->card_event & PCEPP_CARD_WAIT_READY) {
pps->card_event &= ~PCEPP_CARD_WAIT_READY;
cv_broadcast(&pps->readywait_cv);
}
mutex_exit(&pps->event_mutex);
}
pcepp_card_removal()
- This function is called from the event handler in response to a card removal event. Card removal can be broken down into a series of steps. These steps are:
-
- Set up the client event mask so that the driver will not receive the card ready events
- Call csx_ModifyConfiguration() to turn off the power
- Release the card configuration
- Release allocated IRQ resources
- Release allocated IO resources
- Remove the minor devices
-
-
static int
pcepp_card_removal(pcepp_state_t *pps)
{
int ret;
sockevent_t sockevent;
-
-
ASSERT(mutex_owned(&pps->event_mutex));
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: card_removal\n",
pps->instance, pps->sn));
/*
* Remove any pending card ready timer
*/
if (pps->ready_timeout_id) {
int id = pps->ready_timeout_id;
pps->ready_timeout_id = 0;
mutex_exit(&pps->event_mutex);
untimeout(id);
mutex_enter(&pps->event_mutex);
}
/*
* First, grab the I/O mutex; this will cause us to
* spin until any pending I/O interrupts have completed.
* Dropping the Card Inserted state (already occurred)
* ensures that any interrupt triggered after
* we get the locks will return without initiating
* any activity on the device or even acquiring the I/O
* mutexes. This is important since our device may be
* sharing the interrupt line with other devices and
* we don't want to stall or interfere with those
* devices in any way. Our device cannot be generating
* any interrupts since it isn't there anymore.
*/
pps->card_event &= ~PCEPP_CARD_INSERTED;
if (pps->flags & PCEPP_REQUESTIRQ) {
mutex_enter(&pps->irq_mutex);
}
pps->card_event &= ~(PCEPP_CARD_READY | PCEPP_CARD_ERROR |
PCEPP_CARD_WAIT_READY);
/*
* Now that we are sure our interrupt handlers won't
* attempt to initiate any activity, we can continue
* with freeing this card's I/O resources.
-
-
*/
if (pps->flags & PCEPP_REQUESTIRQ) {
mutex_exit(&pps->irq_mutex);
}
-
Set up client event mask
-
-
/*
* Set up the client event mask to NOT give us card ready
* events; we will still receive other events we have
* registered for.
* Note that since we set the global event mask in call
* to RegisterClient in pcepp_attach, we don't have
* to duplicate those events in this event mask.
*/
sockevent.Attributes = CONF_EVENT_MASK_CLIENT;
if ((ret = csx_GetEventMask(pps->client_handle,
&sockevent)) != CS_SUCCESS) {
error2text_t cft;
cft.item = ret;
csx_Error2Text(&cft);
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: card_removal, "
"GetEventMask failed %s (0x%x)\n",
pps->instance, (int)pps->sn, cft.text, ret));
pps->card_event &= ~PCEPP_CARD_BUSY;
pps->flags &= ~PCEPP_ISOPEN;
return (ret);
} /* GetEventMask */
sockevent.EventMask &= ~CS_EVENT_CARD_READY;
if ((ret = csx_SetEventMask(pps->client_handle, &sockevent))
!= CS_SUCCESS) {
error2text_t cft;
cft.item = ret;
csx_Error2Text(&cft);
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: card_removal, "
"SetEventMask failed %s (0x%x)\n",
pps->instance, (int)pps->sn, cft.text, ret));
pps->card_event &= ~PCEPP_CARD_BUSY;
pps->flags &= ~PCEPP_ISOPEN;
return (ret);
-
-
} /* SetEventMask */
if (pps->flags & PCEPP_REQUESTCONFIG) {
modify_config_t modify_config;
release_config_t release_config;
-
Turn off VPP1 and VPP2
-
-
/*
* First, turn off VPP1 and VPP2 since we
* don't need them anymore. This will
* take care of the case of the driver
* being unloaded but the card still
* being present.
*/
modify_config.Attributes =
(CONF_VPP1_CHANGE_VALID |
CONF_VPP2_CHANGE_VALID);
modify_config.Vpp1 = 0;
modify_config.Vpp2 = 0;
ret = csx_ModifyConfiguration(pps->client_handle,
&modify_config);
if ((ret != CS_NO_CARD) && (ret != CS_SUCCESS)) {
error2text_t cft;
cft.item = ret;
csx_Error2Text(&cft);
PCEPP_DEBUG((CE_CONT,
"pcepp%d.%d: card_removal, "
"ModifyConfiguration "
"(Vpp1/Vpp2) failed %s (0x%x)\n",
pps->instance, (int)pps->sn,
cft.text, ret));
} /* ModifyConfig != (CS_NO_CARD || CS_SUCCESS) */
-
Release card configuration
-
-
if ((ret = csx_ReleaseConfiguration(pps->client_handle,
&release_config)) != CS_SUCCESS) {
error2text_t cft;
-
-
cft.item = ret;
csx_Error2Text(&cft);
PCEPP_DEBUG((CE_CONT,
"pcepp%d.%d: card_removal, "
"ReleaseConfiguration "
"failed %s (0x%x)\n",
pps->instance, (int)pps->sn,
cft.text, ret));
} /* ReleaseConfiguration */
pps->flags &= ~PCEPP_REQUESTCONFIG;
} /* PCEPP_REQUESTCONFIG */
-
Release allocated IRQ resources
-
-
/*
* Unregister the softinterrupt handler
*/
if (pps->flags & PCEPP_SOFTINTROK) {
ddi_remove_softintr(pps->softint_id);
pps->flags &= ~PCEPP_SOFTINTROK;
}
if (pps->flags & PCEPP_REQUESTIRQ) {
irq_req_t irq_req;
if ((ret = csx_ReleaseIRQ(pps->client_handle,
&irq_req)) != CS_SUCCESS) {
error2text_t cft;
cft.item = ret;
csx_Error2Text(&cft);
PCEPP_DEBUG((CE_CONT,
"pcepp%d.%d: card_removal, "
"ReleaseIRQ failed %s (0x%x)\n",
pps->instance, (int)pps->sn,
cft.text, ret));
} /* ReleaseIRQ */
/*
* destroy mutex
*/
mutex_destroy(&pps->irq_mutex);
pps->flags &= ~PCEPP_REQUESTIRQ;
-
-
} /* PCEPP_REQUESTIRQ */
-
Release allocated I/O resources
-
-
if (pps->flags & PCEPP_REQUESTIO) {
io_req_t io_req;
if ((ret = csx_ReleaseIO(pps->client_handle,
&io_req)) != CS_SUCCESS) {
error2text_t cft;
cft.item = ret;
csx_Error2Text(&cft);
PCEPP_DEBUG((CE_CONT,
"pcepp%d.%d: card_removal, "
"ReleaseIO failed %s (0x%x)\n",
pps->instance, (int)pps->sn,
cft.text, ret));
} /* ReleaseIO */
pps->flags &= ~PCEPP_REQUESTIO;
} /* PCEPP_REQUESTIO */
-
Remove minor devices
-
-
if (pps->flags & PCEPP_MAKEDEVICENODE) {
remove_device_node_t remove_device_node;
remove_device_node.Action = REMOVAL_ALL_DEVICE_NODES;
remove_device_node.NumDevNodes = 0;
if ((ret = csx_RemoveDeviceNode(pps->client_handle,
&remove_device_node)) != CS_SUCCESS) {
error2text_t cft;
cft.item = ret;
csx_Error2Text(&cft);
PCEPP_DEBUG((CE_CONT,
"pcepp%d.%d: card_removal, "
"RemoveDeviceNode failed %s (0x%x)\n",
pps->instance, (int)pps->sn,
cft.text, ret));
} /* MakeDeviceNode */
pps->flags &= ~PCEPP_MAKEDEVICENODE;
-
-
} /* PCEPP_MAKEDEVICENODE */
pps->card_event &= ~PCEPP_CARD_BUSY;
pps->flags &= ~PCEPP_ISOPEN;
return (CS_SUCCESS);
}
CIS Configuration Information Routines
- The CIS configuration routines parse the CIS tuples and include support routines for tuple table configuration management. The following pcepp sample driver routines show the use of Card Services to parse CIS information.
-
-
pcepp_parse_cis()
-
pcepp_display_cftable_list()
-
pcepp_display_card_config()
-
pcepp_destroy_cftable_list()
-
pcepp_set_cftable_desireability()
-
pcepp_sort_cftable_list()
-
pcepp_swap_cft()
pcepp_parse_cis()
- This function searches the CIS for device data contained in CISTPL structures to configure the card. The function uses csx_GetFIrstTuple() and csx_GetNextTuple() to find the desired tuple and uses the following CIS parser functions to process the tuple:
-
-
csx_Parse_CISTPL_CONFIG() - Parses the configuration tuple
-
csx_Parse_CISTPL_VERS_1() - Parses the version number tuple
-
csx_Parse_CISTPL_MANFID() - Parses the manufacturer information tuple
-
csx_Parse_CISTPL_CFTABLE_ENTRY() - Parses the configuration table entry tuple
-
csx_Parse_CISTPL_FUNCID() - Parses the function identification tuple
-
csx_Parse_CISTPL_FUNCE() - Parses the function extension tuple
-
-
static int
pcepp_parse_cis(pcepp_state_t *pps, pcepp_cftable_t **cftable)
{
int i;
int ret;
int last_config_index;
int default_cftable;
tuple_t tuple;
cistpl_cftable_entry_t cistpl_cftable_entry;
cistpl_cftable_entry_io_t *io =&cistpl_cftable_entry.io;
pcepp_cis_vars_t *cis_vars = &pps->cis_vars;
pcepp_cftable_t *cft, *dcft, *ocft, defcft;
cistpl_config_t cistpl_config;
cistpl_vers_1_t cistpl_vers_1;
cistpl_manfid_t cistpl_manfid;
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: parse_cis\n",
pps->instance, pps->sn));
/*
* Clear the CIS saving information structure
*/
bzero(cis_vars, sizeof (pcepp_cis_vars_t));
-
Parse CISTPL_CONFIG Tuple
-
-
/*
* CISTPL_CONFIG processing.
* Search for the first config tuple so that we
* can get a pointer to the card's configuration
* registers. If this tuple is not found, there's
* no point in searching for anything else.
*/
bzero(&tuple, sizeof (tuple));
tuple.DesiredTuple = CISTPL_CONFIG;
if ((ret = csx_GetFirstTuple(pps->client_handle, &tuple))
!= CS_SUCCESS) {
cmn_err(CE_CONT, "pcepp%d.%d: parse_cis, "
"CISTPL_CONFIG tuple not found (0x%x)\n",
pps->instance, pps->sn, ret);
return (ret);
}
-
-
bzero(&cistpl_config, sizeof (struct cistpl_config_t));
if ((ret = csx_Parse_CISTPL_CONFIG(pps->client_handle,
&tuple,
&cistpl_config)) != CS_SUCCESS) {
cmn_err(CE_CONT, "pcepp%d.%d: parse_cis, "
"csx_Parse_CISTPL_CONFIG failed (0x%x)\n",
pps->instance, pps->sn, ret);
return (ret);
} else {
/*
* This is the last CISTPL_CFTABLE_ENTRY
* tuple index that we need to look at.
*/
last_config_index = cistpl_config.last;
if (cistpl_config.nr) {
cis_vars->config_base = cistpl_config.base;
cis_vars->present = cistpl_config.present;
} else {
cmn_err(CE_CONT, "pcepp%d.%d: parse_cis, "
"CISTPL_CONFIG no configuration "
"registers found\n", pps->sn);
return (CS_BAD_CIS);
}
}
-
Parse CISTPL_VERS_1 Tuple
-
-
/*
* CISTPL_VERS_1 processing.
* Get major/minor version and
* product information strings
*/
bzero(&tuple, sizeof (tuple));
tuple.DesiredTuple = CISTPL_VERS_1;
if ((ret = csx_GetFirstTuple(pps->client_handle, &tuple))
!= CS_SUCCESS) {
cmn_err(CE_CONT, "pcepp%d.%d: parse_cis, "
"CISTPL_VERS_1 tuple not found (0x%x)\n",
pps->instance, pps->sn, ret);
return (ret);
}
-
-
bzero(&cistpl_vers_1, sizeof (struct cistpl_vers_1_t));
if ((ret = csx_Parse_CISTPL_VERS_1(pps->client_handle,
&tuple, &cistpl_vers_1)) != CS_SUCCESS) {
cmn_err(CE_CONT, "pcepp%d.%d: parse_cis, "
"csx_Parse_CISTPL_VERS_1 failed (0x%x)\n",
pps->instance, pps->sn, ret);
return (ret);
} else {
cis_vars->major_revision = cistpl_vers_1.major;
cis_vars->minor_revision = cistpl_vers_1.minor;
cis_vars->nstring = cistpl_vers_1.ns;
for (i = 0; i < cistpl_vers_1.ns; i++) {
strcpy(cis_vars->prod_strings[i],
cistpl_vers_1.pi[i]);
}
}
-
Parse CISTPL_MANFID Tuple
-
-
/*
* CISTPL_MANFID processing.
* Get PCMCIA PC Card manufacturer code and
* manufacturer information
*/
bzero(&tuple, sizeof (tuple));
tuple.DesiredTuple = CISTPL_MANFID;
if ((ret = csx_GetFirstTuple(pps->client_handle, &tuple))
!= CS_SUCCESS) {
cmn_err(CE_CONT, "pcepp%d.%d: parse_cis, "
"CISTPL_MANFID tuple not found (0x%x)\n",
pps->instance, pps->sn, ret);
return (ret);
}
bzero(&cistpl_manfid, sizeof (struct cistpl_manfid_t));
if ((ret = csx_Parse_CISTPL_MANFID(pps->client_handle,
&tuple, &cistpl_manfid)) != CS_SUCCESS) {
cmn_err(CE_CONT, "pcepp%d.%d: parse_cis, "
"csx_Parse_CISTPL_MANFID failed (0x%x)\n",
pps->instance, pps->sn, ret);
-
-
return (ret);
} else {
cis_vars->manufacturer_id = cistpl_manfid.manf;
cis_vars->card_id = cistpl_manfid.card;
}
-
Parse CISTPL_CFTABLE_ENTRY Tuple
-
-
/*
* CISTPL_CFTABLE_ENTRY processing.
*/
bzero(&tuple, sizeof (tuple));
tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
if ((ret = csx_GetFirstTuple(pps->client_handle, &tuple))
!= CS_SUCCESS) {
cmn_err(CE_CONT, "pcepp%d.%d: parse_cis, "
"CISTPL_CFTABLE_ENTRY tuple not found (0x%x)\n",
pps->instance, pps->sn, ret);
return (ret);
}
/* Initialize variable pcepp_cftable_t */
dcft = &defcft;
/* Clear the default values */
bzero(dcft, sizeof (pcepp_cftable_t));
/*
* Look through the CIS for all CISTPL_CFTABLE_ENTRY
* tuples and store them in a list. Stop searching
* if we find a tuple with a config index equal to
* the "last_config_index" value that we got from
* the CISTPL_CONFIG tuple.
*/
do {
bzero(&cistpl_cftable_entry,
sizeof (struct cistpl_cftable_entry_t));
if ((ret = csx_Parse_CISTPL_CFTABLE_ENTRY(
pps->client_handle, &tuple,
&cistpl_cftable_entry)) != CS_SUCCESS) {
cmn_err(CE_CONT, "pcepp%d.%d: parse_cis, "
-
-
"csx_Parse_CISTPL_CFTABLE_ENTRY "
"failed (0x%x)\n",
pps->instance, pps->sn, ret);
return (ret);
}
/*
* This particular application example does not
* support any CISTPL_CFTABLE_ENTRY tuple
* that does not have TPCE_IO information
*/
if ((cistpl_cftable_entry.flags &
CISTPL_CFTABLE_TPCE_FS_IO) == 0) {
continue;
}
ocft = kmem_zalloc(sizeof (pcepp_cftable_t),
KM_SLEEP);
if (!*cftable) {
*cftable = ocft;
cft = ocft;
cft->prev = NULL;
} else {
cft->next = ocft;
cft->next->prev = cft;
cft = cft->next;
}
cft->next = NULL;
/*
* See if this tuple has default values that we
* should save. If so, copy the default values
* that we've seen so far into the current
* cftable structure.
*/
if (cistpl_cftable_entry.flags &
CISTPL_CFTABLE_TPCE_DEFAULT) {
default_cftable = 1;
} else {
default_cftable = 0;
-
-
}
bcopy(&dcft->p, (caddr_t)&cft->p,
sizeof (pcepp_cftable_params_t));
cft->p.config_index = cistpl_cftable_entry.index;
/*
* Starting to process:
* CISTPL_CFTABLE_TPCE_IF
* CISTPL_CFTABLE_TPCE_FS_PWR
* CISTPL_CFTABLE_TPCE_FS_PWR_VCC
* CISTPL_CFTABLE_TPCE_FS_PWR_VPP1
* CISTPL_CFTABLE_TPCE_FS_PWR_VPP2
* CISTPL_CFTABLE_TPCE_FS_IO
* CISTPL_CFTABLE_TPCE_FS_IO_RANGE
*/
if (cistpl_cftable_entry.flags &
CISTPL_CFTABLE_TPCE_IF) {
cft->p.pin = cistpl_cftable_entry.pin;
if (default_cftable) {
dcft->p.pin = cistpl_cftable_entry.pin;
}
}
if (cistpl_cftable_entry.flags &
CISTPL_CFTABLE_TPCE_FS_PWR) {
struct cistpl_cftable_entry_pd_t *pd;
pd = &cistpl_cftable_entry.pd;
if (pd->flags &
CISTPL_CFTABLE_TPCE_FS_PWR_VCC) {
if (pd->pd_vcc.nomV_flags &
CISTPL_CFTABLE_PD_EXISTS) {
cft->p.card_vcc =
pd->pd_vcc.nomV;
if (default_cftable) {
dcft->p.card_vcc =
pd->pd_vcc.nomV;
-
-
}
} /* CISTPL_CFTABLE_PD_EXISTS */
} /* CISTPL_CFTABLE_TPCE_FS_PWR_VCC */
if (pd->flags &
CISTPL_CFTABLE_TPCE_FS_PWR_VPP1) {
if (pd->pd_vpp1.nomV_flags &
CISTPL_CFTABLE_PD_EXISTS) {
cft->p.card_vpp1 =
pd->pd_vpp1.nomV;
if (default_cftable) {
dcft->p.card_vpp1 =
pd->pd_vpp1.nomV;
}
} /* CISTPL_CFTABLE_PD_EXISTS */
} /* CISTPL_CFTABLE_TPCE_FS_PWR_VPP1 */
if (pd->flags &
CISTPL_CFTABLE_TPCE_FS_PWR_VPP2) {
if (pd->pd_vpp2.nomV_flags &
CISTPL_CFTABLE_PD_EXISTS) {
cft->p.card_vpp2 =
pd->pd_vpp2.nomV;
if (default_cftable) {
dcft->p.card_vpp2 =
pd->pd_vpp2.nomV;
}
} /* CISTPL_CFTABLE_PD_EXISTS */
} /* CISTPL_CFTABLE_TPCE_FS_PWR_VPP2 */
} /* CISTPL_CFTABLE_TPCE_FS_PWR */
if (cistpl_cftable_entry.flags &
CISTPL_CFTABLE_TPCE_FS_IO) {
cft->p.addr_lines = io->addr_lines;
if (default_cftable) {
dcft->p.addr_lines = io->addr_lines;
}
-
-
if ((cistpl_cftable_entry.io.flags &
CISTPL_CFTABLE_TPCE_FS_IO_RANGE) == 0) {
/*
* If there's no IO ranges for
* this configuration, then we
* need to calculate the length
* of the IO space by using the
* number of IO address
* lines value. i.e. 4 lines
* means 16 addresses
*/
cft->p.length1 =
(1 << cft->p.addr_lines);
/*
* cft->p.length1 must be
* decremented by 1 since it is
* incremented by 1 when it
* is used for NumPorts1 in
* CardService(RequestIO)
* See pcepp_card_insertion()
*/
cft->p.length1--;
if (default_cftable) {
dcft->p.length1 = cft->p.length1;
}
} else {
/*
* Get multiple IO address bases
* and IO register lengths
*/
cft->p.card_base1 = io->range[0].addr;
cft->p.length1 = io->range[0].length;
if (io->ranges == 2) {
cft->p.card_base2 =
io->range[1].addr;
cft->p.length2 =
io->range[1].length;
} else {
cft->p.card_base2 = 0;
cft->p.length2 = 0;
}
-
-
if (default_cftable) {
dcft->p.card_base1 =
io->range[0].addr;
dcft->p.length1 =
io->range[0].length;
if (io->ranges == 2) {
dcft->p.card_base2 =
io->range[1].addr;
dcft->p.length2 =
io->range[1].length;
} else {
dcft->p.card_base2 = 0;
dcft->p.length2 = 0;
}
}
} /* if (CISTPL_CFTABLE_TPCE_FS_IO_RANGE) */
} /* CISTPL_CFTABLE_TPCE_FS_IO */
cis_vars->n_cistpl_cftable_entry++;
(void) pcepp_set_cftable_desireability(cft);
} while ((cistpl_cftable_entry.index != last_config_index) &&
((ret = csx_GetNextTuple(pps->client_handle, &tuple))
== CS_SUCCESS));
/*
* Now we've got all of the possible configurations,
* so sort the list of configurations.
*/
(void) pcepp_sort_cftable_list(cftable);
#if defined(DEBUG)
cmn_err(CE_CONT, "DEBUG: socket %d --- sorted cftable ---\n",
pps->sn);
(void) pcepp_display_cftable_list(*cftable);
#endif
-
-
/*
* If GetNextTuple gave us any error code other than
* CS_SUCCESS or CS_NO_MORE_ITEMS, this is a
* CIS parser error
*/
if ((ret != CS_SUCCESS) && (ret != CS_NO_MORE_ITEMS)) {
cmn_err(CE_CONT, "pcepp%d.%d: parse_cis, "
"CIS parser error (0x%x)\n",
pps->instance, pps->sn, ret);
return (ret);
}
-
Parse CISTPL_FUNCID and CISTPL_FUNCE Tuples
-
-
/*
* CISTPL_FUNCID and CISTPL_FUNCE processing.
* We should really check to be sure that the
* CISTPL_FUNCID tuple we see is for a parallel port.
* The assumption is made that by the time we get here,
* Card Services would have already validated this.
* Also, we should only search for one CISTPL_FUNCID
* tuple, since a CIS is only allowed to have one
* CISTPL_FUNCID tuple per function.
*/
bzero(&tuple, sizeof (tuple));
tuple.DesiredTuple = CISTPL_FUNCID;
if ((ret = csx_GetFirstTuple(pps->client_handle, &tuple))
!= CS_SUCCESS) {
cmn_err(CE_CONT, "pcepp%d.%d: parse_cis, "
"CISTPL_FUNCID tuple not found (0x%x)\n",
pps->instance, pps->sn, ret);
return (ret);
}
do {
cistpl_funcid_t cistpl_funcid;
cistpl_funce_t cistpl_funce;
bzero((caddr_t)&cistpl_funcid,
sizeof (struct cistpl_funcid_t));
-
-
if ((ret = csx_Parse_CISTPL_FUNCID(pps->client_handle,
&tuple, &cistpl_funcid)) != CS_SUCCESS) {
cmn_err(CE_CONT, "pcepp%d.%d: parse_cis, "
"csx_Parse_CISTPL_FUNCID "
"failed (0x%x)\n",
pps->instance, pps->sn, ret);
return (ret);
}
/*
* Search for any CISTPL_FUNCE tuples.
*/
tuple.DesiredTuple = CISTPL_FUNCE;
while ((ret = csx_GetNextTuple(pps->client_handle,
&tuple)) == CS_SUCCESS) {
bzero((caddr_t)&cistpl_funce,
sizeof (cistpl_funce_t));
if ((ret = csx_Parse_CISTPL_FUNCE(
pps->client_handle, &tuple,
&cistpl_funce,
cistpl_funcid.function))
!= CS_SUCCESS) {
cmn_err(CE_CONT,
"pcepp%d.%d: parse_cis, "
"csx_Parse_CISTPL_FUNCE "
"failed (0x%x)\n",
pps->instance, pps->sn, ret);
return (ret);
}
/*
* Note that there is no CISTPL_FUNCE
* tuple for this sample parallel card.
* But if a card has the CISTPL_FUNCE
* tuple, this is where it can be processed.
*/
} /* while (GetNextTuple) */
tuple.DesiredTuple = CISTPL_FUNCID;
-
-
cis_vars->n_cistpl_funcid++;
} while ((ret = csx_GetNextTuple(pps->client_handle, &tuple))
== CS_SUCCESS);
return (CS_SUCCESS);
}
pcepp_display_cftable_list()
- This utility routine prints the configuration table on the console.
-
-
void
pcepp_display_cftable_list(pcepp_cftable_t *cft)
{
while (cft) {
cmn_err(CE_CONT,
"\nDEBUG: pcepp_display_cftable_list\n");
cmn_err(CE_CONT, " desireability: 0x%x\n",
(int)cft->desireability);
cmn_err(CE_CONT, " config_index: 0x%x\n",
(int)cft->p.config_index);
cmn_err(CE_CONT, " addr_lines: 0x%x\n",
(int)cft->p.addr_lines);
cmn_err(CE_CONT, " pin: 0x%x\n",
(int)cft->p.pin);
cmn_err(CE_CONT, " card_vcc: %d\n",
(int)cft->p.card_vcc);
cmn_err(CE_CONT, " card_vpp1: %d\n",
(int)cft->p.card_vpp1);
cmn_err(CE_CONT, " card_vpp2: %d\n",
(int)cft->p.card_vpp2);
cmn_err(CE_CONT, " card_base1: 0x%x\n",
(int)cft->p.card_base1);
cmn_err(CE_CONT, " length1: 0x%x\n",
(int)cft->p.length1);
cmn_err(CE_CONT, " card_base2: 0x%x\n",
(int)cft->p.card_base2);
cmn_err(CE_CONT, " length2: 0x%x\n",
(int)cft->p.length2);
-
-
cft = cft->next;
} /* while (cft) */
}
pcepp_display_card_config()
- This utility routine prints the card configuration on the console.
-
-
void
pcepp_display_card_config(pcepp_state_t *pps)
{
int i;
pcepp_cis_vars_t *cis_vars = &pps->cis_vars;
cmn_err(CE_CONT, "\nDEBUG: "
"pcepp_display_card_config Socket %d\n", pps->sn);
cmn_err(CE_CONT, " flags: 0x%x\n", (int)cis_vars->flags);
cmn_err(CE_CONT, " config_base: 0x%x\n",
(int)cis_vars->config_base);
cmn_err(CE_CONT, " present: 0x%x\n", (int)cis_vars->present);
cmn_err(CE_CONT, " major_revision: 0x%x\n",
(int)cis_vars->major_revision);
cmn_err(CE_CONT, " minor_revision: 0x%x\n",
(int)cis_vars->minor_revision);
cmn_err(CE_CONT, " prod_strings:\n");
for (i = 0; i < cis_vars->nstring; i++) {
cmn_err(CE_CONT, "\t%d: [%s]\n",
i, cis_vars->prod_strings[i]);
}
cmn_err(CE_CONT, " manufacturer_id: 0x%x\n",
(int)cis_vars->manufacturer_id);
cmn_err(CE_CONT, " card_id: 0x%x\n", (int)cis_vars->card_id);
cmn_err(CE_CONT, " config_index: 0x%x\n",
(u_char)cis_vars->config_index);
cmn_err(CE_CONT, " addr_lines: 0x%x\n",
(int)cis_vars->addr_lines);
cmn_err(CE_CONT, " pin: 0x%x\n", (int)cis_vars->pin);
cmn_err(CE_CONT, " n_cistpl_cftable_entry: 0x%x\n",
(int)cis_vars->n_cistpl_cftable_entry);
cmn_err(CE_CONT, " n_cistpl_funcid: 0x%x\n",
(int)cis_vars->n_cistpl_funcid);
-
-
cmn_err(CE_CONT, "\n");
}
pcepp_destroy_cftable_list()
- This utility frees the configuration table.
-
-
static void
pcepp_destroy_cftable_list(pcepp_cftable_t **cftable)
{
pcepp_cftable_t *cft, *ocft = NULL;
cft = *cftable;
while (cft) {
ocft = cft;
cft = cft->next;
}
while (ocft) {
cft = ocft->prev;
kmem_free(ocft, sizeof (pcepp_cftable_t));
ocft = cft;
}
*cftable = NULL;
}
pcepp_set_cftable_desireability()
- This utility sets the desirability factor based on the desirability order in the pcepp_cis_addr_tran_t table.
-
-
static void
pcepp_set_cftable_desireability(pcepp_cftable_t *cft)
{
int i;
for (i = 0; i < (sizeof (pcepp_cis_addr_tran) /
sizeof (pcepp_cis_addr_tran_t)); i++) {
if (cft->p.card_base1 ==
pcepp_cis_addr_tran[i].card_base1) {
cft->desireability =
-
-
((cft->desireability & 0x0ffff0000) |
(pcepp_cis_addr_tran[i].desireability));
return;
}
}
}
pcepp_sort_cftable_list()
- This utility sorts the configuration based on the desirability factor.
-
-
static void
pcepp_sort_cftable_list(pcepp_cftable_t **cftable)
{
int did_swap = 1;
pcepp_cftable_t *cft;
do {
cft = *cftable;
did_swap = 0;
while (cft) {
if (cft->prev) {
if (cft->desireability <
cft->prev->desireability) {
(void) pcepp_swap_cft(cftable, cft);
did_swap = 1;
} /* if (cft->desireability) */
} /* if (cft->prev) */
cft = cft->next;
} /* while (cft) */
} while (did_swap);
}
pcepp_swap_cft()
- This utility sort routine swaps configuration table entries.
-
-
static void
pcepp_swap_cft(pcepp_cftable_t **cftable, pcepp_cftable_t *cft)
{
pcepp_cftable_t *cfttmp;
if (cft->next) {
-
-
cft->next->prev = cft->prev;
}
cft->prev->next = cft->next;
cft->next = cft->prev;
if (cft->prev->prev) {
cft->prev->prev->next = cft;
}
cfttmp = cft->prev;
if ((cft->prev = cft->prev->prev) == NULL) {
*cftable = cft;
}
cfttmp->prev = cft;
}
Device Interrupt Handler Routines
- The interrupt-handler routines include:
-
-
pcepp_intr()
-
pcepp_softintr()
pcepp_intr()
- This is the driver interrupt-handler routine.
-
-
static u_int
pcepp_intr(pcepp_state_t *pps)
{
int serviced = DDI_INTR_UNCLAIMED;
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: intr\n",
pps->instance, pps->sn));
/*
* If the card isn't inserted or fully initialized yet,
* this isn't an interrupt for us. We must do this before
* grabbing the mutex, since with shared interrupts, we
* may get interrupts from other sources before we are fully
* prepared for them. We also need to stop accessing the
-
-
* card with some promptness when the card gets yanked out
* from under us. The high-level card removal processing
* clears the Card Inserted bit.
*/
if (PCEPP_CARD_IS_READY(pps) && (pps->flags & PCEPP_ISOPEN)) {
/*
* Take care xmit interrupt
*/
mutex_enter(&pps->irq_mutex);
serviced = pcepp_xmit(pps);
mutex_exit(&pps->irq_mutex);
/*
* if we've got any work to do,
* schedule a softinterrupt
*/
if (serviced == PCEPP_XMIT_SUCCESS) {
ddi_trigger_softintr(pps->softint_id);
}
return (DDI_INTR_CLAIMED);
} /* if (PCEPP_CARD_IS_READY) */
return (DDI_INTR_UNCLAIMED);
}
pcepp_softintr()
- When there is more data to be transferred to the printer, pcepp_softintr() calls this function to transfer the expected data.
-
-
static u_int
pcepp_softintr(caddr_t arg)
{
register pcepp_state_t *pps = (pcepp_state_t *)arg;
int ret = DDI_INTR_UNCLAIMED;
/*
* Make sure that we made it through attach.It's OK to run
* some of this code if there's work to do even if there
* is no card in the socket,since the work we're being
-
-
* asked to do might be cleanup from a card removal.
*/
if (pps->flags & PCEPP_ISOPEN) {
/*
* If there is more data, pcepp_start will
* continue to transfer the next data
*/
PCEPP_MUTEX_ENTER(pps);
(void) pcepp_start(pps);
PCEPP_MUTEX_EXIT(pps);
ret = DDI_INTR_CLAIMED;
} /* if (pps->flags) */
return (ret);
}
STREAMS Routines
- The following routines are standard STREAMS modules. Refer to the STREAMS Programmer's Guide for details. The STREAMS-related routines include:
-
-
open(9E)
-
close(9E)
-
ioctl(9E)
-
pcepp_rput()
-
pcepp_wput()
-
pcepp_srvioc()
pcepp_rput()
- This routine is the read-side put procedure. Since this driver is unidirectional, the message is simply queued.
-
-
static int
pcepp_rput(
queue_t *q, /* pointer to the queue */
mblk_t *mp) /* message pointer */
{
register pcepp_state_t *pps = (pcepp_state_t *)q->q_ptr;
-
-
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: rput\n",
pps->instance, pps->sn));
putq(q, mp);
return (0);
}
pcepp_wput()
- This routine is the write-side put procedure.
-
-
static int
pcepp_wput(
queue_t *q, /* pointer to the queue */
mblk_t *mp) /* message pointer */
{
register pcepp_state_t *pps = (pcepp_state_t *)q->q_ptr;
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: wput\n",
pps->instance, pps->sn));
switch (mp->b_datap->db_type) {
default:
freemsg(mp);
break;
case M_FLUSH: /* Canonical flush handling */
if (*mp->b_rptr & FLUSHW) {
/*
* If the FLUSHW is set, the write message
* queue is flushed.
*/
mutex_enter(&pps->event_mutex);
PCEPP_MUTEX_ENTER(pps);
pps->card_event &= ~PCEPP_CARD_BUSY;
PCEPP_MUTEX_EXIT(pps);
mutex_exit(&pps->event_mutex);
flushq(q, FLUSHDATA);
*mp->b_rptr &= ~FLUSHW;
-
-
}
if (*mp->b_rptr & FLUSHR) {
/*
* If the FLUSHR is set, the read queue
* is flushed, the message is sent
* upstream using qreply()
*/
flushq(RD(q), FLUSHDATA);
qreply(q, mp);
} else {
/*
* If the FLUSHR is not set,
* the message is discarded
*/
freemsg(mp);
}
break;
case M_IOCTL:
(void) pcepp_ioctl(q, mp);
break;
case M_DATA:
putq(q, mp);
mutex_enter(&pps->event_mutex);
(void) pcepp_start(pps);
mutex_exit(&pps->event_mutex);
break;
} /* switch (mp->b_datap->db_type) */
return (0);
}
pcepp_open()
- This routine is the STREAMS driver open procedure. It sets up the PCMCIA card, STREAMS queues, and internal termio(7I) structures, and then turns on the queue processing.
-
-
static int
pcepp_open(
queue_t *q, /* pointer to the read queue */
dev_t *devp, /* pointer to major/minor device # */
int flag, /* file flags */
int sflag, /* stream open flags */
cred_t *credp) /* pointer to a credentials struct */
{
int ret;
register struct strtty *tp;
pcepp_state_t *pps;
cs_ddi_info_t cs_ddi_info;
/*
* This driver sample does not support the clone
* feature and will not do module open.
*
* sflag values:
* MODOPEN normal module open
* 0 normal driver open
* CLONEOPEN clone driver open
*/
if (sflag) {
PCEPP_DEBUG((CE_CONT, "pcepp: open failed, "
"Only support normal driver open"
"sflag (0x%x)\n", sflag));
return (ENXIO);
}
/*
* Since PC Card driver encodes physical socket number
* as part of the dev_t of device nodes instead of
* the instance number we have to use csx_CS_DDI_Info(9F)
* to get instance number that is associated with
* a particular PC Card driver and socket number
-
-
* for using in ddi_get_soft_state().
*/
cs_ddi_info.Socket = PCEPP_SOCKET((dev_t)*devp);
cs_ddi_info.driver_name = pcepp_name;
if (csx_CS_DDI_Info(&cs_ddi_info) != DDI_SUCCESS) {
PCEPP_DEBUG((CE_NOTE,
"pcepp: open, csx_CS_DDI_Info() failed\n"));
return (ENXIO);
}
pps = ddi_get_soft_state(pcepp_soft_state_p,
cs_ddi_info.instance);
if (pps == NULL) {
PCEPP_DEBUG((CE_CONT, "pcepp: open, "
"could not get state structure\n"));
return (ENODEV);
}
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: open\n",
cs_ddi_info.instance, pps->sn));
if (!PCEPP_CARD_IS_READY(pps)) {
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: open, "
"Found no PC Card\n", cs_ddi_info.instance,
pps->sn));
return (ENODEV);
}
/*
* Check if open already. Cannot have multiple opens.
*/
if (q->q_ptr) {
return (EBUSY);
}
/* Protect card structure */
mutex_enter(&pps->event_mutex);
PCEPP_HUTEX_ENTER(pps);
/*
* Set up the hardware.
* Disable interrupt bit and configure the
-
-
* parallel port as the standard output port.
*/
csx_Put8(pps->card_handle, PCEPP_REGS_DCR,
csx_Get8(pps->card_handle, PCEPP_REGS_DCR) &
~PCEPP_ECR_INTR_ENB);
csx_Put8(pps->card_handle, PCEPP_REGS_DCR,
PCEPP_ECR_SEL_PRT | PCEPP_ECR_INIT_PRT);
/*
* Check printer condition
*/
if (pcepp_prtstatus(pps)) {
PCEPP_MUTEX_EXIT(pps);
mutex_exit(&pps->event_mutex);
return (EIO);
}
/*
* now set up the streams queues
*/
pps->rdq_ptr = q;
pps->wrq_ptr = WR(q);
q->q_ptr = (caddr_t)pps;
WR(q)->q_ptr = (caddr_t)pps;
/*
* set up the tty structure for CENTRONICS-TYPE printer.
*/
tp = &pps->pcepp_tty;
tp->t_rdqp = q;
tp->t_dev = pps->sn;
if ((tp->t_state & (ISOPEN | WOPEN)) == 0) {
tp->t_iflag = IGNPAR;
tp->t_cflag = B300 | CS8 | CLOCAL;
}
tp->t_state &= ~WOPEN;
tp->t_state |= CARR_ON | ISOPEN;
/*
* start queue processing
*/
-
-
qprocson(q);
pps->flags |= PCEPP_ISOPEN;
PCEPP_MUTEX_EXIT(pps);
mutex_exit(&pps->event_mutex);
return (0);
}
pcepp_close()
- This routine is the STREAMS close procedure. It shuts down the PCMCIA card, turns off STREAMS queue processing, and cleans up the termio(7I) structure and STREAMS queues.
-
-
static int
pcepp_close(
queue_t *q, /* pointer to the read queue */
int flag, /* file flags */
cred_t *credp) /* pointer to a credentials struct */
{
register struct strtty *tp;
pcepp_state_t *pps;
pps = (pcepp_state_t *)q->q_ptr;
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: close\n",
pps->instance, pps->sn));
mutex_enter(&pps->event_mutex);
/*
* Make sure the card is presented before we
* reset card registers.
*/
if (PCEPP_CARD_IS_READY(pps)) {
/*
* Disable interrupt bit
*/
PCEPP_MUTEX_ENTER(pps);
csx_Put8(pps->card_handle, PCEPP_REGS_DCR,
csx_Get8(pps->card_handle, PCEPP_REGS_DCR) &
~PCEPP_ECR_INTR_ENB);
-
-
PCEPP_MUTEX_EXIT(pps);
} /* PCEPP_CARD_IS_READY */
/*
* clean up the queue pointers
*/
qprocsoff(q);
tp = &pps->pcepp_tty;
tp->t_state &= ~(ISOPEN | CARR_ON);
tp->t_rdqp = NULL;
q->q_ptr = (queue_t *)NULL;
WR(q)->q_ptr = (queue_t *)NULL;
PCEPP_MUTEX_ENTER(pps);
pps->rdq_ptr = (queue_t *)NULL;
pps->wrq_ptr = (queue_t *)NULL;
pps->flags &= ~PCEPP_ISOPEN;
PCEPP_MUTEX_EXIT(pps);
mutex_exit(&pps->event_mutex);
return (0);
}
pcepp_ioctl()
- This routine processes M_IOCTL messages sent to the driver. Since this is a STREAMS driver, the I/O control messages are not processed through an entry point in cb_ops(9S). pcepp_ioctl() is called by pcepp_start() while processing messages sent to this STREAMS module.
-
-
static int
pcepp_ioctl(
queue_t *q, /* pointer to the queue */
mblk_t *mp) /* message pointer */
{
struct iocblk *ioc;
pcepp_state_t *pps;
-
-
/* LINTED */
ioc = (struct iocblk *)mp->b_rptr;
pps = (pcepp_state_t *)q->q_ptr;
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: ioctl\n",
pps->instance, pps->sn));
switch (ioc->ioc_cmd) {
case TCSBRK:
case TCSETAW:
case TCSETSW:
case TCSETSF:
case TCSETAF:
if (q->q_first) {
(void) putq(q, mp);
break;
}
(void) pcepp_srvioc(q, mp);
break;
case TCSETS:
case TCSETA: /* immediate parm set */
if (pps->card_event & PCEPP_CARD_BUSY) {
(void) putq(q, mp);
break;
}
(void) pcepp_srvioc(q, mp);
break;
case TCGETS:
case TCGETA: /* immediate parm retrieve */
(void) pcepp_srvioc(q, mp);
break;
default:
/*
* Passing the unknown IOCTL to upper module
*/
ioc->ioc_error = EINVAL;
ioc->ioc_rval = (-1);
mp->b_datap->db_type = M_IOCNAK;
-
-
qreply(q, mp);
break;
} /* switch (ioc->ioc_cmd) */
return (0);
}
pcepp_srvioc()
- This routine processes the termio(7I) I/O control commands for
-
-
pcepp_ioctl().
static void
pcepp_srvioc(
queue_t *q, /* pointer to the queue */
mblk_t *mp) /* message pointer */
{
struct strtty *tp;
struct iocblk *ioc;
struct termio *cb;
struct termios *scb;
pcepp_state_t *pps;
mblk_t *mpr;
mblk_t *mp1;
/* LINTED */
ioc = (struct iocblk *)mp->b_rptr;
pps = (pcepp_state_t *)q->q_ptr;
tp = &pps->pcepp_tty;
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: srvioc\n",
pps->instance, pps->sn));
switch (ioc->ioc_cmd) {
case TCSBRK:
/* Skip the break since it's a parallel port. */
mp->b_datap->db_type = M_IOCACK;
mp1 = unlinkb(mp);
if (mp1) {
-
-
freeb(mp1);
}
ioc->ioc_count = 0;
qreply(q, mp);
break;
case TCSETA:
case TCSETAW:
if (!mp->b_cont) {
ioc->ioc_error = EINVAL;
mp->b_datap->db_type = M_IOCNAK;
ioc->ioc_count = 0;
qreply(q, mp);
break;
}
/* LINTED */
cb = (struct termio *)mp->b_cont->b_rptr;
tp->t_cflag = cb->c_cflag;
tp->t_iflag = cb->c_iflag;
mp->b_datap->db_type = M_IOCACK;
mp1 = unlinkb(mp);
if (mp1) {
freeb(mp1);
}
ioc->ioc_count = 0;
qreply(q, mp);
break;
case TCSETS:
case TCSETSW:
if (!mp->b_cont) {
ioc->ioc_error = EINVAL;
mp->b_datap->db_type = M_IOCNAK;
ioc->ioc_count = 0;
qreply(q, mp);
break;
}
/* LINTED */
scb = (struct termios *)mp->b_cont->b_rptr;
tp->t_cflag = scb->c_cflag;
tp->t_iflag = scb->c_iflag;
-
-
mp->b_datap->db_type = M_IOCACK;
mp1 = unlinkb(mp);
if (mp1) {
freeb(mp1);
}
ioc->ioc_count = 0;
qreply(q, mp);
break;
case TCGETA:
/* immediate parm retrieve */
if (mp->b_cont) {
/* bad user supplied parameter */
freemsg(mp);
}
if ((mpr = allocb(sizeof (struct termio),
BPRI_MED)) == NULL) {
(void) putbq(q, mp);
return;
}
mp->b_cont = mpr;
/* LINTED */
cb = (struct termio *)mp->b_cont->b_rptr;
cb->c_iflag = tp->t_iflag;
cb->c_cflag = tp->t_cflag;
mp->b_cont->b_wptr += sizeof (struct termio);
mp->b_datap->db_type = M_IOCACK;
ioc->ioc_count = sizeof (struct termio);
qreply(q, mp);
break;
case TCGETS:
/* immediate parm retrieve */
if (mp->b_cont) {
freemsg(mp->b_cont);
}
if ((mpr = allocb(sizeof (struct termio),
BPRI_MED)) == NULL) {
-
-
(void) putbq(q, mp);
return;
}
mp->b_cont = mpr;
/* LINTED */
scb = (struct termios *)mp->b_cont->b_rptr;
scb->c_iflag = tp->t_iflag;
scb->c_cflag = tp->t_cflag;
mp->b_cont->b_wptr += sizeof (struct termio);
mp->b_datap->db_type = M_IOCACK;
ioc->ioc_count = sizeof (struct termio);
qreply(q, mp);
break;
default:
/* unexpected ioctl type */
if (canput(RD(q)->q_next) == 1) {
mp->b_datap->db_type = M_IOCNAK;
ioc->ioc_count = 0;
qreply(q, mp);
} else {
(void) putbq(q, mp);
}
break;
} /* switch (ioc->ioc_cmd) */
}
pcepp_xmit()
- This routine sends a character to the printer if the printer is not busy.
-
-
static int
pcepp_xmit(pcepp_state_t *pps)
{
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: xmit\n",
pps->instance, pps->sn));
if (pcepp_prtstatus(pps)) {
-
-
return (PCEPP_XMIT_BUSY);
}
/* Disable interrupt bit */
csx_Put8(pps->card_handle, PCEPP_REGS_DCR,
csx_Get8(pps->card_handle, PCEPP_REGS_DCR) &
~PCEPP_ECR_INTR_ENB);
(void) pcepp_strobe_pulse(pps);
/* Clear busy flag */
pps->card_event &= ~PCEPP_CARD_BUSY;
return (PCEPP_XMIT_SUCCESS);
}
pcepp_strobe_pulse()
- After the data is loaded into the data register, the strobe pulse signal must be generated.
-
-
static void
pcepp_strobe_pulse(pcepp_state_t *pps)
{
/* Enable strobe signal */
csx_Put8(pps->card_handle, PCEPP_REGS_DCR,
csx_Get8(pps->card_handle, PCEPP_REGS_DCR) |
PCEPP_ECR_STROBE_ENB);
PCEPP_HIMUTEX_EXIT(pps);
/* Generate 10msec STROBE pulse */
drv_usecwait(10);
PCEPP_HIMUTEX_ENTER(pps);
/* Enable strobe signal */
csx_Put8(pps->card_handle, PCEPP_REGS_DCR,
csx_Get8(pps->card_handle, PCEPP_REGS_DCR) &
~PCEPP_ECR_STROBE_ENB);
}
pcepp_start()
- This routine processes M_IOCTL and M_DATA messages. M_IOCTL messages are forwarded to pcepp_ioctl(). M_DATA messages provide the data that is sent to the printer one at a time. An interrupt that is triggered when the character is received by the printer causes the interrupt handler to invoke this routine again to cause the next character to be sent.
-
-
static void
pcepp_start(register pcepp_state_t *pps)
{
register queue_t *q;
register mblk_t *bp;
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: start\n",
pps->instance, pps->sn));
if (pps->card_event & PCEPP_CARD_BUSY) {
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: start, "
"Card is BUSY\n", pps->instance, pps->sn));
return;
}
if ((q = pps->wrq_ptr) == NULL) {
cmn_err(CE_CONT, "pcepp%d.%d: start, "
"q is NULL\n", pps->instance, (int)pps->sn);
return;
}
/*
* handle next message block (if any)
*/
if ((bp = getq(q)) == NULL) {
return;
}
/*
* Grab a hardwire mutex here since some of these flags
* are modified in our interrupt handlers
*/
PCEPP_MUTEX_ENTER(pps);
pps->msg = bp;
-
-
PCEPP_MUTEX_EXIT(pps);
switch (bp->b_datap->db_type) {
case M_IOCTL:
(void) pcepp_ioctl(q, bp);
break;
case M_DATA:
/* Transfer to Tx soft data buffer */
PCEPP_MUTEX_ENTER(pps);
pps->pcepp_txdata = *bp->b_rptr;
bp->b_rptr++;
PCEPP_MUTEX_EXIT(pps);
/*
* If there is more data to be transferred,
* then just put this remaining data
* block back and continue send data to
* the printer
*/
if ((bp->b_wptr - bp->b_rptr) > 0) {
putbq(q, bp);
} else {
PCEPP_MUTEX_ENTER(pps);
bp = pps->msg->b_cont;
pps->msg->b_cont = NULL;
freeb(pps->msg);
PCEPP_MUTEX_EXIT(pps);
}
if (PCEPP_CARD__IS_READY(pps)) {
PCEPP_MUTEX_ENTER(pps);
pps->card_event |= PCEPP_CARD_BUSY;
/*
* Printer is ready, send a character
* and generating STROBE pulse
*/
csx_Put8(pps->card_handle, PCEPP_REGS_DATA,
pps->pcepp_txdata);
/*
-
-
* Enable the transmitter empty interrupts
*/
csx_Put8(pps->card_handle, PCEPP_REGS_DCR,
csx_Get8(pps->card_handle,
PCEPP_REGS_DCR) |
PCEPP_ECR_INTR_ENB);
PCEPP_MUTEX_EXIT(pps);
} /* PCEPP_CARD_IS_READY */
break;
default:
freemsg(bp);
break;
} /* switch (bp->b_datap->db_type) */
}
pcepp_prtstatus()
- This routine determines whether the printer is ready to print.
-
-
static int
pcepp_prtstatus(register pcepp_state_t *pps)
{
u_char prtstatus;
int retval = 0;
prtstatus = csx_Get8(pps->card_handle, PCEPP_REGS_DSR);
if (prtstatus & PCEPP_ESR_PERROR) {
cmn_err(CE_CONT, "pcepp%d.%d: Printer PAPER OUT\n",
pps->instance, pps->sn);
retval = 1;
}
if ((prtstatus & PCEPP_ESR_SELECT) == 0) {
cmn_err(CE_CONT, "pcepp%d.%d: Printer is OFFLINE\n",
pps->instance, pps->sn);
retval = 1;
}
if ((prtstatus & PCEPP_ESR_FAULT) == 0) {
cmn_err(CE_CONT, "pcepp%d.%d: "
-
-
"ERROR has been detected\n",
pps->instance, pps->sn);
retval = 1;
}
if ((prtstatus & PCEPP_ESR_PRN_READY) == 0) {
PCEPP_DEBUG((CE_CONT, "pcepp%d.%d: Printer is NOT READY\n",
pps->instance, pps->sn));
retval = 1;
}
return (retval);
}
|
|