pem.c revision 3db86aab554edbb4244c8d1a1c90f152eee768af
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* pem - PCMCIA Event Manager
*
* gives user level access to PCMCIA event notification and
* allows managing devices
*/
#if defined(DEBUG)
#define PEM_DEBUG
#endif
#include <sys/sysmacros.h>
#include <sys/autoconf.h>
#include <sys/sservice.h>
#ifdef PEM_DEBUG
int pem_debug = 0;
#endif
int pem_softint_pend = 0;
int pem_softint_posted = 0;
char _depends_on[] = "misc/pcmcia";
static int (*cardservices)();
static int (*Socket_Services)(int, ...);
/*
* function prototypes, etc.
*/
int _init(void);
int _fini(void);
static int pem_event_handler(int, int, int, void *);
static void pem_event_dispatch(int, int, int, void *);
extern dev_info_t *pcmcia_get_devinfo(int);
static void pem_flushqueue(queue_t *);
static struct pem_inst {
} *pem_instances;
/*
* Allocate and zero-out "number" structures each of type "structure" in
* kernel memory.
*/
((structure *) kmem_zalloc(\
/* STREAMS setup glue */
static struct module_info pem_minfo = {
};
NULL,
NULL,
};
NULL,
NULL,
NULL,
};
&pem_rint,
&pem_wint,
NULL,
};
/*
* Module linkage information for the kernel.
*/
extern struct mod_ops mod_driverops;
&mod_driverops, /* Type of module - a utility provider */
"PCMCIA Event Manager %I%",
&pem_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
};
int
_init(void)
{
int e;
if ((e = ddi_soft_state_init((void **)&pem_instances,
sizeof (struct pem_inst), 1)) != 0)
return (e);
e = mod_install(&modlinkage);
if (e != 0) {
ddi_soft_state_fini((void **)&pem_instances);
}
return (e);
}
int
_fini(void)
{
int ret;
ddi_soft_state_fini((void **)&pem_instances);
}
return (ret);
}
int
{
}
static int
{
int i;
switch (cmd) {
case DDI_ATTACH:
for (i = 0; i < EM_EVENT_SIZE; i++)
sizeof (events),
0x1234,
(void **)&cardservices,
(void **)&Socket_Services);
if (i != 0) {
#if defined(PEM_DEBUG)
if (pem_debug)
#endif
return (DDI_FAILURE);
}
"pcmcia:event", 0);
0,
break;
case DDI_RESUME:
/*
* we need to tell the daemon to start all over again
* since the world state may have changed.
*/
break;
default:
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/* ARGSUSED */
static int
{
#ifdef PEM_DEBUG
" pem_softint_posted=%d\n",
#endif
switch (cmd) {
case DDI_DETACH:
if (pem_minors != 0)
return (DDI_FAILURE);
while (pem_softint_posted > 0) {
/*
* delay for 1 second to allow outstanding soft
* interrupts to be processed before removing the
* soft interrupt handler.
*/
tm = ddi_get_lbolt();
}
pem_intr_id = 0;
break;
case DDI_SUSPEND:
break;
default:
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
{
int error = DDI_SUCCESS;
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
break;
case DDI_INFO_DEVT2INSTANCE:
*result = 0;
break;
default:
error = DDI_FAILURE;
break;
}
return (error);
}
/*
* PEM service routines
*/
/*
* pem_open(q, dev, flag, sflag, cred)
* generic open routine. Hardware open will call this. The
* hardware open passes in the pemevice structure (one per device class) as
* well as all of the normal open parameters.
*/
/* ARGSUSED */
static int
{
ASSERT(q);
/* find minor device number */
if (minordev == 0) {
return (ENXIO);
}
return (ENXIO);
}
minordev - 1);
return (ENXIO);
}
minordev--;
#if defined(PEM_DEBUG)
int result;
#endif
#if defined(PEM_DEBUG)
#else
#endif
}
/*
* get a per-stream structure and link things together so we
* can easily find them later.
*/
return (ENOSR);
}
qprocson(q); /* start the queues running */
return (0);
}
/*
* pem_close(q) normal stream close call checks current status and cleans up
* data structures that were dynamically allocated
*/
/* ARGSUSED */
static int
{
int minor;
ASSERT(q);
qprocsoff(q);
/* disassociate the stream from the device */
}
}
return (0);
}
/*
* pem_wput(q, mp)
* general pem stream write put routine. Receives ioctl's from
* user level and data from upper modules and processes them immediately.
* procedure.
*/
static int
queue_t *q; /* queue pointer */
{
int err = 0;
#ifdef PEM_DEBUG
#endif
case M_IOCTL: /* no waiting in ioctl's */
break;
case M_FLUSH: /* canonical flush handling */
flushq(q, 0);
} else
break;
case M_PROTO:
case M_PCPROTO:
/* for now, we will always queue proto messages */
break;
case M_DATA:
/* force a fatal error */
break;
default:
#ifdef PEM_DEBUG
"pem: Unexpected packet type from queue: %x\n",
#endif
break;
}
return (err);
}
/*
* pem_wsrv - Incoming messages are processed according to the DLPI protocol
* specification
*/
static int
pem_wsrv(q)
queue_t *q; /* queue pointer */
{
int err = 0;
#ifdef PEM_DEBUG
#endif
case M_IOCTL:
/* case where we couldn't do it in the put procedure */
break;
case M_PROTO: /* Will be an PM message of some type */
case M_PCPROTO:
}
break;
case M_DATA:
break;
/* This should never happen */
default:
#ifdef PEM_DEBUG
"pem_wsrv: db_type(%x) not supported\n",
#endif
break;
}
}
return (err);
}
/*
* pem_rsrv(q)
* simple read service procedure
* purpose is to avoid the time it takes for packets
* to move through IP so we can get them off the board
* as fast as possible due to limited PC resources.
*/
static int
{
}
return (0);
}
/*
* pem_ioctl(q, mp)
* handles all ioctl requests passed downstream. This routine is
* passed a pointer to the message block with the ioctl request in it, and a
* pointer to the queue so it can respond to the ioctl request with an ack.
*/
static int
{
#ifdef PEM_DEBUG
#endif
default:
break;
}
return (0);
}
/*
* pem_cmds(q, mp)
* process the PM commands as defined in em.h
* note that the primitives return status which is passed back
* to the service procedure.
*/
static int
queue_t *q; /* queue pointer */
{
#ifdef PEM_DEBUG
"pem_cmds(%p, %p):pem=(N/A), prim->em_primitive=%d\n",
#endif
switch (prim->em_primitive) {
case EM_INIT_REQ:
break;
case EM_INFO_REQ:
break;
case EM_MODIFY_EVENT_MASK_REQ:
break;
case EM_ADAPTER_INFO_REQ:
break;
case EM_SOCKET_INFO_REQ:
break;
case EM_GET_SOCKET_REQ:
break;
case EM_IDENT_SOCKET_REQ:
break;
default:
#ifdef PEM_DEBUG
"pem_cmds: unknown M_PROTO message: %d\n",
(int)prim->em_primitive);
#endif
result = EM_BADPRIM;
}
return (result);
}
/*
* pem_info_req - generate the response to an info request
*/
/* ARGSUSED */
static int
{
int bufsize;
#ifdef PEM_DEBUG
#endif
if (mp) {
}
sizeof (em_info_ack_t) +
}
}
return (PEME_OK);
}
/*
* pem_init_req - initialize the open stream to receive events, etc.
*/
static int
{
return (PEME_UNAVAILABLE);
}
pem_flushqueue(q);
if (initreq->em_event_mask_offset != 0)
else
if (initreq->em_event_class_offset != 0)
else
}
}
#if defined(PEM_DEBUG)
if (pem_debug) {
(int)initreq->em_event_mask_length);
}
#endif
return (PEME_OK);
}
int
{
if (pem_cs_handle == 0) {
return (PEME_UNAVAILABLE);
}
return (PEME_NO_CIS);
}
SUCCESS) {
result);
return (PEME_OK);
}
/* now have a tuple so lets construct a proper response */
len += sizeof (em_get_next_tuple_ack_t);
return (PEME_OK);
if (result == CS_SUCCESS) {
}
return (0);
}
/*
* pem_flushqueue(q)
* used by DLPI primitives that require flushing the queues.
* essentially, this is DL_UNBIND_REQ.
*/
static void
{
/* flush all data in both queues */
/* flush all the queues upstream */
}
static int
{
int result = 0;
result++;
}
return (result);
}
static void
{
int len;
len = sizeof (em_event_ind_t);
switch (event) {
case PCE_DEV_IDENT:
break;
case PCE_INIT_DEV:
len += sizeof (struct pcm_make_dev);
break;
}
return;
switch (event) {
struct pcm_make_dev *devp;
case PCE_DEV_IDENT:
break;
case PCE_INIT_DEV:
break;
}
}
/* ARGSUSED */
{
int i;
int serviced = 0;
do {
pem_softint_pend = 0;
/* have an event */
serviced = 1;
}
}
} while (pem_softint_pend);
if (serviced)
return (DDI_INTR_CLAIMED);
return (DDI_INTR_UNCLAIMED);
}
static void
{
int i;
queue_t *q;
#if defined(PEM_DEBUG)
if (pem_debug)
#endif
if (pem_instances == NULL)
return;
for (i = 0, minors = pem_minors;
if (minors & 1) {
continue;
}
continue;
#if defined(PEM_DEBUG)
if (pem_debug)
"\tflags=%x, id=%x, wanted=%d\n",
#endif
}
}
}
}
static int
{
int i;
int didevents = 0;
#if defined(PEM_DEBUG)
if (pem_debug)
#endif
switch (event) {
struct pcm_make_dev *devp;
case PCE_DEV_IDENT:
(char *)arg,
break;
case PCE_INIT_DEV:
}
didevents = 1;
break;
}
}
if (didevents && pem_intr_id != 0) {
pem_softint_pend = 1;
} else
return (0);
}
/* ARGSUSED */
static void
{}
/* ARGSUSED */
static int
{
return (0);
}
static int
{
int bufsize;
bufsize = sizeof (em_adapter_info_ack_t);
sizeof (em_adapter_info_ack_t);
sizeof (struct power_entry);
}
}
}
return (PEME_OK);
}
static int
{
sizeof (em_socket_info_ack_t);
}
}
return (PEME_OK);
}
static int
{
}
return (PEME_OK);
}
/*
* pem_ident_socket()
* This primitive triggers artificial events for the
* socket. It basically recreates those events that
* have occurred on the socket up to this point in time
*/
static int
{
int num_minors, i;
struct pcm_make_dev *minors;
return (PEME_NO_INFO);
/* OK, have at least a name available */
(void *)ddi_get_name(dip));
for (i = 0; i < num_minors; i++) {
(void *)(minors + i));
}
if (num_minors > 0)
return (PEME_OK);
}