#if defined(DEBUG)
#define CS_DEBUG
#endif
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <sys/buf.h>
#include <sys/file.h>
#include <sys/uio.h>
#include <sys/conf.h>
#include <sys/stat.h>
#include <sys/autoconf.h>
#include <sys/vtoc.h>
#include <sys/dkio.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/debug.h>
#include <sys/varargs.h>
#include <sys/var.h>
#include <sys/proc.h>
#include <sys/thread.h>
#include <sys/utsname.h>
#include <sys/vtrace.h>
#include <sys/kstat.h>
#include <sys/kmem.h>
#include <sys/modctl.h>
#include <sys/kobj.h>
#include <sys/callb.h>
#include <sys/time.h>
#include <sys/pctypes.h>
#include <pcmcia/sys/cs_types.h>
#include <sys/pcmcia.h>
#include <sys/sservice.h>
#include <pcmcia/sys/cis.h>
#include <pcmcia/sys/cis_handlers.h>
#include <pcmcia/sys/cs.h>
#include <pcmcia/sys/cs_priv.h>
#include <pcmcia/sys/cs_stubs.h>
#include <pcmcia/sys/cs_strings.h>
int CardServices(int function, ...);
void *(*cis_parser)(int, ...) = NULL;
csfunction_t *cs_socket_services = NULL;
static event_t ss_to_cs_events(cs_socket_t *, event_t);
static event_t cs_cse2sbm(event_t);
static void cs_event_thread(uint32_t);
static int cs_card_insertion(cs_socket_t *, event_t);
static int cs_card_removal(cs_socket_t *);
static void cs_ss_thread(uint32_t);
void cs_ready_timeout(void *);
static int cs_card_for_client(client_t *);
static int cs_request_socket_mask(client_handle_t, request_socket_mask_t *);
static int cs_release_socket_mask(client_handle_t, release_socket_mask_t *);
static int cs_get_event_mask(client_handle_t, sockevent_t *);
static int cs_set_event_mask(client_handle_t, sockevent_t *);
static int cs_event2text(event2text_t *, int);
static int cs_read_event_status(cs_socket_t *, client_t *, event_t *,
get_ss_status_t *, int);
uint32_t cs_socket_event_softintr(caddr_t);
void cs_event_softintr_timeout(void *);
static int cs_get_status(client_handle_t, get_status_t *);
static uint32_t cs_sbm2cse(uint32_t);
static unsigned cs_merge_event_masks(cs_socket_t *, client_t *);
static int cs_set_socket_event_mask(cs_socket_t *, unsigned);
static uint32_t cs_add_socket(uint32_t);
static uint32_t cs_drop_socket(uint32_t);
static cs_socket_t *cs_get_sp(uint32_t);
static cs_socket_t *cs_find_sp(uint32_t);
static cs_window_t *cs_get_wp(uint32_t);
static cs_window_t *cs_find_wp(uint32_t);
static int cs_add_windows(int, uint32_t);
static uint32_t cs_ss_init();
static void cs_set_acc_attributes(set_window_t *, uint32_t);
cistpl_callout_t *cis_cistpl_std_callout;
static int cs_parse_tuple(client_handle_t, tuple_t *, cisparse_t *, cisdata_t);
static int cs_get_tuple_data(client_handle_t, tuple_t *);
static int cs_validate_cis(client_handle_t, cisinfo_t *);
static int cs_get_firstnext_tuple(client_handle_t, tuple_t *, uint32_t);
static int cs_create_cis(cs_socket_t *);
static int cs_destroy_cis(cs_socket_t *);
unsigned cs_create_next_client_minor(unsigned, unsigned);
static client_t *cs_find_client(client_handle_t, int *);
static client_handle_t cs_create_client_handle(unsigned, client_t *);
static int cs_destroy_client_handle(client_handle_t);
static int cs_register_client(client_handle_t *, client_reg_t *);
static int cs_deregister_client(client_handle_t);
static int cs_deregister_mtd(client_handle_t);
static void cs_clear_superclient_lock(int);
static int cs_add_client_to_socket(unsigned, client_handle_t *,
client_reg_t *, int);
static int cs_get_client_info(client_handle_t, client_info_t *);
static int cs_get_firstnext_client(get_firstnext_client_t *, uint32_t);
static int cs_request_window(client_handle_t, window_handle_t *, win_req_t *);
static int cs_release_window(window_handle_t);
static int cs_modify_window(window_handle_t, modify_win_t *);
static int cs_modify_mem_window(window_handle_t, modify_win_t *, win_req_t *,
int);
static int cs_map_mem_page(window_handle_t, map_mem_page_t *);
static int cs_find_mem_window(uint32_t, win_req_t *, uint32_t *);
static int cs_memwin_space_and_map_ok(inquire_window_t *, win_req_t *);
static int cs_valid_window_speed(inquire_window_t *, uint32_t);
static window_handle_t cs_create_window_handle(uint32_t);
static cs_window_t *cs_find_window(window_handle_t);
static int cs_find_io_win(uint32_t, iowin_char_t *, uint32_t *, uint32_t *);
static int cs_request_io(client_handle_t, io_req_t *);
static int cs_release_io(client_handle_t, io_req_t *);
static int cs_allocate_io_win(uint32_t, uint32_t, uint32_t *);
static int cs_setup_io_win(uint32_t, uint32_t, baseaddru_t *,
uint32_t *, uint32_t, uint32_t);
static int cs_request_irq(client_handle_t, irq_req_t *);
static int cs_release_irq(client_handle_t, irq_req_t *);
static int cs_request_configuration(client_handle_t, config_req_t *);
static int cs_release_configuration(client_handle_t, release_config_t *);
static int cs_modify_configuration(client_handle_t, modify_config_t *);
static int cs_access_configuration_register(client_handle_t,
access_config_reg_t *);
static int cs_reset_function(client_handle_t, reset_function_t *);
static int cs_get_configuration_info(client_handle_t *,
get_configuration_info_t *);
static int cs_get_cardservices_info(client_handle_t,
get_cardservices_info_t *);
static int cs_get_physical_adapter_info(client_handle_t,
get_physical_adapter_info_t *);
static uint32_t cs_get_socket(client_handle_t, uint32_t *, uint32_t *,
cs_socket_t **, client_t **);
static int cs_convert_speed(convert_speed_t *);
static int cs_convert_size(convert_size_t *);
static char *cs_error2text(int, int);
static int cs_map_log_socket(client_handle_t, map_log_socket_t *);
static int cs_convert_powerlevel(uint32_t, uint32_t, uint32_t, unsigned *);
static int cs_make_device_node(client_handle_t, make_device_node_t *);
static int cs_remove_device_node(client_handle_t, remove_device_node_t *);
static int cs_ddi_info(cs_ddi_info_t *);
static int cs_init_cis_window(cs_socket_t *, uint32_t *, acc_handle_t *,
uint32_t);
static int cs_sys_ctl(cs_sys_ctl_t *);
static int cs_max_client_handles = CS_MAX_CLIENTS;
static client_t cs_socket_services_client;
static client_types_t client_types[MAX_CLIENT_TYPES];
static cs_globals_t cs_globals;
int cs_reset_timeout_time = RESET_TIMEOUT_TIME;
int cs_rc1_delay = CS_RC1_DELAY;
int cs_rc2_delay = CS_RC2_DELAY;
int cs_rq_delay = CS_RQ_DELAY;
#ifdef CS_DEBUG
int cs_debug = 0;
#endif
int
cs_init()
{
client_types_t *ct;
client_t *client;
bzero((caddr_t)&cs_globals, sizeof (cs_globals_t));
mutex_init(&cs_globals.global_lock, NULL, MUTEX_DRIVER, NULL);
mutex_init(&cs_globals.window_lock, NULL, MUTEX_DRIVER, NULL);
cs_globals.init_state = GLOBAL_INIT_STATE_MUTEX;
client = &cs_socket_services_client;
bzero((caddr_t)client, sizeof (client_t));
client->client_handle = CS_SS_CLIENT_HANDLE;
client->flags |= (INFO_SOCKET_SERVICES | CLIENT_CARD_INSERTED);
ct = &client_types[0];
ct->type = INFO_IO_CLIENT;
ct->order = CLIENT_EVENTS_LIFO;
ct->next = &client_types[1];
ct = ct->next;
ct->type = INFO_MTD_CLIENT;
ct->order = CLIENT_EVENTS_FIFO;
ct->next = &client_types[2];
ct = ct->next;
ct->type = INFO_MEM_CLIENT;
ct->order = CLIENT_EVENTS_FIFO;
ct->next = NULL;
return (CS_SUCCESS);
}
int
cs_deinit()
{
cs_socket_t *sp;
int sn, have_clients = 0, have_sockets = 0;
cs_register_cardservices_t rcs;
#if defined(CS_DEBUG)
if (cs_debug > 1)
cmn_err(CE_CONT, "CS: cs_deinit\n");
#endif
rcs.magic = CS_STUBS_MAGIC;
rcs.function = CS_ENTRY_DEREGISTER;
(void) csx_register_cardservices(&rcs);
mutex_enter(&cs_globals.global_lock);
cs_globals.init_state |= GLOBAL_INIT_STATE_NO_CLIENTS;
mutex_exit(&cs_globals.global_lock);
for (sn = 0; sn < cs_globals.max_socket_num; sn++) {
if ((sp = cs_get_sp(sn)) != NULL) {
have_sockets++;
if (sp->client_list) {
cmn_err(CE_CONT, "cs_deinit: cannot unload module since "
"socket %d has registered clients\n", sn);
have_clients++;
}
}
}
if ((have_clients > 0) || (have_sockets > 0))
return (BAD_FUNCTION);
#ifdef XXX
SocketServices(CSUnregister);
mutex_enter(&cs_globals.global_lock);
cs_globals.init_state |= GLOBAL_INIT_STATE_UNLOADING;
mutex_exit(&cs_globals.global_lock);
UNTIMEOUT(cs_globals.sotfint_tmo);
mutex_enter(&cs_globals.global_lock);
if (cs_globals.init_state & GLOBAL_INIT_STATE_SOFTINTR) {
ddi_remove_softintr(cs_globals.softint_id);
cs_globals.init_state &= ~GLOBAL_INIT_STATE_SOFTINTR;
}
mutex_exit(&cs_globals.global_lock);
return (CS_SUCCESS);
for (sn = 0; sn < cs_globals.max_socket_num; sn++) {
set_socket_t set_socket;
if ((sp = cs_get_sp(sn)) != NULL) {
UNTIMEOUT(sp->rdybsy_tmo_id);
if (sp->init_state & SOCKET_INIT_STATE_MUTEX)
mutex_enter(&sp->lock);
sp->flags = SOCKET_UNLOAD_MODULE;
if (sp->init_state & SOCKET_INIT_STATE_SOFTINTR)
sp->init_state &= ~SOCKET_INIT_STATE_SOFTINTR;
if (sp->init_state & SOCKET_INIT_STATE_MUTEX)
mutex_exit(&sp->lock);
if (sp->init_state & SOCKET_INIT_STATE_MUTEX)
mutex_enter(&sp->cis_lock);
(void) cs_destroy_cis(sp);
if (sp->init_state & SOCKET_INIT_STATE_MUTEX)
mutex_exit(&sp->cis_lock);
if (sp->init_state & SOCKET_INIT_STATE_MUTEX)
mutex_enter(&sp->client_lock);
if (sp->init_state & SOCKET_INIT_STATE_THREAD) {
sp->thread_state = SOCKET_THREAD_EXIT;
cv_broadcast(&sp->thread_cv);
cv_wait(&sp->caller_cv, &sp->client_lock);
}
if (sp->init_state & SOCKET_INIT_STATE_MUTEX)
mutex_exit(&sp->client_lock);
if (sp->init_state & SOCKET_INIT_STATE_MUTEX)
mutex_enter(&sp->ss_thread_lock);
if (sp->init_state & SOCKET_INIT_STATE_SS_THREAD) {
sp->ss_thread_state = SOCKET_THREAD_EXIT;
cv_broadcast(&sp->ss_thread_cv);
cv_wait(&sp->ss_caller_cv, &sp->ss_thread_lock);
}
if (sp->init_state & SOCKET_INIT_STATE_MUTEX)
mutex_exit(&sp->ss_thread_lock);
if (sp->init_state & SOCKET_INIT_STATE_MUTEX) {
mutex_destroy(&sp->lock);
mutex_destroy(&sp->client_lock);
mutex_destroy(&sp->cis_lock);
mutex_destroy(&sp->ss_thread_lock);
}
if (sp->init_state & SOCKET_INIT_STATE_CV) {
cv_destroy(&sp->thread_cv);
cv_destroy(&sp->caller_cv);
cv_destroy(&sp->reset_cv);
cv_destroy(&sp->ss_thread_cv);
cv_destroy(&sp->ss_caller_cv);
}
#ifdef USE_IOMMAP_WINDOW
if (sp->io_mmap_window)
kmem_free(sp->io_mmap_window, sizeof (io_mmap_window_t));
#endif
sp->event_mask = 0;
set_socket.socket = sp->socket_num;
set_socket.SCIntMask = 0;
set_socket.IREQRouting = 0;
set_socket.IFType = IF_MEMORY;
set_socket.CtlInd = 0;
set_socket.State = (unsigned)~0;
(void) cs_convert_powerlevel(sp->socket_num, 0, VCC,
&set_socket.VccLevel);
(void) cs_convert_powerlevel(sp->socket_num, 0, VPP1,
&set_socket.Vpp1Level);
(void) cs_convert_powerlevel(sp->socket_num, 0, VPP2,
&set_socket.Vpp2Level);
if ((ret =
SocketServices(SS_SetSocket, &set_socket)) != SUCCESS) {
cmn_err(CE_CONT,
"cs_deinit: socket %d SS_SetSocket failure %d\n",
sp->socket_num, ret);
}
}
}
#endif
mutex_destroy(&cs_globals.global_lock);
mutex_destroy(&cs_globals.window_lock);
#ifdef XXX
if (cs_globals.sclient_list)
kmem_free(cs_globals.sclient_list,
(cs_globals.num_sockets * sizeof (struct sclient_list_t)));
cs_globals.sclient_list = NULL;
#endif
return (CS_SUCCESS);
}
int
CardServices(int function, ...)
{
va_list arglist;
int retcode = CS_UNSUPPORTED_FUNCTION;
cs_socket_t *socp;
uint32_t *offp;
acc_handle_t *hp;
client_handle_t ch;
client_handle_t *chp;
window_handle_t wh;
window_handle_t *whp;
tuple_t *tuple;
cisparse_t *cisparse;
#ifdef CS_DEBUG
if (cs_debug > 127) {
cmn_err(CE_CONT, "CardServices: called for function %s (0x%x)\n",
cs_error2text(function, CSFUN2TEXT_FUNCTION),
function);
}
#endif
va_start(arglist, function);
switch (function) {
case CISRegister: {
cisregister_t *cisr;
cisr = va_arg(arglist, cisregister_t *);
if (cisr->cis_magic != PCCS_MAGIC ||
cisr->cis_version != PCCS_VERSION) {
cmn_err(CE_WARN,
"CS: CISRegister (%lx, %lx, %lx, %lx) *ERROR*",
(long)cisr->cis_magic,
(long)cisr->cis_version,
(long)cisr->cis_parser,
(long)cisr->cistpl_std_callout);
retcode = CS_BAD_ARGS;
} else {
if (cisr->cis_parser != NULL)
cis_parser = cisr->cis_parser;
cis_cistpl_std_callout = cisr->cistpl_std_callout;
retcode = CS_SUCCESS;
}
}
break;
case CISUnregister:
cis_parser = NULL;
cis_cistpl_std_callout = NULL;
retcode = CS_SUCCESS;
break;
case InitCISWindow:
socp = va_arg(arglist, cs_socket_t *);
offp = va_arg(arglist, uint32_t *);
hp = va_arg(arglist, acc_handle_t *);
retcode = cs_init_cis_window(socp, offp, hp,
va_arg(arglist, uint32_t));
break;
case RegisterClient:
chp = va_arg(arglist, client_handle_t *),
retcode = cs_register_client(chp,
va_arg(arglist, client_reg_t *));
break;
case DeregisterClient:
retcode = cs_deregister_client(
va_arg(arglist, client_handle_t));
break;
case GetStatus:
ch = va_arg(arglist, client_handle_t);
retcode = cs_get_status(ch,
va_arg(arglist, get_status_t *));
break;
case ResetFunction:
ch = va_arg(arglist, client_handle_t);
retcode = cs_reset_function(ch,
va_arg(arglist, reset_function_t *));
break;
case SetEventMask:
ch = va_arg(arglist, client_handle_t);
retcode = cs_set_event_mask(ch,
va_arg(arglist, sockevent_t *));
break;
case GetEventMask:
ch = va_arg(arglist, client_handle_t);
retcode = cs_get_event_mask(ch,
va_arg(arglist, sockevent_t *));
break;
case RequestIO:
ch = va_arg(arglist, client_handle_t);
retcode = cs_request_io(ch,
va_arg(arglist, io_req_t *));
break;
case ReleaseIO:
ch = va_arg(arglist, client_handle_t);
retcode = cs_release_io(ch,
va_arg(arglist, io_req_t *));
break;
case RequestIRQ:
ch = va_arg(arglist, client_handle_t);
retcode = cs_request_irq(ch,
va_arg(arglist, irq_req_t *));
break;
case ReleaseIRQ:
ch = va_arg(arglist, client_handle_t);
retcode = cs_release_irq(ch,
va_arg(arglist, irq_req_t *));
break;
case RequestWindow:
ch = va_arg(arglist, client_handle_t);
whp = va_arg(arglist, window_handle_t *);
retcode = cs_request_window(ch, whp,
va_arg(arglist, win_req_t *));
break;
case ReleaseWindow:
retcode = cs_release_window(
va_arg(arglist, window_handle_t));
break;
case ModifyWindow:
wh = va_arg(arglist, window_handle_t);
retcode = cs_modify_window(wh,
va_arg(arglist, modify_win_t *));
break;
case MapMemPage:
wh = va_arg(arglist, window_handle_t);
retcode = cs_map_mem_page(wh,
va_arg(arglist, map_mem_page_t *));
break;
case RequestSocketMask:
ch = va_arg(arglist, client_handle_t);
retcode = cs_request_socket_mask(ch,
va_arg(arglist, request_socket_mask_t *));
break;
case ReleaseSocketMask:
ch = va_arg(arglist, client_handle_t);
retcode = cs_release_socket_mask(ch,
va_arg(arglist, release_socket_mask_t *));
break;
case RequestConfiguration:
ch = va_arg(arglist, client_handle_t);
retcode = cs_request_configuration(ch,
va_arg(arglist, config_req_t *));
break;
case GetPhysicalAdapterInfo:
ch = va_arg(arglist, client_handle_t);
retcode = cs_get_physical_adapter_info(ch,
va_arg(arglist, get_physical_adapter_info_t *));
break;
case GetCardServicesInfo:
ch = va_arg(arglist, client_handle_t);
retcode = cs_get_cardservices_info(ch,
va_arg(arglist, get_cardservices_info_t *));
break;
case GetConfigurationInfo:
chp = va_arg(arglist, client_handle_t *);
retcode = cs_get_configuration_info(chp,
va_arg(arglist, get_configuration_info_t *));
break;
case ModifyConfiguration:
ch = va_arg(arglist, client_handle_t);
retcode = cs_modify_configuration(ch,
va_arg(arglist, modify_config_t *));
break;
case AccessConfigurationRegister:
ch = va_arg(arglist, client_handle_t);
retcode = cs_access_configuration_register(ch,
va_arg(arglist, access_config_reg_t *));
break;
case ReleaseConfiguration:
ch = va_arg(arglist, client_handle_t);
retcode = cs_release_configuration(ch,
va_arg(arglist, release_config_t *));
break;
case OpenMemory:
cmn_err(CE_CONT, "CS: OpenMemory\n");
break;
case ReadMemory:
cmn_err(CE_CONT, "CS: ReadMemory\n");
break;
case WriteMemory:
cmn_err(CE_CONT, "CS: WriteMemory\n");
break;
case CopyMemory:
cmn_err(CE_CONT, "CS: CopyMemory\n");
break;
case RegisterEraseQueue:
cmn_err(CE_CONT, "CS: RegisterEraseQueue\n");
break;
case CheckEraseQueue:
cmn_err(CE_CONT, "CS: CheckEraseQueue\n");
break;
case DeregisterEraseQueue:
cmn_err(CE_CONT, "CS: DeregisterEraseQueue\n");
break;
case CloseMemory:
cmn_err(CE_CONT, "CS: CloseMemory\n");
break;
case GetFirstRegion:
cmn_err(CE_CONT, "CS: GetFirstRegion\n");
break;
case GetNextRegion:
cmn_err(CE_CONT, "CS: GetNextRegion\n");
break;
case GetFirstPartition:
cmn_err(CE_CONT, "CS: GetFirstPartition\n");
break;
case GetNextPartition:
cmn_err(CE_CONT, "CS: GetNextPartition\n");
break;
case ReturnSSEntry:
cmn_err(CE_CONT, "CS: ReturnSSEntry\n");
break;
case MapLogSocket:
ch = va_arg(arglist, client_handle_t);
retcode = cs_map_log_socket(ch,
va_arg(arglist, map_log_socket_t *));
break;
case MapPhySocket:
cmn_err(CE_CONT, "CS: MapPhySocket\n");
break;
case MapLogWindow:
cmn_err(CE_CONT, "CS: MapLogWindow\n");
break;
case MapPhyWindow:
cmn_err(CE_CONT, "CS: MapPhyWindow\n");
break;
case RegisterMTD:
cmn_err(CE_CONT, "CS: RegisterMTD\n");
break;
case RegisterTimer:
cmn_err(CE_CONT, "CS: RegisterTimer\n");
break;
case SetRegion:
cmn_err(CE_CONT, "CS: SetRegion\n");
break;
case RequestExclusive:
cmn_err(CE_CONT, "CS: RequestExclusive\n");
break;
case ReleaseExclusive:
cmn_err(CE_CONT, "CS: ReleaseExclusive\n");
break;
case GetFirstClient:
retcode = cs_get_firstnext_client(
va_arg(arglist, get_firstnext_client_t *),
CS_GET_FIRST_FLAG);
break;
case GetNextClient:
retcode = cs_get_firstnext_client(
va_arg(arglist, get_firstnext_client_t *),
CS_GET_NEXT_FLAG);
break;
case GetClientInfo:
ch = va_arg(arglist, client_handle_t);
retcode = cs_get_client_info(ch,
va_arg(arglist, client_info_t *));
break;
case AddSocketServices:
cmn_err(CE_CONT, "CS: AddSocketServices\n");
break;
case ReplaceSocketServices:
cmn_err(CE_CONT, "CS: ReplaceSocketServices\n");
break;
case VendorSpecific:
cmn_err(CE_CONT, "CS: VendorSpecific\n");
break;
case AdjustResourceInfo:
cmn_err(CE_CONT, "CS: AdjustResourceInfo\n");
break;
case ValidateCIS:
ch = va_arg(arglist, client_handle_t);
retcode = cs_validate_cis(ch,
va_arg(arglist, cisinfo_t *));
break;
case GetFirstTuple:
ch = va_arg(arglist, client_handle_t);
retcode = cs_get_firstnext_tuple(ch,
va_arg(arglist, tuple_t *),
CS_GET_FIRST_FLAG);
break;
case GetNextTuple:
ch = va_arg(arglist, client_handle_t);
retcode = cs_get_firstnext_tuple(ch,
va_arg(arglist, tuple_t *),
CS_GET_NEXT_FLAG);
break;
case GetTupleData:
ch = va_arg(arglist, client_handle_t);
retcode = cs_get_tuple_data(ch,
va_arg(arglist, tuple_t *));
break;
case ParseTuple:
ch = va_arg(arglist, client_handle_t);
tuple = va_arg(arglist, tuple_t *);
cisparse = va_arg(arglist, cisparse_t *);
retcode = cs_parse_tuple(ch, tuple, cisparse,
va_arg(arglist, uint_t));
break;
case MakeDeviceNode:
ch = va_arg(arglist, client_handle_t);
retcode = cs_make_device_node(ch,
va_arg(arglist, make_device_node_t *));
break;
case RemoveDeviceNode:
ch = va_arg(arglist, client_handle_t);
retcode = cs_remove_device_node(ch,
va_arg(arglist, remove_device_node_t *));
break;
case ConvertSpeed:
retcode = cs_convert_speed(
va_arg(arglist, convert_speed_t *));
break;
case ConvertSize:
retcode = cs_convert_size(
va_arg(arglist, convert_size_t *));
break;
case Event2Text:
retcode = cs_event2text(
va_arg(arglist, event2text_t *), 1);
break;
case Error2Text: {
error2text_t *cft;
cft = va_arg(arglist, error2text_t *);
(void) strcpy(cft->text,
cs_error2text(cft->item, CSFUN2TEXT_RETURN));
retcode = CS_SUCCESS;
}
break;
case CS_DDI_Info:
retcode = cs_ddi_info(va_arg(arglist, cs_ddi_info_t *));
break;
case CS_Sys_Ctl:
retcode = cs_sys_ctl(va_arg(arglist, cs_sys_ctl_t *));
break;
default:
cmn_err(CE_CONT, "CS: {unknown function %d}\n", function);
break;
}
va_end(arglist);
#ifdef CS_DEBUG
if (cs_debug > 127) {
cmn_err(CE_CONT, "CardServices: returning %s (0x%x)\n",
cs_error2text(retcode, CSFUN2TEXT_RETURN),
retcode);
}
#endif
return (retcode);
}
static int
cs_parse_tuple(client_handle_t client_handle, tuple_t *tuple,
cisparse_t *cisparse, cisdata_t cisdata)
{
cs_socket_t *sp;
client_t *client;
uint32_t fn;
int ret;
if ((ret = cs_get_socket(client_handle, &tuple->Socket,
&fn, &sp, &client)) != CS_SUCCESS)
return (ret);
if (!(client->flags & CLIENT_CARD_INSERTED))
return (CS_NO_CARD);
if (!(tuple->CISOffset))
return (CS_BAD_ARGS);
mutex_enter(&sp->cis_lock);
if ((sp->cis_flags & CW_VALID_CIS) &&
(sp->cis[fn].flags & CW_VALID_CIS)) {
ret = (int)(uintptr_t)CIS_PARSER(CISP_CIS_PARSE_TUPLE,
cis_cistpl_std_callout,
tuple->CISOffset,
(tuple->Attributes & TUPLE_RETURN_NAME)?
HANDTPL_RETURN_NAME:
HANDTPL_PARSE_LTUPLE,
cisparse, cisdata);
mutex_exit(&sp->cis_lock);
if (ret == CISTPLF_UNKNOWN)
return (CS_UNKNOWN_TUPLE);
if (ret != CISTPLF_NOERROR)
return (CS_BAD_CIS);
ret = CS_SUCCESS;
} else {
mutex_exit(&sp->cis_lock);
ret = CS_NO_CIS;
}
return (ret);
}
static int
cs_get_firstnext_tuple(client_handle_t client_handle,
tuple_t *tuple, uint32_t flags)
{
cs_socket_t *sp;
client_t *client;
uint32_t fn;
int ret;
if ((ret = cs_get_socket(client_handle, &tuple->Socket, &fn,
&sp, &client)) != CS_SUCCESS)
return (ret);
if (!(client->flags & CLIENT_CARD_INSERTED))
return (CS_NO_CARD);
mutex_enter(&sp->cis_lock);
if ((!(sp->cis_flags & CW_VALID_CIS)) ||
(!(sp->cis[fn].flags & CW_VALID_CIS))) {
mutex_exit(&sp->cis_lock);
return (CS_NO_CIS);
}
if (tuple->Attributes & TUPLE_RETURN_IGNORED_TUPLES)
flags |= CIS_GET_LTUPLE_IGNORE;
if ((flags & CIS_GET_LTUPLE_OPMASK) & CS_GET_FIRST_FLAG) {
if (sp->cis_flags & CW_MULTI_FUNCTION_CIS) {
if ((tuple->CISOffset =
GET_FIRST_LTUPLE(sp->cis[CS_GLOBAL_CIS].cis,
flags)) == NULL) {
mutex_exit(&sp->cis_lock);
return (CS_NO_MORE_ITEMS);
}
} else {
tuple->CISOffset = sp->cis[0].cis;
}
} else {
cistpl_t *tp;
if (tuple->CISOffset == NULL) {
mutex_exit(&sp->cis_lock);
return (CS_NO_MORE_ITEMS);
}
if ((tp = GET_NEXT_TUPLE(tuple->CISOffset, flags)) == NULL) {
if (sp->cis_flags & CW_MULTI_FUNCTION_CIS) {
if ((tuple->CISOffset->flags & CISTPLF_GLOBAL_CIS) &&
(fn != CS_GLOBAL_CIS)) {
tp = GET_FIRST_LTUPLE(sp->cis[fn].cis, flags);
}
}
}
if ((tuple->CISOffset = tp) == NULL) {
mutex_exit(&sp->cis_lock);
return (CS_NO_MORE_ITEMS);
}
}
if (tuple->DesiredTuple != RETURN_FIRST_TUPLE) {
cistpl_t *tp;
if ((tp = FIND_LTUPLE_FWD(tuple->CISOffset,
tuple->DesiredTuple, flags)) == NULL) {
if (sp->cis_flags & CW_MULTI_FUNCTION_CIS) {
if ((tuple->CISOffset->flags & CISTPLF_GLOBAL_CIS) &&
(fn != CS_GLOBAL_CIS)) {
tp = FIND_FIRST_LTUPLE(sp->cis[fn].cis,
tuple->DesiredTuple, flags);
}
}
}
if ((tuple->CISOffset = tp) == NULL) {
mutex_exit(&sp->cis_lock);
return (CS_NO_MORE_ITEMS);
}
}
tuple->Flags = tuple->CISOffset->flags;
tuple->TupleCode = tuple->CISOffset->type;
tuple->TupleLink = tuple->CISOffset->len;
tuple->TupleDataLen = tuple->CISOffset->len;
mutex_exit(&sp->cis_lock);
return (CS_SUCCESS);
}
static int
cs_get_tuple_data(client_handle_t client_handle, tuple_t *tuple)
{
cs_socket_t *sp;
client_t *client;
int ret, nbytes;
uint32_t fn, flags;
cisdata_t *tsd, *tdd;
uint32_t newoffset;
acc_handle_t cis_handle;
if ((ret = cs_get_socket(client_handle, &tuple->Socket, &fn,
&sp, &client)) != CS_SUCCESS)
return (ret);
if (!(client->flags & CLIENT_CARD_INSERTED))
return (CS_NO_CARD);
mutex_enter(&sp->cis_lock);
if ((sp->cis_flags & CW_VALID_CIS) &&
(sp->cis[fn].flags & CW_VALID_CIS)) {
if (!(tuple->CISOffset)) {
mutex_exit(&sp->cis_lock);
return (CS_NO_MORE_ITEMS);
}
tuple->TupleDataMax = sizeof (tuple->TupleData);
if ((int)tuple->TupleOffset >= min((int)tuple->TupleLink,
(int)tuple->TupleDataMax)) {
mutex_exit(&sp->cis_lock);
return (CS_NO_MORE_ITEMS);
}
tuple->TupleDataLen = tuple->TupleLink;
if ((nbytes = min((int)tuple->TupleDataMax -
(int)tuple->TupleOffset,
(int)tuple->TupleDataLen -
(int)tuple->TupleOffset)) < 1) {
mutex_exit(&sp->cis_lock);
return (CS_BAD_ARGS);
}
tdd = tuple->TupleData;
bzero((caddr_t)tdd, sizeof (tuple->TupleData));
switch (tuple->CISOffset->flags & CISTPLF_SPACE_MASK) {
case CISTPLF_LM_SPACE:
tsd = (tuple->CISOffset->data +
(unsigned)tuple->TupleOffset);
while (nbytes--)
*tdd++ = *tsd++;
break;
case CISTPLF_AM_SPACE:
case CISTPLF_CM_SPACE:
newoffset = tuple->CISOffset->offset;
if (tuple->CISOffset->flags & CISTPLF_AM_SPACE) {
flags = CISTPLF_AM_SPACE;
newoffset += ((tuple->TupleOffset * 2) + 4);
} else {
flags = CISTPLF_CM_SPACE;
newoffset += (tuple->TupleOffset + 2);
}
if (cs_init_cis_window(sp, &newoffset, &cis_handle,
flags) != CS_SUCCESS) {
mutex_exit(&sp->cis_lock);
cmn_err(CE_CONT, "cs_get_tuple_data: socket %d "
"can't init CIS window\n",
sp->socket_num);
return (CS_GENERAL_FAILURE);
}
while (nbytes--) {
*tdd++ = csx_Get8(cis_handle, newoffset++);
if (tuple->CISOffset->flags & CISTPLF_AM_SPACE)
newoffset++;
}
break;
default:
mutex_exit(&sp->cis_lock);
return (CS_GENERAL_FAILURE);
}
ret = CS_SUCCESS;
} else {
ret = CS_NO_CIS;
}
mutex_exit(&sp->cis_lock);
return (ret);
}
static int
cs_validate_cis(client_handle_t client_handle, cisinfo_t *cisinfo)
{
cs_socket_t *sp;
client_t *client;
uint32_t fn;
int ret;
if ((ret = cs_get_socket(client_handle, &cisinfo->Socket, &fn,
&sp, &client)) != CS_SUCCESS)
return (ret);
if (!(client->flags & CLIENT_CARD_INSERTED))
return (CS_NO_CARD);
mutex_enter(&sp->cis_lock);
if ((sp->cis_flags & CW_VALID_CIS) &&
(sp->cis[fn].flags & CW_VALID_CIS)) {
cisinfo->Chains = sp->cis[fn].nchains;
cisinfo->Tuples = sp->cis[fn].ntuples;
if ((fn != CS_GLOBAL_CIS) &&
(sp->cis[CS_GLOBAL_CIS].flags & CW_VALID_CIS)) {
cisinfo->Chains += sp->cis[CS_GLOBAL_CIS].nchains;
cisinfo->Tuples += sp->cis[CS_GLOBAL_CIS].ntuples;
}
ret = CS_SUCCESS;
} else {
cisinfo->Chains = 0;
cisinfo->Tuples = 0;
ret = CS_NO_CIS;
}
mutex_exit(&sp->cis_lock);
return (ret);
}
int
cs_init_cis_window(cs_socket_t *sp, uint32_t *offset,
acc_handle_t *hp, uint32_t flags)
{
set_window_t sw;
get_window_t gw;
inquire_window_t iw;
set_page_t set_page;
cs_window_t *cw;
if (!SOCKET_HAS_CIS_WINDOW(sp)) {
cmn_err(CE_CONT,
"cs_init_cis_window: socket %d has no CIS window\n",
sp->socket_num);
return (CS_BAD_WINDOW);
}
if ((cw = cs_get_wp(sp->cis_win_num)) == NULL)
return (CS_BAD_WINDOW);
if (!(cw->state & CW_CIS)) {
cmn_err(CE_CONT,
"cs_init_cis_window: socket %d invalid CIS window state 0x%x\n",
sp->socket_num, cw->state);
return (CS_BAD_WINDOW);
}
iw.window = sp->cis_win_num;
SocketServices(SS_InquireWindow, &iw);
if (iw.mem_win_char.MemWndCaps & WC_SIZE) {
sw.WindowSize = sp->cis_win_size;
set_page.offset = ((*offset / sp->cis_win_size) *
sp->cis_win_size);
} else {
set_page.offset = ((*offset / iw.mem_win_char.MinSize) *
iw.mem_win_char.MinSize);
sw.WindowSize = (((*offset & ~(PAGESIZE - 1)) &
(set_page.offset - 1)) + PAGESIZE);
}
*offset = *offset - set_page.offset;
#ifdef CS_DEBUG
if (cs_debug > 1)
cmn_err(CE_CONT, "cs_init_cis_window: WindowSize 0x%x "
"offset 0x%x\n",
(int)sw.WindowSize,
(int)set_page.offset);
if (cs_debug > 1)
cmn_err(CE_CONT, "\t*offset = 0x%x space = %s\n",
(int)*offset,
(flags & CISTPLF_AM_SPACE)?
"CISTPLF_AM_SPACE":"CISTPLF_CM_SPACE");
#endif
sw.window = sp->cis_win_num;
sw.socket = sp->socket_num;
sw.state = (WS_ENABLED | WS_EXACT_MAPIN);
sw.attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
sw.attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
sw.attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
sw.speed = CIS_DEFAULT_SPEED;
sw.base = 0;
if (SocketServices(SS_SetWindow, &sw) != SUCCESS) {
sp->cis_win_num = PCMCIA_MAX_WINDOWS;
cw->state = 0;
return (CS_BAD_WINDOW);
} else {
set_page.window = sp->cis_win_num;
set_page.page = 0;
set_page.state = PS_ENABLED;
if (flags & CISTPLF_AM_SPACE)
set_page.state |= PS_ATTRIBUTE;
if (SocketServices(SS_SetPage, &set_page) != SUCCESS) {
sp->cis_win_num = PCMCIA_MAX_WINDOWS;
cw->state = 0;
return (CS_BAD_WINDOW);
}
}
gw.window = sp->cis_win_num;
gw.socket = sp->socket_num;
if (SocketServices(SS_GetWindow, &gw) != SUCCESS)
return (CS_BAD_WINDOW);
*hp = (acc_handle_t)gw.handle;
return (CS_SUCCESS);
}
static int
cs_register_client(client_handle_t *ch, client_reg_t *cr)
{
uint32_t sn;
int super_client = 0;
sclient_reg_t *scr = cr->priv;
struct sclient_list_t *scli;
if (cs_globals.init_state & GLOBAL_INIT_STATE_NO_CLIENTS)
return (CS_OUT_OF_RESOURCE);
if (CS_VERSION < cr->Version)
return (CS_BAD_VERSION);
switch (cr->Attributes & INFO_CLIENT_TYPE_MASK) {
case INFO_SOCKET_SERVICES:
*ch = cs_socket_services_client.client_handle;
return (CS_SUCCESS);
case (INFO_CSI_CLIENT | INFO_IO_CLIENT):
break;
case INFO_IO_CLIENT:
case INFO_MTD_CLIENT:
case INFO_MEM_CLIENT:
if (cr->EventMask & CS_EVENT_ALL_CLIENTS)
return (CS_BAD_ATTRIBUTE);
break;
case (INFO_IO_CLIENT | INFO_MTD_CLIENT | INFO_SOCKET_SERVICES):
if ((!(cr->Attributes & INFO_CARD_SHARE)) ||
(cr->Attributes & INFO_CARD_EXCL))
return (CS_BAD_ATTRIBUTE);
mutex_enter(&cs_globals.global_lock);
if (cs_globals.flags & GLOBAL_SUPER_CLIENT_REGISTERED) {
mutex_exit(&cs_globals.global_lock);
return (CS_NO_MORE_ITEMS);
}
cs_globals.flags |= GLOBAL_SUPER_CLIENT_REGISTERED;
mutex_exit(&cs_globals.global_lock);
super_client = CLIENT_SUPER_CLIENT;
break;
default:
return (CS_BAD_ATTRIBUTE);
}
if (super_client != CLIENT_SUPER_CLIENT) {
if (cr->Attributes & INFO_CSI_CLIENT)
sn = (uint32_t)(uintptr_t)cr->priv;
else
sn = DIP2SOCKET_NUM(cr->dip);
return (cs_add_client_to_socket(sn, ch, cr, super_client));
}
scr->num_clients = 0;
scr->max_socket_num = cs_globals.max_socket_num;
scr->num_sockets = cs_globals.num_sockets;
scr->num_windows = cs_globals.num_windows;
*(scr->sclient_list) = cs_globals.sclient_list;
for (sn = 0; sn < scr->num_sockets; sn++) {
scli = scr->sclient_list[sn];
if ((scli->error = cs_add_client_to_socket(sn, &scli->client_handle,
cr, super_client)) == CS_SUCCESS) {
scr->num_clients++;
}
}
if (!scr->num_clients) {
return (CS_BAD_HANDLE);
}
return (CS_SUCCESS);
}
static int
cs_add_client_to_socket(unsigned sn, client_handle_t *ch,
client_reg_t *cr, int super_client)
{
cs_socket_t *sp;
client_t *client, *cclp;
int error, cie = 1;
int client_lock_acquired;
if (cr->event_handler == NULL)
return (CS_BAD_ARGS);
if ((sp = cs_get_sp(sn)) == NULL)
return (CS_BAD_SOCKET);
EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp);
mutex_enter(&sp->lock);
client = sp->client_list;
while (client) {
if (!(cr->Attributes & INFO_CSI_CLIENT) &&
(client->dip == cr->dip)) {
mutex_exit(&sp->lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
cmn_err(CE_CONT, "cs_add_client_to_socket: socket %d "
"function 0x%x\n"
"\tclient already registered with "
"handle 0x%x\n",
(int)CS_GET_SOCKET_NUMBER(sn),
(int)CS_GET_FUNCTION_NUMBER(sn),
(int)client->client_handle);
return (CS_BAD_HANDLE);
}
client = client->next;
}
mutex_exit(&sp->lock);
cclp = (client_t *)kmem_zalloc(sizeof (client_t), KM_SLEEP);
mutex_enter(&sp->lock);
if (!(*ch = cs_create_client_handle(sn, cclp))) {
mutex_exit(&sp->lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
kmem_free(cclp, sizeof (client_t));
return (CS_OUT_OF_RESOURCE);
}
if (!(client = cs_find_client(*ch, &error))) {
mutex_exit(&sp->lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
cmn_err(CE_CONT, "cs_add_client_to_socket: socket %d function 0x%x "
"invalid client handle created handle 0x%x\n",
(int)CS_GET_SOCKET_NUMBER(sn),
(int)CS_GET_FUNCTION_NUMBER(sn),
(int)*ch);
return (error);
}
client->dip = cr->dip;
cr->driver_name[MODMAXNAMELEN - 1] = '\0';
client->driver_name = kmem_zalloc(strlen(cr->driver_name) + 1,
KM_SLEEP);
(void) strcpy(client->driver_name, cr->driver_name);
client->instance = ddi_get_instance(cr->dip);
client->flags = (cr->Attributes & INFO_CLIENT_TYPE_MASK);
client->event_callback_handler = cr->event_handler;
bcopy((caddr_t)&cr->event_callback_args,
(caddr_t)&client->event_callback_args,
sizeof (event_callback_args_t));
client->event_callback_args.client_handle = *ch;
client->io_alloc.Window1 = PCMCIA_MAX_WINDOWS;
client->io_alloc.Window2 = PCMCIA_MAX_WINDOWS;
cr->iblk_cookie = sp->iblk;
cr->idev_cookie = sp->idev;
client->global_mask = cr->EventMask;
if (cr->Attributes & INFO_CSI_CLIENT)
client->flags |= CLIENT_CSI_CLIENT;
if (super_client == CLIENT_SUPER_CLIENT)
client->flags |= CLIENT_SUPER_CLIENT;
client->flags |= (cr->Attributes & INFO_CARD_FLAGS_MASK);
if (cr->Attributes & (INFO_CARD_SHARE | INFO_CARD_EXCL))
client->pending_events = CS_EVENT_REGISTRATION_COMPLETE;
if (client->flags & CLIENT_CSI_CLIENT) {
get_ss_status_t get_ss_status;
get_ss_status.socket = sp->socket_num;
if (SocketServices(SS_GetStatus, &get_ss_status) != SUCCESS) {
mutex_exit(&sp->lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BAD_SOCKET);
}
if (!(cs_sbm2cse(get_ss_status.CardState) &
CS_EVENT_CARD_INSERTION))
cie = 0;
}
if (cs_card_for_client(client) && (cie != 0)) {
client->pending_events |= CS_EVENT_CARD_INSERTION;
client->flags |= CLIENT_CARD_INSERTED;
}
sp->num_clients++;
mutex_exit(&sp->lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_SUCCESS);
}
static int
cs_deregister_client(client_handle_t client_handle)
{
cs_socket_t *sp;
client_t *client;
int error, super_client = 0;
int client_lock_acquired;
if (CLIENT_HANDLE_IS_SS(client_handle))
return (CS_SUCCESS);
if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL)
return (CS_BAD_SOCKET);
EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp);
if (!(client = cs_find_client(client_handle, &error))) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (error);
}
if (client->flags & (CLIENT_IO_ALLOCATED |
CLIENT_IRQ_ALLOCATED |
CLIENT_WIN_ALLOCATED |
REQ_CONFIGURATION_DONE |
REQ_SOCKET_MASK_DONE |
REQ_IO_DONE |
REQ_IRQ_DONE)) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BUSY);
}
if (client->flags & CLIENT_MTD_IN_PROGRESS) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_IN_USE);
}
if (client->flags & INFO_MTD_CLIENT)
(void) cs_deregister_mtd(client_handle);
if (client->flags & CLIENT_SUPER_CLIENT)
super_client = CLIENT_SUPER_CLIENT;
kmem_free(client->driver_name, strlen(client->driver_name) + 1);
error = cs_destroy_client_handle(client_handle);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
cs_clear_superclient_lock(super_client);
return (error);
}
unsigned
cs_create_next_client_minor(unsigned socket_num, unsigned next_minor)
{
unsigned max_client_handles = cs_max_client_handles;
do {
next_minor &= CS_MAX_CLIENTS_MASK;
if (!cs_find_client(MAKE_CLIENT_HANDLE(
CS_GET_SOCKET_NUMBER(socket_num),
CS_GET_FUNCTION_NUMBER(socket_num),
next_minor), NULL)) {
return (next_minor);
}
next_minor++;
} while (max_client_handles--);
return (0);
}
static client_t *
cs_find_client(client_handle_t client_handle, int *error)
{
cs_socket_t *sp;
client_t *clp;
if (CLIENT_HANDLE_IS_SS(client_handle))
return (&cs_socket_services_client);
if (!(CHECK_SOCKET_NUM(GET_CLIENT_SOCKET(client_handle),
cs_globals.max_socket_num))) {
if (error)
*error = CS_BAD_SOCKET;
return (NULL);
}
if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL) {
if (error)
*error = CS_BAD_SOCKET;
return (NULL);
}
clp = sp->client_list;
while (clp) {
if (clp->client_handle == client_handle)
return (clp);
clp = clp->next;
}
if (error)
*error = CS_BAD_HANDLE;
return (NULL);
}
static int
cs_destroy_client_handle(client_handle_t client_handle)
{
client_t *clp;
cs_socket_t *sp;
int error = CS_BAD_HANDLE;
if ((!(clp = cs_find_client(client_handle, &error))) ||
(CLIENT_HANDLE_IS_SS(client_handle)))
return (error);
if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL)
return (CS_BAD_SOCKET);
mutex_enter(&sp->lock);
sp->next_cl_minor = GET_CLIENT_MINOR(client_handle);
if (clp->prev) {
clp->prev->next = clp->next;
} else {
sp->client_list = clp->next;
}
if (clp->next)
clp->next->prev = clp->prev;
sp->num_clients--;
mutex_exit(&sp->lock);
kmem_free(clp, sizeof (client_t));
return (CS_SUCCESS);
}
static client_handle_t
cs_create_client_handle(unsigned socket_num, client_t *cclp)
{
client_t *clp;
cs_socket_t *sp;
unsigned next_minor;
client_handle_t client_handle;
if ((sp = cs_get_sp(socket_num)) == NULL)
return (0);
if (!(next_minor =
cs_create_next_client_minor(socket_num, sp->next_cl_minor)))
return (0);
client_handle = MAKE_CLIENT_HANDLE(CS_GET_SOCKET_NUMBER(socket_num),
CS_GET_FUNCTION_NUMBER(socket_num),
next_minor);
if (cs_find_client(client_handle, NULL)) {
cmn_err(CE_CONT,
"cs_create_client_handle: duplicate client handle 0x%x\n",
(int)client_handle);
return (0);
}
if (!sp->client_list) {
sp->client_list = cclp;
clp = sp->client_list;
} else {
clp = sp->client_list;
while (clp->next) {
clp = clp->next;
}
clp->next = cclp;
clp->next->prev = clp;
clp = clp->next;
}
clp->client_handle = client_handle;
sp->next_cl_minor =
cs_create_next_client_minor(socket_num, sp->next_cl_minor);
return (client_handle);
}
static void
cs_clear_superclient_lock(int super_client)
{
if (super_client == CLIENT_SUPER_CLIENT) {
mutex_enter(&cs_globals.global_lock);
cs_globals.flags &= ~GLOBAL_SUPER_CLIENT_REGISTERED;
mutex_exit(&cs_globals.global_lock);
}
}
uint32_t
cs_event(event_t event, uint32_t sn, uint32_t arg)
{
client_t *client;
cs_socket_t *sp;
client_types_t *ct;
uint32_t ret = CS_SUCCESS;
switch (event) {
case PCE_SS_INIT_STATE:
mutex_enter(&cs_globals.global_lock);
switch (sn) {
case PCE_SS_STATE_INIT:
if ((ret = cs_ss_init()) == CS_SUCCESS)
cs_globals.init_state |= GLOBAL_INIT_STATE_SS_READY;
break;
case PCE_SS_STATE_DEINIT:
cs_globals.init_state &= ~GLOBAL_INIT_STATE_SS_READY;
break;
default:
ret = CS_UNSUPPORTED_FUNCTION;
cmn_err(CE_CONT, "cs_event: PCE_SS_INIT_STATE invalid "
"directive: 0x%x\n", sn);
break;
}
mutex_exit(&cs_globals.global_lock);
return (ret);
case PCE_ADD_SOCKET:
return (cs_add_socket(sn));
case PCE_DROP_SOCKET:
return (cs_drop_socket(sn));
}
if ((sp = cs_get_sp(sn)) == NULL)
return (CS_BAD_SOCKET);
if (sp->flags & SOCKET_UNLOAD_MODULE) {
if (event == PCE_CARD_INSERT)
cmn_err(CE_CONT, "PCMCIA: socket %d disabled - please "
"remove card\n", sn);
return (CS_SUCCESS);
}
mutex_enter(&sp->lock);
#ifdef CS_DEBUG
if (cs_debug > 1) {
event2text_t event2text;
event2text.event = event;
(void) cs_event2text(&event2text, 0);
cmn_err(CE_CONT, "cs_event: event=%s (x%x), socket=0x%x\n",
event2text.text, (int)event, (int)sn);
}
#endif
sp->events |= ss_to_cs_events(sp, event);
ct = &client_types[0];
while (ct) {
client = sp->client_list;
if (ct->order & CLIENT_EVENTS_LIFO) {
client_t *clp = NULL;
while (client) {
clp = client;
client = client->next;
}
client = clp;
}
while (client) {
client->events |= ((sp->events & ~CS_EVENT_CARD_INSERTION) &
(client->event_mask | client->global_mask));
if (client->flags & ct->type) {
#ifdef CS_DEBUG
if (cs_debug > 1) {
cmn_err(CE_CONT, "cs_event: socket %d client [%s] "
"events 0x%x flags 0x%x\n",
sn, client->driver_name,
(int)client->events,
(int)client->flags);
}
#endif
if (client->events & CS_EVENT_PM_SUSPEND) {
if (client->flags & CLIENT_CARD_INSERTED) {
CLIENT_EVENT_CALLBACK(client, CS_EVENT_PM_SUSPEND,
CS_EVENT_PRI_HIGH);
}
client->events &= ~CS_EVENT_PM_SUSPEND;
}
if (client->events & CS_EVENT_CARD_REMOVAL) {
if (client->flags & CLIENT_CARD_INSERTED) {
client->flags &= ~(CLIENT_CARD_INSERTED |
CLIENT_SENT_INSERTION);
CLIENT_EVENT_CALLBACK(client,
CS_EVENT_CARD_REMOVAL,
CS_EVENT_PRI_HIGH);
if ((client->event_mask | client->global_mask) &
CS_EVENT_CARD_REMOVAL_LOWP) {
client->events |= CS_EVENT_CARD_REMOVAL_LOWP;
}
}
client->events &= ~CS_EVENT_CARD_REMOVAL;
}
}
if (ct->order & CLIENT_EVENTS_LIFO) {
client = client->prev;
} else {
client = client->next;
}
}
ct = ct->next;
}
if (sp->events)
sp->flags |= SOCKET_NEEDS_THREAD;
if ((sp->init_state & SOCKET_INIT_STATE_SOFTINTR) &&
!(cs_globals.init_state & GLOBAL_INIT_STATE_UNLOADING))
ddi_trigger_softintr(sp->softint_id);
mutex_exit(&sp->lock);
return (CS_SUCCESS);
}
static int
cs_card_insertion(cs_socket_t *sp, event_t event)
{
int ret;
UNTIMEOUT(sp->rdybsy_tmo_id);
#ifdef CS_DEBUG
if (cs_debug > 1) {
cmn_err(CE_CONT, "cs_card_insertion: event=0x%x, socket=0x%x\n",
(int)event, sp->socket_num);
}
#endif
if (event & CS_EVENT_CARD_INSERTION) {
set_socket_t set_socket;
get_ss_status_t gs;
if (!SOCKET_HAS_CIS_WINDOW(sp)) {
cmn_err(CE_CONT,
"cs_card_insertion: socket %d has no "
"CIS window\n",
sp->socket_num);
return (CS_GENERAL_FAILURE);
}
mutex_enter(&sp->lock);
sp->event_mask = (CS_EVENT_CARD_REMOVAL |
CS_EVENT_CARD_READY);
mutex_exit(&sp->lock);
set_socket.socket = sp->socket_num;
set_socket.SCIntMask = (SBM_CD | SBM_RDYBSY);
set_socket.IREQRouting = 0;
set_socket.IFType = IF_MEMORY;
set_socket.CtlInd = 0;
set_socket.State = (unsigned)~0;
(void) cs_convert_powerlevel(sp->socket_num, 50, VCC,
&set_socket.VccLevel);
(void) cs_convert_powerlevel(sp->socket_num, 50, VPP1,
&set_socket.Vpp1Level);
(void) cs_convert_powerlevel(sp->socket_num, 50, VPP2,
&set_socket.Vpp2Level);
if ((ret = SocketServices(SS_SetSocket, &set_socket)) != SUCCESS) {
cmn_err(CE_CONT,
"cs_card_insertion: socket %d SS_SetSocket failure %d\n",
sp->socket_num, ret);
return (ret);
}
mutex_enter(&sp->lock);
sp->events &= ~(CS_EVENT_CARD_READY | CS_EVENT_READY_TIMEOUT);
mutex_exit(&sp->lock);
SocketServices(SS_ResetSocket, sp->socket_num,
RESET_MODE_CARD_ONLY);
mutex_enter(&sp->lock);
sp->thread_state |= SOCKET_RESET_TIMER;
(void) timeout(cs_ready_timeout, sp,
drv_usectohz(cs_reset_timeout_time * 1000));
cv_wait(&sp->reset_cv, &sp->lock);
sp->thread_state &= ~SOCKET_RESET_TIMER;
mutex_exit(&sp->lock);
#ifdef CS_DEBUG
if (cs_debug > 2) {
cmn_err(CE_CONT, "cs_card_insertion: socket %d out of RESET "
"for %d mS sp->events 0x%x\n",
sp->socket_num, cs_reset_timeout_time, (int)sp->events);
}
#endif
if (sp->events & CS_EVENT_CARD_REMOVAL) {
#ifdef CS_DEBUG
if (cs_debug > 0) {
cmn_err(CE_CONT, "cs_card_insertion: socket %d "
"CS_EVENT_CARD_REMOVAL event "
"terminating insertion "
"processing\n",
sp->socket_num);
}
#endif
return (CS_SUCCESS);
}
gs.socket = sp->socket_num;
gs.CardState = 0;
if ((ret = SocketServices(SS_GetStatus, &gs)) != SUCCESS) {
cmn_err(CE_CONT,
"cs_card_insertion: socket %d SS_GetStatus failure %d\n",
sp->socket_num, ret);
return (ret);
}
mutex_enter(&sp->lock);
if ((sp->events & CS_EVENT_CARD_READY) ||
(gs.CardState & SBM_RDYBSY)) {
event = CS_EVENT_CARD_READY;
#ifdef CS_DEBUG
if (cs_debug > 1) {
cmn_err(CE_CONT, "cs_card_insertion: socket %d card "
"READY\n", sp->socket_num);
}
#endif
} else {
#ifdef CS_DEBUG
if (cs_debug > 1) {
cmn_err(CE_CONT, "cs_card_insertion: socket %d setting "
"READY timer\n", sp->socket_num);
}
#endif
sp->rdybsy_tmo_id = timeout(cs_ready_timeout, sp,
READY_TIMEOUT_TIME);
sp->thread_state |= SOCKET_WAIT_FOR_READY;
}
mutex_exit(&sp->lock);
}
if (event == CS_EVENT_CARD_READY) {
get_socket_t get_socket;
set_socket_t set_socket;
mutex_enter(&sp->lock);
sp->event_mask = CS_EVENT_CARD_REMOVAL;
mutex_exit(&sp->lock);
get_socket.socket = sp->socket_num;
if (SocketServices(SS_GetSocket, &get_socket) != SUCCESS) {
cmn_err(CE_CONT,
"cs_card_insertion: socket %d SS_GetSocket failed\n",
sp->socket_num);
return (CS_BAD_SOCKET);
}
set_socket.socket = sp->socket_num;
set_socket.SCIntMask = SBM_CD;
set_socket.VccLevel = get_socket.VccLevel;
set_socket.Vpp1Level = get_socket.Vpp1Level;
set_socket.Vpp2Level = get_socket.Vpp2Level;
set_socket.IREQRouting = get_socket.IRQRouting;
set_socket.IFType = get_socket.IFType;
set_socket.CtlInd = get_socket.CtlInd;
set_socket.State = (unsigned)~0;
if (SocketServices(SS_SetSocket, &set_socket) != SUCCESS) {
cmn_err(CE_CONT,
"cs_card_insertion: socket %d SS_SetSocket failed\n",
sp->socket_num);
return (CS_BAD_SOCKET);
}
mutex_enter(&sp->cis_lock);
if ((ret = cs_create_cis(sp)) != CS_SUCCESS) {
mutex_exit(&sp->cis_lock);
return (ret);
}
mutex_exit(&sp->cis_lock);
if (sp->events & CS_EVENT_CARD_REMOVAL) {
mutex_enter(&sp->cis_lock);
(void) cs_destroy_cis(sp);
mutex_exit(&sp->cis_lock);
} else {
mutex_enter(&sp->ss_thread_lock);
sp->ss_thread_state |= SOCKET_THREAD_CSCISInit;
cv_broadcast(&sp->ss_thread_cv);
mutex_exit(&sp->ss_thread_lock);
}
}
if (event == CS_EVENT_SS_UPDATED) {
client_t *client;
mutex_enter(&sp->lock);
client = sp->client_list;
while (client) {
client->events |= (CS_EVENT_CARD_INSERTION &
(client->event_mask | client->global_mask));
client = client->next;
}
mutex_exit(&sp->lock);
}
return (CS_SUCCESS);
}
static int
cs_card_removal(cs_socket_t *sp)
{
set_socket_t set_socket;
int ret;
#ifdef CS_DEBUG
if (cs_debug > 0) {
cmn_err(CE_CONT, "cs_card_removal: socket %d\n", sp->socket_num);
}
#endif
UNTIMEOUT(sp->rdybsy_tmo_id);
mutex_enter(&sp->lock);
sp->thread_state &= ~(SOCKET_WAIT_FOR_READY | SOCKET_RESET_TIMER);
sp->event_mask = CS_EVENT_CARD_INSERTION;
mutex_exit(&sp->lock);
set_socket.socket = sp->socket_num;
set_socket.SCIntMask = SBM_CD;
set_socket.IREQRouting = 0;
set_socket.IFType = IF_MEMORY;
set_socket.CtlInd = 0;
set_socket.State = (unsigned)~0;
(void) cs_convert_powerlevel(sp->socket_num, 0, VCC,
&set_socket.VccLevel);
(void) cs_convert_powerlevel(sp->socket_num, 0, VPP1,
&set_socket.Vpp1Level);
(void) cs_convert_powerlevel(sp->socket_num, 0, VPP2,
&set_socket.Vpp2Level);
if ((ret = SocketServices(SS_SetSocket, &set_socket)) != SUCCESS) {
cmn_err(CE_CONT,
"cs_card_removal: socket %d SS_SetSocket failure %d\n",
sp->socket_num, ret);
return (ret);
}
#ifdef CS_DEBUG
if (cs_debug > 2) {
cmn_err(CE_CONT, "cs_card_removal: socket %d "
"calling cs_destroy_cis\n",
sp->socket_num);
}
#endif
mutex_enter(&sp->cis_lock);
(void) cs_destroy_cis(sp);
mutex_exit(&sp->cis_lock);
#ifdef CS_DEBUG
if (cs_debug > 2) {
cmn_err(CE_CONT, "cs_card_removal: calling CSCardRemoved\n");
}
#endif
SocketServices(CSCardRemoved, sp->socket_num);
return (CS_SUCCESS);
}
static event_t
ss_to_cs_events(cs_socket_t *sp, event_t event)
{
event_t revent = 0;
switch (event) {
case PCE_CARD_STATUS_CHANGE:
revent |= CS_EVENT_STATUS_CHANGE;
break;
case PCE_CARD_REMOVAL:
if (sp->flags & SOCKET_CARD_INSERTED) {
sp->flags &= ~(SOCKET_CARD_INSERTED | SOCKET_IS_IO);
revent |= CS_EVENT_CARD_REMOVAL;
sp->events &= ~(CS_EVENT_CARD_INSERTION |
CS_EVENT_CARD_READY |
CS_EVENT_READY_TIMEOUT);
sp->thread_state &= ~SOCKET_WAIT_FOR_READY;
}
break;
case PCE_CARD_INSERT:
if (!(sp->flags & SOCKET_CARD_INSERTED)) {
sp->flags |= SOCKET_CARD_INSERTED;
revent |= CS_EVENT_CARD_INSERTION;
}
break;
case PCE_CARD_READY:
if (sp->flags & SOCKET_CARD_INSERTED)
revent |= CS_EVENT_CARD_READY;
break;
case PCE_CARD_BATTERY_WARN:
if (sp->flags & SOCKET_CARD_INSERTED)
revent |= CS_EVENT_BATTERY_LOW;
break;
case PCE_CARD_BATTERY_DEAD:
if (sp->flags & SOCKET_CARD_INSERTED)
revent |= CS_EVENT_BATTERY_DEAD;
break;
case PCE_CARD_WRITE_PROTECT:
if (sp->flags & SOCKET_CARD_INSERTED)
revent |= CS_EVENT_WRITE_PROTECT;
break;
case PCE_PM_RESUME:
revent |= CS_EVENT_PM_RESUME;
break;
case PCE_PM_SUSPEND:
revent |= CS_EVENT_PM_SUSPEND;
break;
default:
cmn_err(CE_CONT, "ss_to_cs_events: unknown event 0x%x\n",
(int)event);
break;
}
return (revent);
}
void
cs_ready_timeout(void *arg)
{
cs_socket_t *sp = arg;
kcondvar_t *cvp = NULL;
mutex_enter(&sp->lock);
if (sp->thread_state & SOCKET_RESET_TIMER) {
#ifdef CS_DEBUG
if (cs_debug > 1) {
cmn_err(CE_CONT, "cs_ready_timeout: SOCKET_RESET_TIMER socket %d\n",
sp->socket_num);
}
#endif
cvp = &sp->reset_cv;
}
if (sp->thread_state & SOCKET_WAIT_FOR_READY) {
sp->events |= CS_EVENT_READY_TIMEOUT;
cvp = &sp->thread_cv;
#ifdef CS_DEBUG
if (cs_debug > 1) {
cmn_err(CE_CONT, "cs_ready_timeout: SOCKET_WAIT_FOR_READY "
"socket %d\n", sp->socket_num);
}
#endif
}
mutex_exit(&sp->lock);
if (cvp)
cv_broadcast(cvp);
}
void
cs_event_softintr_timeout(void *arg)
{
mutex_enter(&cs_globals.global_lock);
if (!(cs_globals.init_state & GLOBAL_INIT_STATE_UNLOADING)) {
mutex_exit(&cs_globals.global_lock);
(void) cs_socket_event_softintr(NULL);
cs_globals.sotfint_tmo = timeout(cs_event_softintr_timeout,
NULL, SOFTINT_TIMEOUT_TIME);
} else {
mutex_exit(&cs_globals.global_lock);
}
}
uint32_t
cs_socket_event_softintr(caddr_t notused)
{
cs_socket_t *sp;
uint32_t sn;
int ret = DDI_INTR_UNCLAIMED;
mutex_enter(&cs_globals.global_lock);
if ((cs_globals.init_state & GLOBAL_INIT_STATE_UNLOADING) ||
(cs_globals.init_state & GLOBAL_IN_SOFTINTR)) {
mutex_exit(&cs_globals.global_lock);
return (ret);
} else {
cs_globals.init_state |= GLOBAL_IN_SOFTINTR;
mutex_exit(&cs_globals.global_lock);
}
for (sn = 0; sn < cs_globals.max_socket_num; sn++) {
if ((sp = cs_get_sp(sn)) != NULL) {
if (sp->init_state & SOCKET_INIT_STATE_READY) {
if (!(sp->flags & SOCKET_UNLOAD_MODULE) &&
(sp->flags & SOCKET_NEEDS_THREAD)) {
ret = DDI_INTR_CLAIMED;
mutex_enter(&sp->client_lock);
cv_broadcast(&sp->thread_cv);
mutex_exit(&sp->client_lock);
}
}
}
}
mutex_enter(&cs_globals.global_lock);
cs_globals.init_state &= ~GLOBAL_IN_SOFTINTR;
mutex_exit(&cs_globals.global_lock);
return (ret);
}
static void
cs_event_thread(uint32_t sn)
{
cs_socket_t *sp;
client_t *client;
client_types_t *ct;
if ((sp = cs_get_sp(sn)) == NULL)
return;
#ifdef CS_DEBUG
if (cs_debug > 1) {
cmn_err(CE_CONT, "cs_event_thread: socket %d thread started\n",
sp->socket_num);
}
#endif
CALLB_CPR_INIT(&sp->cprinfo_cs, &sp->client_lock,
callb_generic_cpr, "cs_event_thread");
mutex_enter(&sp->client_lock);
for (;;) {
CALLB_CPR_SAFE_BEGIN(&sp->cprinfo_cs);
cv_wait(&sp->thread_cv, &sp->client_lock);
CALLB_CPR_SAFE_END(&sp->cprinfo_cs, &sp->client_lock);
mutex_enter(&sp->lock);
sp->flags &= ~SOCKET_NEEDS_THREAD;
mutex_exit(&sp->lock);
if (sp->thread_state & SOCKET_THREAD_EXIT) {
#ifdef CS_DEBUG
if (cs_debug > 1) {
cmn_err(CE_CONT, "cs_event_thread: socket %d "
"SOCKET_THREAD_EXIT\n",
sp->socket_num);
}
#endif
CALLB_CPR_EXIT(&sp->cprinfo_cs);
cv_broadcast(&sp->caller_cv);
mutex_exit(&sp->client_lock);
return;
}
#ifdef CS_DEBUG
if (cs_debug > 1) {
cmn_err(CE_CONT, "cs_event_thread: socket %d sp->events 0x%x\n",
sp->socket_num,
(int)sp->events);
}
#endif
if (sp->events & CS_EVENT_CARD_INSERTION) {
mutex_enter(&sp->lock);
sp->events &= ~CS_EVENT_CARD_INSERTION;
mutex_exit(&sp->lock);
if ((sp->events & CS_EVENT_CARD_REMOVAL) == 0) {
(void) cs_card_insertion(sp, CS_EVENT_CARD_INSERTION);
}
#ifdef CS_DEBUG
else if (cs_debug > 0) {
cmn_err(CE_CONT, "cs_event_thread: socket %d "
"CS_EVENT_CARD_REMOVAL event "
"terminating "
"CS_EVENT_CARD_INSERTION "
"processing\n", sp->socket_num);
}
#endif
}
if (sp->events & (CS_EVENT_CARD_READY | CS_EVENT_READY_TIMEOUT)) {
mutex_enter(&sp->lock);
sp->events &= ~(CS_EVENT_CARD_READY | CS_EVENT_READY_TIMEOUT);
mutex_exit(&sp->lock);
if (sp->thread_state & SOCKET_WAIT_FOR_READY) {
mutex_enter(&sp->lock);
sp->thread_state &= ~SOCKET_WAIT_FOR_READY;
mutex_exit(&sp->lock);
(void) cs_card_insertion(sp, CS_EVENT_CARD_READY);
}
}
if (sp->events & CS_EVENT_SS_UPDATED) {
mutex_enter(&sp->lock);
sp->events &= ~CS_EVENT_SS_UPDATED;
mutex_exit(&sp->lock);
(void) cs_card_insertion(sp, CS_EVENT_SS_UPDATED);
}
if (sp->events & CS_EVENT_STATUS_CHANGE) {
event_t revent;
mutex_enter(&sp->cis_lock);
mutex_enter(&sp->lock);
sp->events &= ~CS_EVENT_STATUS_CHANGE;
client = sp->client_list;
while (client) {
if (client->flags & CLIENT_CARD_INSERTED) {
(void) cs_read_event_status(sp, client,
&revent, NULL, 0);
client->events = ((client->events | revent) &
(client->event_mask |
client->global_mask));
}
client = client->next;
}
mutex_exit(&sp->lock);
mutex_exit(&sp->cis_lock);
}
ct = &client_types[0];
while (ct) {
client = sp->client_list;
if (ct->order & CLIENT_EVENTS_LIFO) {
client_t *clp = NULL;
while (client) {
clp = client;
client = client->next;
}
client = clp;
}
while (client) {
if (client->flags & ct->type) {
uint32_t bit = 0;
event_t event;
while (client->events) {
switch (event = CS_BIT_GET(client->events, bit)) {
case CS_EVENT_REGISTRATION_COMPLETE:
CLIENT_EVENT_CALLBACK(client, event,
CS_EVENT_PRI_LOW);
break;
case CS_EVENT_CARD_INSERTION:
if (cs_card_for_client(client)) {
int send_insertion;
mutex_enter(&sp->lock);
send_insertion = client->flags;
client->flags |=
(CLIENT_CARD_INSERTED |
CLIENT_SENT_INSERTION);
mutex_exit(&sp->lock);
if (!(send_insertion &
CLIENT_SENT_INSERTION)) {
CLIENT_EVENT_CALLBACK(client,
event, CS_EVENT_PRI_LOW);
}
}
break;
case CS_EVENT_CARD_REMOVAL_LOWP:
mutex_enter(&sp->lock);
client->flags &= ~CLIENT_SENT_INSERTION;
mutex_exit(&sp->lock);
CLIENT_EVENT_CALLBACK(client,
CS_EVENT_CARD_REMOVAL,
CS_EVENT_PRI_LOW);
break;
case CS_EVENT_CARD_REMOVAL:
if (client->flags & CLIENT_CARD_INSERTED) {
mutex_enter(&sp->lock);
client->flags &=
~(CLIENT_CARD_INSERTED |
CLIENT_SENT_INSERTION);
mutex_exit(&sp->lock);
CLIENT_EVENT_CALLBACK(client, event,
CS_EVENT_PRI_LOW);
}
break;
case CS_EVENT_WRITE_PROTECT:
if (client->flags & CLIENT_CARD_INSERTED) {
get_ss_status_t gs;
mutex_enter(&sp->cis_lock);
mutex_enter(&sp->lock);
(void) cs_read_event_status(sp, client,
NULL,
&gs, 0);
if (gs.CardState & SBM_WP) {
client->event_callback_args.info =
(void *)
CS_EVENT_WRITE_PROTECT_WPON;
} else {
client->event_callback_args.info =
(void *)
CS_EVENT_WRITE_PROTECT_WPOFF;
}
mutex_exit(&sp->lock);
mutex_exit(&sp->cis_lock);
CLIENT_EVENT_CALLBACK(client, event,
CS_EVENT_PRI_LOW);
}
break;
case CS_EVENT_CLIENT_INFO:
CLIENT_EVENT_CALLBACK(client, event,
CS_EVENT_PRI_LOW);
break;
case 0:
break;
default:
if (client->flags & CLIENT_CARD_INSERTED) {
CLIENT_EVENT_CALLBACK(client, event,
CS_EVENT_PRI_LOW);
}
break;
}
mutex_enter(&sp->lock);
CS_BIT_CLEAR(client->events, bit);
mutex_exit(&sp->lock);
bit++;
}
}
if (ct->order & CLIENT_EVENTS_LIFO) {
client = client->prev;
} else {
client = client->next;
}
}
ct = ct->next;
}
if (sp->events & CS_EVENT_CARD_REMOVAL) {
mutex_enter(&sp->lock);
sp->events &= ~CS_EVENT_CARD_REMOVAL;
mutex_exit(&sp->lock);
(void) cs_card_removal(sp);
}
if (sp->thread_state & SOCKET_WAIT_SYNC) {
mutex_enter(&sp->lock);
sp->thread_state &= ~SOCKET_WAIT_SYNC;
mutex_exit(&sp->lock);
cv_broadcast(&sp->caller_cv);
}
}
}
static int
cs_card_for_client(client_t *client)
{
if ((client->flags & (CLIENT_SUPER_CLIENT | CLIENT_CSI_CLIENT)) &&
((client->global_mask | client->event_mask) &
CS_EVENT_ALL_CLIENTS))
return (1);
if (ddi_getprop(DDI_DEV_T_ANY, client->dip, (DDI_PROP_CANSLEEP |
DDI_PROP_NOTPROM),
PCM_DEV_ACTIVE, 0)) {
#ifdef CS_DEBUG
if (cs_debug > 1) {
cmn_err(CE_CONT, "cs_card_for_client: client handle 0x%x "
"driver [%s] says %s found\n",
(int)client->client_handle,
client->driver_name,
PCM_DEV_ACTIVE);
}
#endif
return (1);
}
return (0);
}
static void
cs_ss_thread(uint32_t sn)
{
cs_socket_t *sp;
if ((sp = cs_get_sp(sn)) == NULL)
return;
CALLB_CPR_INIT(&sp->cprinfo_ss, &sp->ss_thread_lock,
callb_generic_cpr, "cs_ss_thread");
mutex_enter(&sp->ss_thread_lock);
for (;;) {
CALLB_CPR_SAFE_BEGIN(&sp->cprinfo_ss);
cv_wait(&sp->ss_thread_cv, &sp->ss_thread_lock);
CALLB_CPR_SAFE_END(&sp->cprinfo_ss, &sp->ss_thread_lock);
if (sp->ss_thread_state & SOCKET_THREAD_EXIT) {
#ifdef CS_DEBUG
if (cs_debug > 1) {
cmn_err(CE_CONT, "cs_ss_thread: socket %d "
"SOCKET_THREAD_EXIT\n",
sp->socket_num);
}
#endif
CALLB_CPR_EXIT(&sp->cprinfo_ss);
cv_broadcast(&sp->ss_caller_cv);
mutex_exit(&sp->ss_thread_lock);
return;
}
#ifdef CS_DEBUG
if (cs_debug > 1) {
cmn_err(CE_CONT, "cs_ss_thread: socket %d "
"ss_thread_state = 0x%x\n",
(int)sp->socket_num,
(int)sp->ss_thread_state);
}
#endif
if (sp->ss_thread_state & SOCKET_THREAD_CSCISInit) {
sp->ss_thread_state &= ~SOCKET_THREAD_CSCISInit;
if (!(sp->flags & SOCKET_CARD_INSERTED)) {
cmn_err(CE_CONT, "cs_ss_thread %d "
"card NOT inserted\n",
sp->socket_num);
}
#ifdef CS_DEBUG
if (cs_debug > 1) {
cmn_err(CE_CONT, "cs_ss_thread: socket %d calling "
"CSCISInit\n", sp->socket_num);
}
#endif
SocketServices(CSCISInit, sp->socket_num);
mutex_enter(&sp->lock);
sp->events |= CS_EVENT_SS_UPDATED;
mutex_exit(&sp->lock);
cv_broadcast(&sp->thread_cv);
}
}
}
static int
cs_request_socket_mask(client_handle_t client_handle,
request_socket_mask_t *se)
{
cs_socket_t *sp;
client_t *client;
int error;
int client_lock_acquired;
if (CLIENT_HANDLE_IS_SS(client_handle))
return (CS_SUCCESS);
if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL)
return (CS_BAD_SOCKET);
EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp);
if (!(client = cs_find_client(client_handle, &error))) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (error);
}
mutex_enter(&sp->lock);
if (client->flags & REQ_SOCKET_MASK_DONE) {
mutex_exit(&sp->lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_IN_USE);
}
client->event_mask = se->EventMask;
if (client->pending_events) {
client->events |= client->pending_events;
client->pending_events = 0;
#ifdef CS_DEBUG
if (cs_debug > 1) {
cmn_err(CE_CONT, "cs_request_socket_mask: client_handle = 0x%x "
"driver_name = [%s] events = 0x%x\n",
(int)client->client_handle,
client->driver_name,
(int)client->events);
}
#endif
}
client->flags |= REQ_SOCKET_MASK_DONE;
(void) cs_set_socket_event_mask(sp, cs_merge_event_masks(sp, client));
mutex_exit(&sp->lock);
if (client->events) {
cv_broadcast(&sp->thread_cv);
#ifdef CS_DEBUG
if (cs_debug > 1) {
cmn_err(CE_CONT, "cs_request_socket_mask: did cv_broadcast for "
"client_handle = 0x%x "
"driver_name = [%s] events = 0x%x\n",
(int)client->client_handle,
client->driver_name,
(int)client->events);
}
#endif
}
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_SUCCESS);
}
static int
cs_release_socket_mask(client_handle_t client_handle,
release_socket_mask_t *rsm)
{
cs_socket_t *sp;
client_t *client;
int error;
int client_lock_acquired;
if (CLIENT_HANDLE_IS_SS(client_handle))
return (CS_SUCCESS);
if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL)
return (CS_BAD_SOCKET);
EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp);
if (!(client = cs_find_client(client_handle, &error))) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (error);
}
mutex_enter(&sp->lock);
if (!(client->flags & REQ_SOCKET_MASK_DONE)) {
mutex_exit(&sp->lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BAD_SOCKET);
}
client->event_mask = 0;
client->global_mask = 0;
client->flags &= ~REQ_SOCKET_MASK_DONE;
(void) cs_set_socket_event_mask(sp, cs_merge_event_masks(sp, client));
mutex_exit(&sp->lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_SUCCESS);
}
static int
cs_get_event_mask(client_handle_t client_handle, sockevent_t *se)
{
cs_socket_t *sp;
client_t *client;
int error;
int client_lock_acquired;
if (CLIENT_HANDLE_IS_SS(client_handle))
return (CS_SUCCESS);
if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL)
return (CS_BAD_SOCKET);
EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp);
if (!(client = cs_find_client(client_handle, &error))) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (error);
}
mutex_enter(&sp->lock);
#ifdef XXX
if (!(client->flags & CLIENT_CARD_INSERTED)) {
mutex_exit(&sp->lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_NO_CARD);
}
#endif
if (se->Attributes & CONF_EVENT_MASK_CLIENT) {
if (!(client->flags & REQ_SOCKET_MASK_DONE)) {
mutex_exit(&sp->lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BAD_SOCKET);
}
se->EventMask = client->event_mask;
} else {
se->EventMask = client->global_mask;
}
mutex_exit(&sp->lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_SUCCESS);
}
static int
cs_set_event_mask(client_handle_t client_handle, sockevent_t *se)
{
cs_socket_t *sp;
client_t *client;
int error;
int client_lock_acquired;
if (CLIENT_HANDLE_IS_SS(client_handle))
return (CS_SUCCESS);
if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL)
return (CS_BAD_SOCKET);
EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp);
if (!(client = cs_find_client(client_handle, &error))) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (error);
}
mutex_enter(&sp->lock);
#ifdef XXX
if (!(client->flags & CLIENT_CARD_INSERTED)) {
mutex_exit(&sp->lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_NO_CARD);
}
#endif
if (se->Attributes & CONF_EVENT_MASK_CLIENT) {
if (!(client->flags & REQ_SOCKET_MASK_DONE)) {
mutex_exit(&sp->lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BAD_SOCKET);
}
client->event_mask = se->EventMask;
} else {
client->global_mask = se->EventMask;
}
(void) cs_set_socket_event_mask(sp, cs_merge_event_masks(sp, client));
mutex_exit(&sp->lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_SUCCESS);
}
static int
cs_read_event_status(cs_socket_t *sp, client_t *client, event_t *revent,
get_ss_status_t *gs, int flags)
{
cfg_regs_t prrd = 0;
if ((sp->flags & SOCKET_CARD_INSERTED) ||
(flags & CS_RES_IGNORE_NO_CARD)) {
if (sp->flags & SOCKET_IS_IO) {
if (client->present & CONFIG_PINREPL_REG_PRESENT) {
acc_handle_t cis_handle;
uint32_t newoffset = client->config_regs_offset;
if (cs_init_cis_window(sp, &newoffset, &cis_handle,
CISTPLF_AM_SPACE) != CS_SUCCESS) {
cmn_err(CE_CONT, "cs_read_event_status: socket %d "
"can't init CIS window\n",
sp->socket_num);
return (CS_GENERAL_FAILURE);
}
prrd = csx_Get8(cis_handle, client->config_regs.prr_p);
prrd &= client->pin;
#ifdef CS_DEBUG
if (cs_debug > 1) {
cmn_err(CE_CONT, "cs_read_event_status: "
"prrd 0x%x client->pin 0x%x\n",
(int)prrd,
client->pin);
cmn_err(CE_CONT, "PRR(1) = [%s%s%s%s%s%s%s%s]\n",
((prrd & PRR_WP_STATUS)?
"PRR_WP_STATUS ":""),
((prrd & PRR_READY_STATUS)?
"PRR_READY_STATUS ":""),
((prrd & PRR_BVD2_STATUS)?
"PRR_BVD2_STATUS ":""),
((prrd & PRR_BVD1_STATUS)?
"PRR_BVD1_STATUS ":""),
((prrd & PRR_WP_EVENT)?
"PRR_WP_EVENT ":""),
((prrd & PRR_READY_EVENT)?
"PRR_READY_EVENT ":""),
((prrd & PRR_BVD2_EVENT)?
"PRR_BVD2_EVENT ":""),
((prrd & PRR_BVD1_EVENT)?
"PRR_BVD1_EVENT ":""));
}
#endif
if (revent) {
get_socket_t get_socket;
set_socket_t set_socket;
*revent = 0;
PRR_EVENT(prrd, PRR_WP_EVENT, PRR_WP_STATUS,
CS_EVENT_WRITE_PROTECT, *revent);
PRR_EVENT(prrd, PRR_READY_EVENT, PRR_READY_STATUS,
CS_EVENT_CARD_READY, *revent);
PRR_EVENT(prrd, PRR_BVD2_EVENT, PRR_BVD2_STATUS,
CS_EVENT_BATTERY_LOW, *revent);
PRR_EVENT(prrd, PRR_BVD1_EVENT, PRR_BVD1_STATUS,
CS_EVENT_BATTERY_DEAD, *revent);
#ifdef CS_DEBUG
if (cs_debug > 1) {
cmn_err(CE_CONT, "PRR() = [%s%s%s%s%s%s%s%s]\n",
((prrd & PRR_WP_STATUS)?
"PRR_WP_STATUS ":""),
((prrd & PRR_READY_STATUS)?
"PRR_READY_STATUS ":""),
((prrd & PRR_BVD2_STATUS)?
"PRR_BVD2_STATUS ":""),
((prrd & PRR_BVD1_STATUS)?
"PRR_BVD1_STATUS ":""),
((prrd & PRR_WP_EVENT)?
"PRR_WP_EVENT ":""),
((prrd & PRR_READY_EVENT)?
"PRR_READY_EVENT ":""),
((prrd & PRR_BVD2_EVENT)?
"PRR_BVD2_EVENT ":""),
((prrd & PRR_BVD1_EVENT)?
"PRR_BVD1_EVENT ":""));
}
#endif
if (prrd)
csx_Put8(cis_handle, client->config_regs.prr_p,
prrd);
if (client->pin) {
get_socket.socket = sp->socket_num;
SocketServices(SS_GetSocket, &get_socket);
set_socket.socket = sp->socket_num;
set_socket.SCIntMask =
get_socket.SCIntMask | SBM_BVD1;
set_socket.VccLevel = get_socket.VccLevel;
set_socket.Vpp1Level = get_socket.Vpp1Level;
set_socket.Vpp2Level = get_socket.Vpp2Level;
set_socket.IREQRouting = get_socket.IRQRouting;
set_socket.IFType = get_socket.IFType;
set_socket.CtlInd = get_socket.CtlInd;
set_socket.State = get_socket.state;
SocketServices(SS_SetSocket, &set_socket);
}
}
}
}
if (gs) {
gs->socket = sp->socket_num;
gs->CardState = 0;
if (SocketServices(SS_GetStatus, gs) != SUCCESS)
return (CS_BAD_SOCKET);
if (sp->flags & SOCKET_IS_IO) {
gs->CardState &= ~(SBM_WP | SBM_BVD1 |
SBM_BVD2 | SBM_RDYBSY);
if (prrd & PRR_WP_STATUS)
gs->CardState |= SBM_WP;
if (prrd & PRR_BVD2_STATUS)
gs->CardState |= SBM_BVD2;
if (prrd & PRR_BVD1_STATUS)
gs->CardState |= SBM_BVD1;
if (!(client->present & CONFIG_PINREPL_REG_PRESENT) ||
((client->present & CONFIG_PINREPL_REG_PRESENT) &&
!((client->pin &
(PRR_READY_STATUS | PRR_READY_EVENT)) ==
(PRR_READY_STATUS | PRR_READY_EVENT))) ||
(prrd & PRR_READY_STATUS))
gs->CardState |= SBM_RDYBSY;
#ifdef CS_DEBUG
if (cs_debug > 1) {
cmn_err(CE_CONT, "cs_read_event_status: prrd 0x%x "
"client->pin 0x%x "
"gs->CardState 0x%x\n",
prrd, client->pin, gs->CardState);
}
#endif
}
}
return (CS_SUCCESS);
}
return (CS_NO_CARD);
}
static int
cs_get_status(client_handle_t client_handle, get_status_t *gs)
{
cs_socket_t *sp;
client_t *client;
get_ss_status_t get_ss_status;
get_socket_t get_socket;
set_socket_t set_socket;
int error;
int client_lock_acquired;
if (CLIENT_HANDLE_IS_SS(client_handle))
return (CS_SUCCESS);
if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL)
return (CS_BAD_SOCKET);
EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp);
if (!(client = cs_find_client(client_handle, &error))) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (error);
}
mutex_enter(&sp->cis_lock);
if ((error = cs_read_event_status(sp, client, NULL, &get_ss_status,
CS_RES_IGNORE_NO_CARD)) != CS_SUCCESS) {
mutex_exit(&sp->cis_lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (error);
}
mutex_exit(&sp->cis_lock);
gs->raw_CardState = cs_sbm2cse(get_ss_status.CardState);
gs->CardState = gs->raw_CardState;
if (!(client->flags & CLIENT_CARD_INSERTED))
gs->CardState &= ~CS_EVENT_CARD_INSERTION;
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
get_socket.socket = sp->socket_num;
if (SocketServices(SS_GetSocket, &get_socket) != SUCCESS)
return (CS_BAD_SOCKET);
gs->SocketState = cs_sbm2cse(get_socket.state);
set_socket.socket = sp->socket_num;
set_socket.SCIntMask = get_socket.SCIntMask;
set_socket.VccLevel = get_socket.VccLevel;
set_socket.Vpp1Level = get_socket.Vpp1Level;
set_socket.Vpp2Level = get_socket.Vpp2Level;
set_socket.IREQRouting = get_socket.IRQRouting;
set_socket.IFType = get_socket.IFType;
set_socket.CtlInd = get_socket.CtlInd;
set_socket.State = (unsigned)~0;
if (SocketServices(SS_SetSocket, &set_socket) != SUCCESS)
return (CS_BAD_SOCKET);
return (CS_SUCCESS);
}
static event_t
cs_cse2sbm(event_t event_mask)
{
event_t sbm_event = 0;
if (event_mask & CS_EVENT_WRITE_PROTECT)
sbm_event |= SBM_WP;
if (event_mask & CS_EVENT_BATTERY_DEAD)
sbm_event |= SBM_BVD1;
if (event_mask & CS_EVENT_BATTERY_LOW)
sbm_event |= SBM_BVD2;
if (event_mask & CS_EVENT_CARD_READY)
sbm_event |= SBM_RDYBSY;
if (event_mask & CS_EVENT_CARD_LOCK)
sbm_event |= SBM_LOCKED;
if (event_mask & CS_EVENT_EJECTION_REQUEST)
sbm_event |= SBM_EJECT;
if (event_mask & CS_EVENT_INSERTION_REQUEST)
sbm_event |= SBM_INSERT;
if (event_mask & (CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL))
sbm_event |= SBM_CD;
return (sbm_event);
}
static uint32_t
cs_sbm2cse(uint32_t state)
{
uint32_t rstate = 0;
if (state & SBM_WP)
rstate |= CS_EVENT_WRITE_PROTECT;
if (state & SBM_BVD1)
rstate |= CS_EVENT_BATTERY_DEAD;
if (state & SBM_BVD2)
rstate |= CS_EVENT_BATTERY_LOW;
if (state & SBM_RDYBSY)
rstate |= CS_EVENT_CARD_READY;
if (state & SBM_LOCKED)
rstate |= CS_EVENT_CARD_LOCK;
if (state & SBM_EJECT)
rstate |= CS_EVENT_EJECTION_REQUEST;
if (state & SBM_INSERT)
rstate |= CS_EVENT_INSERTION_REQUEST;
if (state & SBM_CD)
rstate |= CS_EVENT_CARD_INSERTION;
return (rstate);
}
static unsigned
cs_merge_event_masks(cs_socket_t *sp, client_t *client)
{
unsigned SCIntMask;
uint32_t event_mask;
SCIntMask = SBM_CD;
event_mask = client->event_mask | client->global_mask |
sp->event_mask;
if (!(sp->flags & SOCKET_IS_IO)) {
SCIntMask |= cs_cse2sbm(event_mask);
} else {
if (client->present & CONFIG_PINREPL_REG_PRESENT) {
SCIntMask |= (cs_cse2sbm(event_mask) &
~(SBM_WP | SBM_BVD1 | SBM_BVD2 | SBM_RDYBSY));
if ((client->pin & (PRR_WP_STATUS | PRR_WP_EVENT)) ==
(PRR_WP_STATUS | PRR_WP_EVENT))
if (event_mask & CS_EVENT_WRITE_PROTECT)
SCIntMask |= SBM_BVD1;
if ((client->pin & (PRR_READY_STATUS | PRR_READY_EVENT)) ==
(PRR_READY_STATUS | PRR_READY_EVENT))
if (event_mask & CS_EVENT_CARD_READY)
SCIntMask |= SBM_BVD1;
if ((client->pin & (PRR_BVD2_STATUS | PRR_BVD2_EVENT)) ==
(PRR_BVD2_STATUS | PRR_BVD2_EVENT))
if (event_mask & CS_EVENT_BATTERY_LOW)
SCIntMask |= SBM_BVD1;
if ((client->pin & (PRR_BVD1_STATUS | PRR_BVD1_EVENT)) ==
(PRR_BVD1_STATUS | PRR_BVD1_EVENT))
if (event_mask & CS_EVENT_BATTERY_DEAD)
SCIntMask |= SBM_BVD1;
}
}
return (SCIntMask);
}
static int
cs_set_socket_event_mask(cs_socket_t *sp, unsigned event_mask)
{
get_socket_t get_socket;
set_socket_t set_socket;
get_socket.socket = sp->socket_num;
if (SocketServices(SS_GetSocket, &get_socket) != SUCCESS)
return (CS_BAD_SOCKET);
set_socket.socket = sp->socket_num;
set_socket.SCIntMask = event_mask;
set_socket.VccLevel = get_socket.VccLevel;
set_socket.Vpp1Level = get_socket.Vpp1Level;
set_socket.Vpp2Level = get_socket.Vpp2Level;
set_socket.IREQRouting = get_socket.IRQRouting;
set_socket.IFType = get_socket.IFType;
set_socket.CtlInd = get_socket.CtlInd;
set_socket.State = (unsigned)~0;
if (SocketServices(SS_SetSocket, &set_socket) != SUCCESS)
return (CS_BAD_SOCKET);
return (CS_SUCCESS);
}
static int
cs_deregister_mtd(client_handle_t client_handle)
{
cmn_err(CE_CONT, "cs_deregister_mtd: client_handle 0x%x\n",
(int)client_handle);
return (CS_SUCCESS);
}
static int
cs_request_window(client_handle_t client_handle,
window_handle_t *wh,
win_req_t *rw)
{
cs_socket_t *sp;
cs_window_t *cw;
client_t *client;
modify_win_t mw;
inquire_window_t iw;
uint32_t aw;
int error;
int client_lock_acquired;
uint32_t socket_num;
if (CLIENT_HANDLE_IS_SS(client_handle))
return (CS_UNSUPPORTED_FUNCTION);
if (rw->Attributes & (
WIN_PAGED |
WIN_SHARED |
WIN_FIRST_SHARED |
WIN_BINDING_SPECIFIC |
WIN_DATA_WIDTH_VALID |
WIN_MEMORY_TYPE_IO |
WIN_DATA_WIDTH_32 |
WIN_PREFETCH_CACHE_MASK |
WIN_BAR_MASK))
return (CS_BAD_ATTRIBUTE);
mutex_enter(&cs_globals.window_lock);
if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL)
return (CS_BAD_SOCKET);
EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp);
if (!(client = cs_find_client(client_handle, &error))) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (error);
}
mutex_enter(&sp->lock);
if (!(client->flags & CLIENT_CARD_INSERTED)) {
mutex_exit(&sp->lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (CS_NO_CARD);
}
mutex_exit(&sp->lock);
socket_num = CS_MAKE_SOCKET_NUMBER(GET_CLIENT_SOCKET(client_handle),
GET_CLIENT_FUNCTION(client_handle));
if ((error = cs_find_mem_window(sp->socket_num, rw, &aw)) !=
CS_SUCCESS) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (error);
}
*wh = cs_create_window_handle(aw);
if ((cw = cs_get_wp(aw)) == NULL) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (CS_BAD_WINDOW);
}
cw->window_handle = *wh;
cw->client_handle = client_handle;
cw->socket_num = sp->socket_num;
cw->state |= (CW_ALLOCATED | CW_MEM);
mw.Attributes = (
rw->Attributes |
WIN_DATA_WIDTH_VALID |
WIN_ACCESS_SPEED_VALID);
mw.AccessSpeed = rw->win_params.AccessSpeed;
if ((error = cs_modify_mem_window(*wh, &mw, rw, socket_num)) !=
CS_SUCCESS) {
cw->state = 0;
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (error);
}
iw.window = GET_WINDOW_NUMBER(*wh);
SocketServices(SS_InquireWindow, &iw);
if (iw.mem_win_char.MemWndCaps & WC_CALIGN)
rw->ReqOffset = rw->Size;
else
rw->ReqOffset = iw.mem_win_char.ReqOffset;
client->memwin_count++;
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (CS_SUCCESS);
}
static int
cs_release_window(window_handle_t wh)
{
cs_socket_t *sp;
cs_window_t *cw;
client_t *client;
int error;
int client_lock_acquired;
mutex_enter(&cs_globals.window_lock);
if (!(cw = cs_find_window(wh))) {
mutex_exit(&cs_globals.window_lock);
return (CS_BAD_HANDLE);
}
if (CLIENT_HANDLE_IS_SS(cw->client_handle)) {
mutex_exit(&cs_globals.window_lock);
return (CS_UNSUPPORTED_FUNCTION);
}
if ((sp = cs_get_sp(GET_CLIENT_SOCKET(cw->client_handle))) == NULL)
return (CS_BAD_SOCKET);
EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp);
if (!(client = cs_find_client(cw->client_handle, &error))) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (error);
}
cw->state &= ~CW_WIN_IN_USE;
if (!(--(client->memwin_count)))
client->flags &= ~CLIENT_WIN_ALLOCATED;
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (CS_SUCCESS);
}
static int
cs_modify_window(window_handle_t wh, modify_win_t *mw)
{
cs_socket_t *sp;
cs_window_t *cw;
client_t *client;
int error;
int client_lock_acquired;
mutex_enter(&cs_globals.window_lock);
if (!(cw = cs_find_window(wh))) {
mutex_exit(&cs_globals.window_lock);
return (CS_BAD_HANDLE);
}
if ((sp = cs_get_sp(GET_CLIENT_SOCKET(cw->client_handle))) == NULL)
return (CS_BAD_SOCKET);
EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp);
if (!(client = cs_find_client(cw->client_handle, &error))) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (error);
}
mutex_enter(&sp->lock);
if (!(client->flags & CLIENT_CARD_INSERTED)) {
mutex_exit(&sp->lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (CS_NO_CARD);
}
mutex_exit(&sp->lock);
mw->Attributes &= (
WIN_MEMORY_TYPE_MASK |
WIN_ENABLE |
WIN_ACCESS_SPEED_VALID |
WIN_ACC_ENDIAN_MASK |
WIN_ACC_ORDER_MASK);
mw->Attributes &= ~WIN_DATA_WIDTH_VALID;
if ((error = cs_modify_mem_window(wh, mw, NULL, 0)) != CS_SUCCESS) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (error);
}
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (CS_SUCCESS);
}
static int
cs_modify_mem_window(window_handle_t wh, modify_win_t *mw,
win_req_t *wr, int sn)
{
get_window_t gw;
set_window_t sw;
set_page_t set_page;
get_page_t get_page;
if (!wr) {
gw.window = GET_WINDOW_NUMBER(wh);
if (SocketServices(SS_GetWindow, &gw) != SUCCESS)
return (CS_BAD_WINDOW);
sw.state = gw.state;
sw.socket = gw.socket;
sw.WindowSize = gw.size;
} else {
sw.state = 0;
sw.socket = sn;
sw.WindowSize = wr->Size;
}
if (mw->Attributes & WIN_ACCESS_SPEED_VALID) {
convert_speed_t convert_speed;
convert_speed.Attributes = CONVERT_DEVSPEED_TO_NS;
convert_speed.devspeed = mw->AccessSpeed;
if (cs_convert_speed(&convert_speed) != CS_SUCCESS)
return (CS_BAD_SPEED);
sw.speed = convert_speed.nS;
} else {
sw.speed = gw.speed;
}
if (!wr) {
get_page.window = GET_WINDOW_NUMBER(wh);
get_page.page = 0;
if (SocketServices(SS_GetPage, &get_page) != SUCCESS)
return (CS_BAD_WINDOW);
set_page.state = get_page.state;
set_page.offset = get_page.offset;
} else {
set_page.state = 0;
set_page.offset = 0;
}
if (mw->Attributes & WIN_ENABLE) {
sw.state |= WS_ENABLED;
set_page.state |= PS_ENABLED;
} else {
sw.state &= ~WS_ENABLED;
set_page.state &= ~PS_ENABLED;
}
if (mw->Attributes & WIN_DATA_WIDTH_VALID) {
if (mw->Attributes & WIN_DATA_WIDTH_16)
sw.state |= WS_16BIT;
else
sw.state &= ~WS_16BIT;
}
sw.window = GET_WINDOW_NUMBER(wh);
sw.base = 0;
cs_set_acc_attributes(&sw, mw->Attributes);
if (SocketServices(SS_SetWindow, &sw) != SUCCESS)
return (CS_BAD_WINDOW);
if (mw->Attributes & WIN_MEMORY_TYPE_AM)
set_page.state |= PS_ATTRIBUTE;
else
set_page.state &= ~PS_ATTRIBUTE;
set_page.window = GET_WINDOW_NUMBER(wh);
set_page.page = 0;
if (SocketServices(SS_SetPage, &set_page) != SUCCESS)
return (CS_BAD_OFFSET);
if (wr) {
gw.window = GET_WINDOW_NUMBER(wh);
if (SocketServices(SS_GetWindow, &gw) != SUCCESS)
return (CS_BAD_WINDOW);
wr->Base.handle = (acc_handle_t)gw.handle;
}
return (CS_SUCCESS);
}
static int
cs_map_mem_page(window_handle_t wh, map_mem_page_t *mmp)
{
cs_socket_t *sp;
cs_window_t *cw;
client_t *client;
inquire_window_t iw;
get_window_t gw;
set_page_t set_page;
get_page_t get_page;
int error;
uint32_t size;
int client_lock_acquired;
if (mmp->Page)
return (CS_BAD_PAGE);
mutex_enter(&cs_globals.window_lock);
if (!(cw = cs_find_window(wh))) {
mutex_exit(&cs_globals.window_lock);
return (CS_BAD_HANDLE);
}
if ((sp = cs_get_sp(GET_CLIENT_SOCKET(cw->client_handle))) == NULL)
return (CS_BAD_SOCKET);
EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp);
if (!(client = cs_find_client(cw->client_handle, &error))) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (error);
}
mutex_enter(&sp->lock);
if (!(client->flags & CLIENT_CARD_INSERTED)) {
mutex_exit(&sp->lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (CS_NO_CARD);
}
mutex_exit(&sp->lock);
gw.window = GET_WINDOW_NUMBER(wh);
SocketServices(SS_GetWindow, &gw);
iw.window = GET_WINDOW_NUMBER(wh);
SocketServices(SS_InquireWindow, &iw);
if (iw.mem_win_char.MemWndCaps & WC_CALIGN)
size = gw.size;
else
size = iw.mem_win_char.ReqOffset;
if (((mmp->CardOffset/size)*size) != mmp->CardOffset) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (CS_BAD_OFFSET);
}
get_page.window = GET_WINDOW_NUMBER(wh);
get_page.page = 0;
SocketServices(SS_GetPage, &get_page);
set_page.window = GET_WINDOW_NUMBER(wh);
set_page.page = 0;
set_page.state = get_page.state;
set_page.offset = mmp->CardOffset;
if (SocketServices(SS_SetPage, &set_page) != SUCCESS) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (CS_BAD_OFFSET);
}
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (CS_SUCCESS);
}
cs_window_t *
cs_find_window(window_handle_t wh)
{
cs_window_t *cw;
if ((GET_WINDOW_NUMBER(wh) > cs_globals.num_windows) ||
(GET_WINDOW_MAGIC(wh) != WINDOW_HANDLE_MAGIC))
return ((cs_window_t *)NULL);
if ((cw = cs_get_wp(GET_WINDOW_NUMBER(wh))) == NULL)
return (NULL);
if ((cw->state & CW_ALLOCATED) && (cw->state & CW_MEM))
return (cw);
return ((cs_window_t *)NULL);
}
static window_handle_t
cs_create_window_handle(uint32_t aw)
{
return (WINDOW_HANDLE_MAGIC | (aw & WINDOW_HANDLE_MASK));
}
static int
cs_find_mem_window(uint32_t sn, win_req_t *rw, uint32_t *assigned_window)
{
uint32_t wn;
int error = CS_OUT_OF_RESOURCE;
uint32_t window_num = PCMCIA_MAX_WINDOWS;
uint32_t min_size = UINT_MAX;
inquire_window_t inquire_window, *iw;
uint32_t MinSize, MaxSize, ReqGran, MemWndCaps, WndCaps;
uint32_t tws;
iw = &inquire_window;
for (wn = 0; wn < cs_globals.num_windows; wn++) {
cs_window_t *cw;
if ((cw = cs_get_wp(wn)) != NULL) {
iw->window = wn;
if (SocketServices(SS_InquireWindow, iw) != SUCCESS)
return (CS_BAD_WINDOW);
MinSize = iw->mem_win_char.MinSize;
MaxSize = iw->mem_win_char.MaxSize;
ReqGran = iw->mem_win_char.ReqGran;
MemWndCaps = iw->mem_win_char.MemWndCaps;
WndCaps = iw->WndCaps;
if (WINDOW_FOR_SOCKET(iw->Sockets, sn) &&
WINDOW_AVAILABLE_FOR_MEM(cw) &&
WndCaps & (WC_COMMON|WC_ATTRIBUTE)) {
if ((error = cs_valid_window_speed(iw, rw->win_params.AccessSpeed)) ==
CS_SUCCESS) {
error = CS_OUT_OF_RESOURCE;
if (cs_memwin_space_and_map_ok(iw, rw)) {
error = CS_BAD_SIZE;
if (!rw->Size) {
min_size = min(min_size, MinSize);
window_num = wn;
goto found_window;
} else {
if (!(MemWndCaps & WC_SIZE)) {
if (rw->Size == MinSize) {
min_size = MinSize;
window_num = wn;
goto found_window;
}
} else {
if (!ReqGran) {
error = CS_BAD_WINDOW;
} else {
if ((rw->Size >= MinSize) &&
(rw->Size <= MaxSize)) {
if (MemWndCaps & WC_POW2) {
unsigned rg = ReqGran;
for (tws = MinSize; tws <= MaxSize;
rg = (rg<<1)) {
if (rw->Size == tws) {
min_size = tws;
window_num = wn;
goto found_window;
}
tws += rg;
}
} else {
for (tws = MinSize; tws <= MaxSize;
tws += ReqGran) {
if (rw->Size == tws) {
min_size = tws;
window_num = wn;
goto found_window;
}
}
}
}
}
}
}
}
}
}
}
}
if (window_num == PCMCIA_MAX_WINDOWS) {
if (error == CS_BAD_TYPE)
error = CS_OUT_OF_RESOURCE;
return (error);
}
found_window:
rw->Size = min_size;
*assigned_window = window_num;
iw->window = window_num;
SocketServices(SS_InquireWindow, iw);
MemWndCaps = iw->mem_win_char.MemWndCaps;
if (MemWndCaps & WC_CALIGN)
rw->Attributes |= WIN_OFFSET_SIZE;
else
rw->Attributes &= ~WIN_OFFSET_SIZE;
return (CS_SUCCESS);
}
static int
cs_memwin_space_and_map_ok(inquire_window_t *iw, win_req_t *rw)
{
#ifdef CS_DEBUG
if (cs_debug > 240)
printf("-> s&m_ok: Attributes 0x%x AccessSpeed 0x%x "
"WndCaps 0x%x MemWndCaps 0x%x\n",
(int)rw->Attributes,
(int)rw->win_params.AccessSpeed,
iw->WndCaps,
iw->mem_win_char.MemWndCaps);
#endif
if (rw->win_params.AccessSpeed & WIN_USE_WAIT) {
if (!(iw->WndCaps & WC_WAIT))
return (0);
}
if (rw->Attributes & WIN_DATA_WIDTH_16) {
if (!(iw->mem_win_char.MemWndCaps & WC_16BIT))
return (0);
} else {
if (!(iw->mem_win_char.MemWndCaps & WC_8BIT))
return (0);
}
if (rw->Attributes & WIN_MEMORY_TYPE_AM) {
if (!(iw->WndCaps & WC_ATTRIBUTE))
return (0);
}
if (rw->Attributes & WIN_MEMORY_TYPE_CM) {
if (!(iw->WndCaps & WC_COMMON))
return (0);
}
return (1);
}
static int
cs_valid_window_speed(inquire_window_t *iw, uint32_t AccessSpeed)
{
convert_speed_t convert_speed, *cs;
cs = &convert_speed;
cs->Attributes = CONVERT_DEVSPEED_TO_NS;
cs->devspeed = AccessSpeed;
if (cs_convert_speed(cs) != CS_SUCCESS)
return (CS_BAD_SPEED);
if ((cs->nS < iw->mem_win_char.Fastest) ||
(cs->nS > iw->mem_win_char.Slowest))
return (CS_BAD_TYPE);
return (CS_SUCCESS);
}
static int
cs_request_io(client_handle_t client_handle, io_req_t *ior)
{
cs_socket_t *sp;
client_t *client;
int error;
int client_lock_acquired;
uint32_t socket_num;
if (CLIENT_HANDLE_IS_SS(client_handle))
return (CS_UNSUPPORTED_FUNCTION);
if (!ior->NumPorts2)
ior->Attributes2 = 0;
if ((ior->Attributes1 | ior->Attributes2) & (IO_SHARED |
IO_FIRST_SHARED |
IO_FORCE_ALIAS_ACCESS |
IO_DEALLOCATE_WINDOW |
IO_DISABLE_WINDOW))
return (CS_BAD_ATTRIBUTE);
if (!ior->NumPorts1)
return (CS_BAD_BASE);
if ((ior->NumPorts2) && !(ior->BasePort1.base && ior->BasePort2.base))
return (CS_BAD_BASE);
mutex_enter(&cs_globals.window_lock);
if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL)
return (CS_BAD_SOCKET);
EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp);
if (!(client = cs_find_client(client_handle, &error))) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (error);
}
if (client->flags & REQ_CONFIGURATION_DONE) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (CS_CONFIGURATION_LOCKED);
}
if (client->flags & REQ_IO_DONE) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (CS_IN_USE);
}
mutex_enter(&sp->lock);
if (!(client->flags & CLIENT_CARD_INSERTED)) {
mutex_exit(&sp->lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (CS_NO_CARD);
}
mutex_exit(&sp->lock);
if (!ior->NumPorts2)
ior->BasePort2.base = 0;
if (ior->IOAddrLines) {
ior->BasePort1.base = IOADDR_FROBNITZ(ior->BasePort1.base,
ior->IOAddrLines);
ior->BasePort2.base = IOADDR_FROBNITZ(ior->BasePort2.base,
ior->IOAddrLines);
} else {
ior->BasePort1.base = ior->BasePort1.base &
((IONUMPORTS_FROBNITZ(ior->NumPorts1) +
IONUMPORTS_FROBNITZ(ior->NumPorts2)) - 1);
ior->BasePort2.base = ior->BasePort2.base &
((IONUMPORTS_FROBNITZ(ior->NumPorts1) +
IONUMPORTS_FROBNITZ(ior->NumPorts2)) - 1);
}
socket_num = CS_MAKE_SOCKET_NUMBER(GET_CLIENT_SOCKET(client_handle),
GET_CLIENT_FUNCTION(client_handle));
#ifdef USE_IOMMAP_WINDOW
if (sp->io_mmap_window) {
cs_window_t *cw;
io_mmap_window_t *imw = sp->io_mmap_window;
uint32_t offset;
if (!imw->count) {
set_window_t set_window;
if (!WINDOW_AVAILABLE_FOR_IO(imw->number)) {
iowin_char_t iowin_char;
iowin_char.IOWndCaps = (WC_IO_RANGE_PER_WINDOW |
WC_8BIT |
WC_16BIT);
if ((error = cs_find_io_win(sp->socket_num, &iowin_char,
&imw->number, &imw->size)) != CS_SUCCESS) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
}
}
set_window.socket = socket_num;
set_window.window = imw->number;
set_window.speed = IO_WIN_SPEED;
set_window.base.base = 0;
set_window.WindowSize = imw->size;
set_window.state = (WS_ENABLED | WS_16BIT |
WS_EXACT_MAPIN | WS_IO);
cs_set_acc_attributes(&set_window, Attributes);
if (SocketServices(SS_SetWindow, &set_window) != SUCCESS) {
(void) cs_setup_io_win(socket_num, imw->number,
NULL, NULL, NULL,
(IO_DEALLOCATE_WINDOW |
IO_DISABLE_WINDOW));
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (CS_BAD_WINDOW);
}
imw->handle = set_window.base.handle;
imw->size = set_window.WindowSize;
if ((ior->BasePort1.base + ior->NumPorts1 +
ior->BasePort2.base + ior->NumPorts2) > imw->size) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (CS_BAD_BASE);
}
if ((cw = cs_get_wp(imw->number)) == NULL) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (CS_BAD_WINDOW)
}
cw->state |= (CW_ALLOCATED | CW_IO);
}
imw->count++;
csx_DupHandle(imw->handle, &ior->BasePort1.handle, 0);
csx_GetHandleOffset(ior->BasePort1.handle, &offset);
csx_SetHandleOffset(ior->BasePort1.handle,
ior->BasePort1.base + offset);
if (ior->NumPorts2) {
csx_DupHandle(imw->handle, &ior->BasePort2.handle, 0);
csx_GetHandleOffset(ior->BasePort2.handle, &offset);
csx_SetHandleOffset(ior->BasePort2.handle,
ior->BasePort1.base + offset);
}
client->io_alloc.Window1 = imw->number;
client->io_alloc.Window2 = PCMCIA_MAX_WINDOWS;
} else {
#else
{
#endif
baseaddru_t baseaddru;
baseaddru.base = ior->BasePort1.base;
if ((error = cs_allocate_io_win(sp->socket_num, ior->Attributes1,
&client->io_alloc.Window1)) != CS_SUCCESS) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (error);
}
if ((error = cs_setup_io_win(socket_num,
client->io_alloc.Window1,
&baseaddru,
&ior->NumPorts1,
ior->IOAddrLines,
ior->Attributes1)) !=
CS_SUCCESS) {
(void) cs_setup_io_win(socket_num, client->io_alloc.Window1,
NULL, NULL, 0,
(
IO_DEALLOCATE_WINDOW |
IO_DISABLE_WINDOW));
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (error);
}
ior->BasePort1.handle = (acc_handle_t)baseaddru.handle;
ior->BasePort1.base = baseaddru.base;
if (ior->NumPorts2) {
baseaddru_t baseaddru;
baseaddru.base = ior->BasePort2.base;
if ((error = cs_allocate_io_win(sp->socket_num,
ior->Attributes2,
&client->io_alloc.Window2)) !=
CS_SUCCESS) {
(void) cs_setup_io_win(socket_num,
client->io_alloc.Window2,
NULL, NULL, 0,
(
IO_DEALLOCATE_WINDOW |
IO_DISABLE_WINDOW));
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (error);
}
if ((error = cs_setup_io_win(socket_num,
client->io_alloc.Window2,
&baseaddru,
&ior->NumPorts2,
ior->IOAddrLines,
ior->Attributes2)) !=
CS_SUCCESS) {
(void) cs_setup_io_win(socket_num,
client->io_alloc.Window1,
NULL, NULL, 0,
(
IO_DEALLOCATE_WINDOW |
IO_DISABLE_WINDOW));
(void) cs_setup_io_win(socket_num,
client->io_alloc.Window2,
NULL, NULL, 0,
(
IO_DEALLOCATE_WINDOW |
IO_DISABLE_WINDOW));
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (error);
}
ior->BasePort2.handle = (acc_handle_t)baseaddru.handle;
ior->BasePort2.base = baseaddru.base;
} else {
client->io_alloc.Window2 = PCMCIA_MAX_WINDOWS;
}
}
client->io_alloc.BasePort1.base = ior->BasePort1.base;
client->io_alloc.BasePort1.handle = ior->BasePort1.handle;
client->io_alloc.NumPorts1 = ior->NumPorts1;
client->io_alloc.Attributes1 = ior->Attributes1;
client->io_alloc.BasePort2.base = ior->BasePort2.base;
client->io_alloc.BasePort2.handle = ior->BasePort2.handle;
client->io_alloc.NumPorts2 = ior->NumPorts2;
client->io_alloc.Attributes2 = ior->Attributes2;
client->io_alloc.IOAddrLines = ior->IOAddrLines;
client->flags |= (REQ_IO_DONE | CLIENT_IO_ALLOCATED);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (CS_SUCCESS);
}
static int
cs_release_io(client_handle_t client_handle, io_req_t *ior)
{
cs_socket_t *sp;
client_t *client;
int error;
int client_lock_acquired;
uint32_t socket_num;
#ifdef lint
ior = NULL;
#endif
if (CLIENT_HANDLE_IS_SS(client_handle))
return (CS_UNSUPPORTED_FUNCTION);
mutex_enter(&cs_globals.window_lock);
if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL)
return (CS_BAD_SOCKET);
EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp);
if (!(client = cs_find_client(client_handle, &error))) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (error);
}
if (client->flags & REQ_CONFIGURATION_DONE) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (CS_CONFIGURATION_LOCKED);
}
if (!(client->flags & REQ_IO_DONE)) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (CS_IN_USE);
}
socket_num = CS_MAKE_SOCKET_NUMBER(GET_CLIENT_SOCKET(client_handle),
GET_CLIENT_FUNCTION(client_handle));
#ifdef XXX
if ((client->io_alloc.BasePort1 != ior->BasePort1) ||
(client->io_alloc.NumPorts1 != ior->NumPorts1) ||
(client->io_alloc.Attributes1 != ior->Attributes1) ||
(client->io_alloc.BasePort2 != ior->BasePort2) ||
(client->io_alloc.NumPorts2 != ior->NumPorts2) ||
(client->io_alloc.Attributes2 != ior->Attributes2) ||
(client->io_alloc.IOAddrLines != ior->IOAddrLines)) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (CS_BAD_ARGS);
}
#endif
#ifdef USE_IOMMAP_WINDOW
if (sp->io_mmap_window) {
io_mmap_window_t *imw = sp->io_mmap_window;
if (!imw->count) {
cmn_err(CE_CONT, "cs_release_io: socket %d !imw->count\n",
sp->socket_num);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (CS_GENERAL_FAILURE);
}
csx_FreeHandle(&ior->BasePort1.handle);
if (!--(imw->count)) {
(void) cs_setup_io_win(socket_num, imw->number, NULL,
NULL, NULL,
(
IO_DEALLOCATE_WINDOW |
IO_DISABLE_WINDOW));
}
} else {
#endif
(void) cs_setup_io_win(socket_num, client->io_alloc.Window1,
NULL, NULL, 0,
(
IO_DEALLOCATE_WINDOW |
IO_DISABLE_WINDOW));
if (client->io_alloc.Window2 != PCMCIA_MAX_WINDOWS)
(void) cs_setup_io_win(socket_num, client->io_alloc.Window2,
NULL, NULL, 0,
(
IO_DEALLOCATE_WINDOW |
IO_DISABLE_WINDOW));
#ifdef USE_IOMMAP_WINDOW
}
#endif
client->flags &= ~(REQ_IO_DONE | CLIENT_IO_ALLOCATED);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
mutex_exit(&cs_globals.window_lock);
return (CS_SUCCESS);
}
static int
cs_find_io_win(uint32_t sn, iowin_char_t *iwc, uint32_t *assigned_window,
uint32_t *size)
{
inquire_window_t inquire_window, *iw;
unsigned wn;
iw = &inquire_window;
for (wn = 0; wn < cs_globals.num_windows; wn++) {
iowin_char_t *iowc;
cs_window_t *cw;
if ((cw = cs_get_wp(wn)) != NULL) {
iw->window = wn;
SocketServices(SS_InquireWindow, iw);
iowc = &iw->iowin_char;
if (WINDOW_FOR_SOCKET(iw->Sockets, sn) &&
WINDOW_AVAILABLE_FOR_IO(cw) &&
(iw->WndCaps & WC_IO) &&
((iowc->IOWndCaps & iwc->IOWndCaps) == iwc->IOWndCaps)) {
*assigned_window = wn;
if (size)
*size = iw->iowin_char.ReqGran;
return (CS_SUCCESS);
}
}
}
return (CS_OUT_OF_RESOURCE);
}
static int
cs_allocate_io_win(uint32_t sn, uint32_t Attributes, uint32_t *assigned_window)
{
iowin_char_t iowin_char;
cs_window_t *cw;
iowin_char.IOWndCaps =
((Attributes & IO_DATA_PATH_WIDTH_16)?WC_16BIT:WC_8BIT);
if (cs_find_io_win(sn, &iowin_char, assigned_window, NULL) ==
CS_SUCCESS) {
if ((cw = cs_get_wp(*assigned_window)) == NULL)
return (CS_OUT_OF_RESOURCE);
cw->state = (cw->state & CW_WINDOW_VALID) | (CW_ALLOCATED | CW_IO);
return (CS_SUCCESS);
}
return (CS_OUT_OF_RESOURCE);
}
static int
cs_setup_io_win(uint32_t sn, uint32_t wn, baseaddru_t *Base, uint32_t *NumPorts,
uint32_t IOAddrLines, uint32_t Attributes)
{
set_window_t set_window;
if (Attributes & (IO_DEALLOCATE_WINDOW | IO_DISABLE_WINDOW)) {
if (Attributes & IO_DEALLOCATE_WINDOW) {
cs_window_t *cw;
if ((cw = cs_get_wp(wn)) == NULL)
return (CS_BAD_WINDOW);
cw->state &= CW_WINDOW_VALID;
}
if (Attributes & IO_DISABLE_WINDOW) {
get_window_t get_window;
get_window.window = wn;
SocketServices(SS_GetWindow, &get_window);
set_window.socket = get_window.socket;
set_window.window = get_window.window;
set_window.speed = get_window.speed;
set_window.base = 0;
set_window.WindowSize = get_window.size;
set_window.state = get_window.state & ~WS_ENABLED;
cs_set_acc_attributes(&set_window, Attributes);
SocketServices(SS_SetWindow, &set_window);
}
return (CS_SUCCESS);
}
if (!IOAddrLines)
Base->base = 0;
else
Base->base = IOADDR_FROBNITZ(Base->base, IOAddrLines);
set_window.socket = sn;
set_window.window = wn;
set_window.speed = IO_WIN_SPEED;
set_window.base = Base->base;
set_window.WindowSize = *NumPorts;
set_window.state = (WS_ENABLED | WS_IO |
((Attributes & IO_DATA_PATH_WIDTH_16)?WS_16BIT:0));
cs_set_acc_attributes(&set_window, Attributes);
if (SocketServices(SS_SetWindow, &set_window) != SUCCESS)
return (CS_BAD_WINDOW);
Base->base = set_window.base;
Base->handle = set_window.handle;
*NumPorts = set_window.WindowSize;
return (CS_SUCCESS);
}
static int
cs_request_irq(client_handle_t client_handle, irq_req_t *irqr)
{
cs_socket_t *sp;
client_t *client;
set_irq_handler_t set_irq_handler;
int error;
int client_lock_acquired;
if (CLIENT_HANDLE_IS_SS(client_handle))
return (CS_UNSUPPORTED_FUNCTION);
if ((irqr->Attributes & (IRQ_TYPE_TIME | IRQ_TYPE_DYNAMIC_SHARING |
IRQ_FIRST_SHARED | IRQ_PULSE_ALLOCATED |
IRQ_FORCED_PULSE)) ||
!(irqr->Attributes & IRQ_TYPE_EXCLUSIVE))
return (CS_BAD_ATTRIBUTE);
if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL)
return (CS_BAD_SOCKET);
EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp);
if (!(client = cs_find_client(client_handle, &error))) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (error);
}
if (client->flags & REQ_CONFIGURATION_DONE) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_CONFIGURATION_LOCKED);
}
if (client->flags & REQ_IRQ_DONE) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_IN_USE);
}
if (!(client->flags & CLIENT_CARD_INSERTED)) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_NO_CARD);
}
set_irq_handler.socket =
CS_MAKE_SOCKET_NUMBER(GET_CLIENT_SOCKET(client_handle),
GET_CLIENT_FUNCTION(client_handle));
set_irq_handler.irq = IRQ_ANY;
set_irq_handler.handler_id = client_handle;
set_irq_handler.handler = (f_t *)irqr->irq_handler;
set_irq_handler.arg1 = irqr->irq_handler_arg;
set_irq_handler.arg2 = NULL;
if ((error = SocketServices(SS_SetIRQHandler,
&set_irq_handler)) != SUCCESS) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BAD_IRQ);
}
irqr->iblk_cookie = set_irq_handler.iblk_cookie;
irqr->idev_cookie = set_irq_handler.idev_cookie;
client->irq_alloc.Attributes = irqr->Attributes;
client->irq_alloc.irq = set_irq_handler.irq;
client->irq_alloc.handler_id = set_irq_handler.handler_id;
client->irq_alloc.irq_handler = (f_t *)set_irq_handler.handler;
client->irq_alloc.irq_handler_arg1 = set_irq_handler.arg1;
client->irq_alloc.irq_handler_arg2 = set_irq_handler.arg2;
#ifdef CS_DEBUG
if (cs_debug > 0)
cmn_err(CE_CONT, "cs_request_irq: socket %d irqr->Attributes 0x%x "
"set_irq_handler.irq 0x%x\n",
sp->socket_num,
(int)irqr->Attributes,
set_irq_handler.irq);
#endif
client->flags |= (REQ_IRQ_DONE | CLIENT_IRQ_ALLOCATED);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_SUCCESS);
}
static int
cs_release_irq(client_handle_t client_handle, irq_req_t *irqr)
{
cs_socket_t *sp;
client_t *client;
clear_irq_handler_t clear_irq_handler;
int error;
int client_lock_acquired;
#ifdef lint
irqr = NULL;
#endif
if (CLIENT_HANDLE_IS_SS(client_handle))
return (CS_UNSUPPORTED_FUNCTION);
if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL)
return (CS_BAD_SOCKET);
EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp);
if (!(client = cs_find_client(client_handle, &error))) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (error);
}
if (client->flags & REQ_CONFIGURATION_DONE) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_CONFIGURATION_LOCKED);
}
if (!(client->flags & REQ_IRQ_DONE)) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_IN_USE);
}
clear_irq_handler.socket =
CS_MAKE_SOCKET_NUMBER(GET_CLIENT_SOCKET(client_handle),
GET_CLIENT_FUNCTION(client_handle));
clear_irq_handler.handler_id = client->irq_alloc.handler_id;
clear_irq_handler.handler = (f_t *)client->irq_alloc.irq_handler;
if ((error = SocketServices(SS_ClearIRQHandler, &clear_irq_handler)) !=
SUCCESS) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BAD_IRQ);
}
client->flags &= ~(REQ_IRQ_DONE | CLIENT_IRQ_ALLOCATED);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_SUCCESS);
}
static int
cs_request_configuration(client_handle_t client_handle, config_req_t *cr)
{
cs_socket_t *sp;
client_t *client;
volatile config_regs_t *crt;
set_socket_t set_socket;
get_socket_t get_socket;
acc_handle_t cis_handle;
int error;
uint32_t newoffset;
int client_lock_acquired;
if (CLIENT_HANDLE_IS_SS(client_handle))
return (CS_UNSUPPORTED_FUNCTION);
#ifdef XXX
if (!(cr->Vcc) && (cr->Vpp1 || cr->Vpp2))
return (CS_BAD_VCC);
#endif
if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL)
return (CS_BAD_SOCKET);
if (cr->IntType & SOCKET_INTERFACE_MEMORY_AND_IO) {
inquire_socket_t inquire_socket;
inquire_socket.socket = sp->socket_num;
if (SocketServices(SS_InquireSocket, &inquire_socket) != SUCCESS)
return (CS_BAD_SOCKET);
if (!(inquire_socket.SocketCaps & IF_IO))
return (CS_BAD_TYPE);
}
EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp);
if (!(client = cs_find_client(client_handle, &error))) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (error);
}
if (client->flags & REQ_CONFIGURATION_DONE) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_CONFIGURATION_LOCKED);
}
if (!(client->flags & CLIENT_CARD_INSERTED)) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_NO_CARD);
}
mutex_enter(&sp->cis_lock);
get_socket.socket = sp->socket_num;
if (SocketServices(SS_GetSocket, &get_socket) != SUCCESS) {
mutex_exit(&sp->cis_lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BAD_SOCKET);
}
#ifdef CS_DEBUG
if (cs_debug > 0)
cmn_err(CE_CONT, "cs_request_configuration: socket %d "
"client->irq_alloc.irq 0x%x "
"get_socket.IRQRouting 0x%x\n",
sp->socket_num,
(int)client->irq_alloc.irq,
get_socket.IRQRouting);
#endif
bzero(&set_socket, sizeof (set_socket));
set_socket.socket = sp->socket_num;
set_socket.IREQRouting = client->irq_alloc.irq & ~IRQ_ENABLE;
set_socket.CtlInd = get_socket.CtlInd;
set_socket.State = 0;
if (cs_convert_powerlevel(sp->socket_num, cr->Vcc, VCC,
&set_socket.VccLevel) != CS_SUCCESS) {
mutex_exit(&sp->cis_lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BAD_VCC);
}
if (cs_convert_powerlevel(sp->socket_num, cr->Vpp1, VPP1,
&set_socket.Vpp1Level) != CS_SUCCESS) {
mutex_exit(&sp->cis_lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BAD_VPP);
}
if (cs_convert_powerlevel(sp->socket_num, cr->Vpp2, VPP2,
&set_socket.Vpp2Level) != CS_SUCCESS) {
mutex_exit(&sp->cis_lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BAD_VPP);
}
if (!(cr->IntType & SOCKET_INTERFACE_MEMORY_AND_IO))
set_socket.IFType = IF_MEMORY;
else {
set_socket.IFType = IF_IO;
if (SocketServices(SS_SetSocket, &set_socket) != SUCCESS) {
sp->flags &= ~SOCKET_IS_IO;
mutex_exit(&sp->cis_lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BAD_SOCKET);
}
}
if (cs_rc2_delay)
drv_usecwait(cs_rc2_delay * 1000);
mutex_enter(&sp->lock);
client->config_regs_offset = cr->ConfigBase;
newoffset = client->config_regs_offset;
mutex_exit(&sp->lock);
if (cs_init_cis_window(sp, &newoffset, &cis_handle,
CISTPLF_AM_SPACE) != CS_SUCCESS) {
mutex_exit(&sp->cis_lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
cmn_err(CE_CONT, "cs_request_configuration: socket %d can't init "
"CIS window\n", sp->socket_num);
return (CS_GENERAL_FAILURE);
}
mutex_enter(&sp->lock);
crt = &client->config_regs;
client->present = cr->Present;
bzero((char *)crt, sizeof (config_regs_t));
if (client->present & CONFIG_OPTION_REG_PRESENT)
crt->cor_p = (newoffset + CONFIG_OPTION_REG_OFFSET);
if (client->present & CONFIG_STATUS_REG_PRESENT)
crt->ccsr_p = (newoffset + CONFIG_STATUS_REG_OFFSET);
if (client->present & CONFIG_PINREPL_REG_PRESENT)
crt->prr_p = (newoffset + CONFIG_PINREPL_REG_OFFSET);
if (client->present & CONFIG_COPY_REG_PRESENT)
crt->scr_p = (newoffset + CONFIG_COPY_REG_OFFSET);
if (client->present & CONFIG_EXSTAT_REG_PRESENT)
crt->exstat_p = (newoffset + CONFIG_EXSTAT_REG_OFFSET);
if (client->present & CONFIG_IOBASE0_REG_PRESENT)
crt->iobase0_p = (newoffset + CONFIG_IOBASE0_REG_OFFSET);
if (client->present & CONFIG_IOBASE1_REG_PRESENT)
crt->iobase1_p = (newoffset + CONFIG_IOBASE1_REG_OFFSET);
if (client->present & CONFIG_IOBASE2_REG_PRESENT)
crt->iobase2_p = (newoffset + CONFIG_IOBASE2_REG_OFFSET);
if (client->present & CONFIG_IOBASE3_REG_PRESENT)
crt->iobase3_p = (newoffset + CONFIG_IOBASE3_REG_OFFSET);
if (client->present & CONFIG_IOLIMIT_REG_PRESENT)
crt->iolimit_p = (newoffset + CONFIG_IOLIMIT_REG_OFFSET);
client->pin = cr->Pin;
#ifdef CS_DEBUG
if (cs_debug > 128)
cmn_err(CE_CONT, "cs_request_configuration: client->pin 0x%x "
"client->config_regs_offset 0x%x newoffset 0x%x cor_p 0x%x "
"ccsr_p 0x%x prr_p 0x%x scr_p 0x%x\n",
client->pin, (int)client->config_regs_offset, newoffset,
(int)crt->cor_p, (int)crt->ccsr_p, (int)crt->prr_p,
(int)crt->scr_p);
#endif
if ((client->present != 0) &&
(!(cr->IntType & SOCKET_INTERFACE_MEMORY_AND_IO)) &&
(get_socket.state & SBM_WP)) {
cmn_err(CE_NOTE, "!cs_request_configuration: attempting to "
"write CIS config regs with WP set\n");
}
if (client->present & CONFIG_COPY_REG_PRESENT) {
crt->scr = cr->Copy;
csx_Put8(cis_handle, crt->scr_p, crt->scr);
}
if (client->present & CONFIG_PINREPL_REG_PRESENT) {
crt->prr = cr->Pin;
csx_Put8(cis_handle, crt->prr_p, crt->prr);
}
if (client->present & CONFIG_STATUS_REG_PRESENT) {
crt->ccsr = cr->Status;
csx_Put8(cis_handle, crt->ccsr_p, crt->ccsr);
}
if (client->present & CONFIG_EXSTAT_REG_PRESENT) {
crt->exstat = cr->ExtendedStatus;
csx_Put8(cis_handle, crt->exstat_p, crt->exstat);
}
if (client->flags & REQ_IO_DONE) {
if (client->present & CONFIG_IOBASE0_REG_PRESENT) {
uint32_t base = client->io_alloc.BasePort1.base;
uint32_t present = (client->present &
CONFIG_IOBASE_REG_MASK) >>
CONFIG_IOBASE_REG_SHIFT;
uint32_t reg = crt->iobase0_p;
do {
csx_Put8(cis_handle, reg, base & 0x0ff);
reg = reg + 2;
base = base >> 8;
present = present >> 1;
} while (present);
}
if (client->present & CONFIG_IOLIMIT_REG_PRESENT) {
uint32_t np = client->io_alloc.NumPorts1 +
client->io_alloc.NumPorts2;
uint32_t limit, do_bit = 0;
int lm;
limit = (IONUMPORTS_FROBNITZ(np) - 1);
for (lm = 7; lm >= 0; lm--) {
if (limit & (1 << lm))
do_bit = 1;
if (do_bit)
limit |= (1 << lm);
}
csx_Put8(cis_handle, crt->iolimit_p, limit);
}
}
if (cr->IntType & SOCKET_INTERFACE_MEMORY_AND_IO)
sp->flags |= SOCKET_IS_IO;
mutex_exit(&sp->lock);
if (cr->Attributes & CONF_ENABLE_IRQ_STEERING)
set_socket.IREQRouting |= IRQ_ENABLE;
set_socket.SCIntMask = cs_merge_event_masks(sp, client);
if (client->present & CONFIG_OPTION_REG_PRESENT) {
crt->cor = (cr->ConfigIndex & ~COR_SOFT_RESET) | COR_LEVEL_IRQ;
if (client->present & CONFIG_IOBASE0_REG_PRESENT)
crt->cor |= COR_ENABLE_BASE_LIMIT;
if (sp->cis_flags & CW_MULTI_FUNCTION_CIS) {
crt->cor |= COR_ENABLE_FUNCTION;
crt->cor &= ~COR_ENABLE_IREQ_ROUTING;
if (cr->Attributes & CONF_ENABLE_IRQ_STEERING)
crt->cor |= COR_ENABLE_IREQ_ROUTING;
}
#ifdef CS_DEBUG
if (cs_debug > 0)
cmn_err(CE_CONT, "cs_request_configuration "
"cor=x%x ConfigIndex=x%x Attributes=x%x flags=x%x\n"
"present=x%x cis_handle=%p cor_p=x%x\n",
crt->cor, cr->ConfigIndex, cr->Attributes, sp->cis_flags,
client->present, cis_handle, crt->cor_p);
#endif
csx_Put8(cis_handle, crt->cor_p, crt->cor);
}
if (cs_rc1_delay)
drv_usecwait(cs_rc1_delay * 1000);
if (SocketServices(SS_SetSocket, &set_socket) != SUCCESS) {
if (client->present & CONFIG_OPTION_REG_PRESENT) {
crt->cor = 0;
csx_Put8(cis_handle, crt->cor_p, crt->cor);
}
sp->flags &= ~SOCKET_IS_IO;
mutex_exit(&sp->cis_lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BAD_SOCKET);
}
if (cs_rc2_delay)
drv_usecwait(cs_rc2_delay * 1000);
client->flags |= REQ_CONFIGURATION_DONE;
mutex_exit(&sp->cis_lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_SUCCESS);
}
static int
cs_release_configuration(client_handle_t client_handle, release_config_t *rcfg)
{
cs_socket_t *sp;
client_t *client;
volatile config_regs_t *crt;
set_socket_t set_socket;
get_socket_t get_socket;
acc_handle_t cis_handle;
int error;
uint32_t newoffset;
int client_lock_acquired;
if (CLIENT_HANDLE_IS_SS(client_handle))
return (CS_UNSUPPORTED_FUNCTION);
if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL)
return (CS_BAD_SOCKET);
EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp);
if (!(client = cs_find_client(client_handle, &error))) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (error);
}
if (!(client->flags & REQ_CONFIGURATION_DONE)) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BAD_HANDLE);
}
#ifdef CS_DEBUG
if (cs_debug > 0)
cmn_err(CE_CONT, "cs_release_configuration: "
"flags=0x%x CW_MULTI_FUNCTION_CIS =0x%x \n",
sp->cis_flags, CW_MULTI_FUNCTION_CIS);
#endif
mutex_enter(&sp->cis_lock);
crt = &client->config_regs;
newoffset = client->config_regs_offset;
if (cs_init_cis_window(sp, &newoffset, &cis_handle,
CISTPLF_AM_SPACE) != CS_SUCCESS) {
mutex_exit(&sp->cis_lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
cmn_err(CE_CONT, "cs_release_configuration: socket %d can't init "
"CIS window\n", sp->socket_num);
return (CS_GENERAL_FAILURE);
}
if (sp->cis_flags & CW_MULTI_FUNCTION_CIS) {
client->flags &= ~REQ_CONFIGURATION_DONE;
csx_Put8(cis_handle, crt->cor_p, 0);
mutex_exit(&sp->cis_lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_SUCCESS);
}
get_socket.socket = sp->socket_num;
if (SocketServices(SS_GetSocket, &get_socket) != SUCCESS) {
mutex_exit(&sp->cis_lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BAD_SOCKET);
}
mutex_enter(&sp->lock);
sp->flags &= ~SOCKET_IS_IO;
set_socket.SCIntMask = cs_merge_event_masks(sp, client);
mutex_exit(&sp->lock);
set_socket.socket = sp->socket_num;
set_socket.IREQRouting = 0;
set_socket.CtlInd = get_socket.CtlInd;
set_socket.State = 0;
set_socket.VccLevel = get_socket.VccLevel;
set_socket.Vpp1Level = get_socket.Vpp1Level;
set_socket.Vpp2Level = get_socket.Vpp2Level;
set_socket.IFType = IF_MEMORY;
if (client->present & CONFIG_OPTION_REG_PRESENT)
csx_Put8(cis_handle, crt->cor_p, 0);
if (SocketServices(SS_SetSocket, &set_socket) != SUCCESS) {
mutex_exit(&sp->cis_lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BAD_SOCKET);
}
client->flags &= ~REQ_CONFIGURATION_DONE;
mutex_exit(&sp->cis_lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_SUCCESS);
}
static int
cs_modify_configuration(client_handle_t client_handle, modify_config_t *mc)
{
cs_socket_t *sp;
client_t *client;
set_socket_t set_socket;
get_socket_t get_socket;
int error;
int client_lock_acquired;
if (CLIENT_HANDLE_IS_SS(client_handle))
return (CS_UNSUPPORTED_FUNCTION);
if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL)
return (CS_BAD_SOCKET);
EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp);
if (!(client = cs_find_client(client_handle, &error))) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (error);
}
if (!(client->flags & REQ_CONFIGURATION_DONE)) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BAD_HANDLE);
}
if (!(client->flags & CLIENT_CARD_INSERTED)) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_NO_CARD);
}
get_socket.socket = sp->socket_num;
if (SocketServices(SS_GetSocket, &get_socket) != SUCCESS) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BAD_SOCKET);
}
#ifdef CS_DEBUG
if (cs_debug > 0)
cmn_err(CE_CONT, "cs_modify_configuration: socket %d "
"client->irq_alloc.irq 0x%x "
"get_socket.IRQRouting 0x%x\n",
sp->socket_num, (int)client->irq_alloc.irq,
get_socket.IRQRouting);
#endif
set_socket.socket = sp->socket_num;
set_socket.SCIntMask = get_socket.SCIntMask;
set_socket.CtlInd = get_socket.CtlInd;
set_socket.State = 0;
set_socket.IFType = get_socket.IFType;
set_socket.IREQRouting = get_socket.IRQRouting;
if (mc->Attributes & CONF_IRQ_CHANGE_VALID) {
set_socket.IREQRouting &= ~IRQ_ENABLE;
if ((sp->cis_flags & CW_MULTI_FUNCTION_CIS) &&
(client->present & CONFIG_OPTION_REG_PRESENT)) {
config_regs_t *crt = &client->config_regs;
acc_handle_t cis_handle;
uint32_t newoffset = client->config_regs_offset;
if (cs_init_cis_window(sp, &newoffset, &cis_handle,
CISTPLF_AM_SPACE) != CS_SUCCESS) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
cmn_err(CE_CONT,
"cs_modify_configuration: socket %d can't init "
"CIS window\n", sp->socket_num);
return (CS_GENERAL_FAILURE);
}
crt->cor &= ~COR_ENABLE_IREQ_ROUTING;
if (mc->Attributes & CONF_ENABLE_IRQ_STEERING)
crt->cor |= COR_ENABLE_IREQ_ROUTING;
#ifdef CS_DEBUG
if (cs_debug > 0)
cmn_err(CE_CONT, "cs_modify_configuration:"
" cor_p=0x%x cor=0x%x\n",
crt->cor_p, crt->cor);
#endif
csx_Put8(cis_handle, crt->cor_p, crt->cor);
}
if (mc->Attributes & CONF_ENABLE_IRQ_STEERING)
set_socket.IREQRouting |= IRQ_ENABLE;
}
set_socket.VccLevel = get_socket.VccLevel;
if (mc->Attributes & CONF_VPP1_CHANGE_VALID) {
if (cs_convert_powerlevel(sp->socket_num, mc->Vpp1, VPP1,
&set_socket.Vpp1Level) != CS_SUCCESS) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BAD_VPP);
}
} else {
set_socket.Vpp1Level = get_socket.Vpp1Level;
}
if (mc->Attributes & CONF_VPP2_CHANGE_VALID) {
if (cs_convert_powerlevel(sp->socket_num, mc->Vpp2, VPP2,
&set_socket.Vpp2Level) != CS_SUCCESS) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BAD_VPP);
}
} else {
set_socket.Vpp2Level = get_socket.Vpp2Level;
}
if (SocketServices(SS_SetSocket, &set_socket) != SUCCESS) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BAD_SOCKET);
}
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_SUCCESS);
}
static int
cs_access_configuration_register(client_handle_t client_handle,
access_config_reg_t *acr)
{
cs_socket_t *sp;
client_t *client;
acc_handle_t cis_handle;
int error;
uint32_t newoffset;
int client_lock_acquired;
if (CLIENT_HANDLE_IS_SS(client_handle))
return (CS_UNSUPPORTED_FUNCTION);
if (acr->Offset > ((CISTPL_CONFIG_MAX_CONFIG_REGS * 2) - 2))
return (CS_BAD_ARGS);
if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL)
return (CS_BAD_SOCKET);
EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp);
if (!(client = cs_find_client(client_handle, &error))) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (error);
}
if (!(client->flags & CLIENT_CARD_INSERTED)) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_NO_CARD);
}
if (!(client->flags & REQ_CONFIGURATION_DONE)) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_UNSUPPORTED_MODE);
}
mutex_enter(&sp->cis_lock);
newoffset = client->config_regs_offset + acr->Offset;
if (cs_init_cis_window(sp, &newoffset, &cis_handle,
CISTPLF_AM_SPACE) != CS_SUCCESS) {
mutex_exit(&sp->cis_lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
cmn_err(CE_CONT, "cs_ACR: socket %d can't init CIS window\n",
sp->socket_num);
return (CS_GENERAL_FAILURE);
}
mutex_enter(&sp->lock);
#ifdef CS_DEBUG
if (cs_debug > 1) {
cmn_err(CE_CONT, "cs_ACR: config_regs_offset 0x%x "
"Offset 0x%x newoffset 0x%x\n",
(int)client->config_regs_offset,
(int)acr->Offset, newoffset);
}
#endif
error = CS_SUCCESS;
switch (acr->Action) {
case CONFIG_REG_READ:
acr->Value = csx_Get8(cis_handle, newoffset);
break;
case CONFIG_REG_WRITE:
csx_Put8(cis_handle, newoffset, acr->Value);
break;
default:
error = CS_BAD_ARGS;
break;
}
mutex_exit(&sp->lock);
mutex_exit(&sp->cis_lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (error);
}
static int
cs_reset_function(client_handle_t ch, reset_function_t *rf)
{
return (CS_IN_USE);
}
static int
cs_get_configuration_info(client_handle_t *chp, get_configuration_info_t *gci)
{
cs_socket_t *sp;
uint32_t fn;
client_t *client;
int client_lock_acquired;
if ((sp = cs_get_sp(CS_GET_SOCKET_NUMBER(gci->Socket))) == NULL)
return (CS_BAD_SOCKET);
EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp);
mutex_enter(&sp->lock);
fn = CS_GET_FUNCTION_NUMBER(gci->Socket);
client = sp->client_list;
while (client) {
if (GET_CLIENT_FUNCTION(client->client_handle) == fn) {
if (!(client->flags & CLIENT_CARD_INSERTED)) {
mutex_exit(&sp->lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_NO_CARD);
}
mutex_exit(&sp->lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_SUCCESS);
}
client = client->next;
}
mutex_exit(&sp->lock);
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BAD_SOCKET);
}
static int
cs_get_cardservices_info(client_handle_t ch, get_cardservices_info_t *gcsi)
{
gcsi->Signature[0] = 'C';
gcsi->Signature[1] = 'S';
gcsi->NumSockets = cs_globals.num_sockets;
gcsi->Revision = CS_INTERNAL_REVISION_LEVEL;
gcsi->CSLevel = CS_VERSION;
gcsi->FuncsPerSocket = CIS_MAX_FUNCTIONS;
(void) strncpy(gcsi->VendorString,
CS_GET_CARDSERVICES_INFO_VENDOR_STRING,
CS_GET_CARDSERVICES_INFO_MAX_VS_LEN);
return (CS_SUCCESS);
}
static int
cs_get_physical_adapter_info(client_handle_t ch,
get_physical_adapter_info_t *gpai)
{
cs_socket_t *sp;
int client_lock_acquired;
if (ch == 0)
gpai->PhySocket = CS_GET_SOCKET_NUMBER(gpai->LogSocket);
else
gpai->PhySocket = GET_CLIENT_SOCKET(ch);
if ((sp = cs_get_sp(CS_GET_SOCKET_NUMBER(gpai->PhySocket))) == NULL)
return ((ch == 0) ? CS_BAD_SOCKET : CS_BAD_HANDLE);
EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp);
if (ch != 0) {
if (cs_find_client(ch, NULL) == NULL) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BAD_HANDLE);
}
}
gpai->flags = sp->adapter.flags;
(void) strcpy(gpai->name, sp->adapter.name);
gpai->major = sp->adapter.major;
gpai->minor = sp->adapter.minor;
gpai->instance = sp->adapter.instance;
gpai->number = sp->adapter.number;
gpai->num_sockets = sp->adapter.num_sockets;
gpai->first_socket = sp->adapter.first_socket;
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_SUCCESS);
}
static int
cs_map_log_socket(client_handle_t ch, map_log_socket_t *mls)
{
cs_socket_t *sp;
int client_lock_acquired;
if (ch == 0)
mls->PhySocket = CS_GET_SOCKET_NUMBER(mls->LogSocket);
else
mls->PhySocket = GET_CLIENT_SOCKET(ch);
if ((sp = cs_get_sp(CS_GET_SOCKET_NUMBER(mls->PhySocket))) == NULL)
return ((ch == 0) ? CS_BAD_SOCKET : CS_BAD_HANDLE);
EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp);
if (ch != 0) {
if (cs_find_client(ch, NULL) == NULL) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BAD_HANDLE);
}
}
mls->PhyAdapter = sp->adapter.number;
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_SUCCESS);
}
static int
cs_convert_speed(convert_speed_t *cs)
{
return ((int)(uintptr_t)CIS_PARSER(CISP_CIS_CONV_DEVSPEED, cs));
}
static int
cs_convert_size(convert_size_t *cs)
{
return ((int)(uintptr_t)CIS_PARSER(CISP_CIS_CONV_DEVSIZE, cs));
}
static int
cs_convert_powerlevel(uint32_t sn, uint32_t volts, uint32_t flags, unsigned *pl)
{
inquire_adapter_t inquire_adapter;
int i;
#ifdef lint
if (sn == 0)
panic("lint panic");
#endif
*pl = 0;
if (SocketServices(SS_InquireAdapter, &inquire_adapter) != SUCCESS)
return (CS_BAD_ADAPTER);
for (i = 0; (i < inquire_adapter.NumPower); i++) {
if ((inquire_adapter.power_entry[i].ValidSignals & flags) &&
(inquire_adapter.power_entry[i].PowerLevel == volts)) {
*pl = i;
return (CS_SUCCESS);
}
}
return (CS_BAD_ARGS);
}
static int
cs_event2text(event2text_t *e2t, int event_source)
{
event_t event;
char *sepchar = "|";
if (!event_source) {
for (event = 0; event < MAX_SS_EVENTS; event++) {
if (cs_ss_event_text[event].ss_event == e2t->event) {
(void) strcpy(e2t->text, cs_ss_event_text[event].text);
return (CS_SUCCESS);
}
}
(void) strcpy(e2t->text, cs_ss_event_text[MAX_CS_EVENTS].text);
return (CS_SUCCESS);
} else {
e2t->text[0] = '\0';
for (event = 0; event < MAX_CS_EVENTS; event++) {
if (cs_ss_event_text[event].cs_event & e2t->event) {
(void) strcat(e2t->text, cs_ss_event_text[event].text);
(void) strcat(e2t->text, sepchar);
}
}
if (e2t->text[0])
e2t->text[strlen(e2t->text)-1] = '\0';
}
return (CS_SUCCESS);
}
static char *
cs_error2text(int function, int type)
{
cs_csfunc2text_strings_t *cfs;
int end_marker;
if (type == CSFUN2TEXT_FUNCTION) {
cfs = cs_csfunc2text_funcstrings;
end_marker = CSFuncListEnd;
} else {
cfs = cs_csfunc2text_returnstrings;
end_marker = CS_ERRORLIST_END;
}
while (cfs->item != end_marker) {
if (cfs->item == function)
return (cfs->text);
cfs++;
}
return (cfs->text);
}
static int
cs_make_device_node(client_handle_t client_handle, make_device_node_t *mdn)
{
cs_socket_t *sp;
client_t *client;
ss_make_device_node_t ss_make_device_node;
int error, i;
int client_lock_acquired;
if (CLIENT_HANDLE_IS_SS(client_handle))
return (CS_UNSUPPORTED_FUNCTION);
if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL)
return (CS_BAD_SOCKET);
EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp);
if (!(client = cs_find_client(client_handle, &error))) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (error);
}
#ifdef XXX
if (!(client->flags & CLIENT_CARD_INSERTED)) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_NO_CARD);
}
#endif
ss_make_device_node.dip = client->dip;
error = CS_BAD_ARGS;
switch (mdn->Action) {
case CREATE_DEVICE_NODE:
case REMOVE_DEVICE_NODE:
break;
case REMOVAL_ALL_DEVICE_NODES:
if (mdn->NumDevNodes) {
error = CS_BAD_ATTRIBUTE;
} else {
ss_make_device_node.flags = SS_CSINITDEV_REMOVE_DEVICE;
ss_make_device_node.name = NULL;
SocketServices(CSInitDev, &ss_make_device_node);
error = CS_SUCCESS;
}
default:
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (error);
}
for (i = 0; i < mdn->NumDevNodes; i++) {
devnode_desc_t *devnode_desc = &mdn->devnode_desc[i];
ss_make_device_node.name = devnode_desc->name;
ss_make_device_node.spec_type = devnode_desc->spec_type;
ss_make_device_node.minor_num = devnode_desc->minor_num;
ss_make_device_node.node_type = devnode_desc->node_type;
if (mdn->Action == CREATE_DEVICE_NODE) {
ss_make_device_node.flags = SS_CSINITDEV_CREATE_DEVICE;
} else {
ss_make_device_node.flags = SS_CSINITDEV_REMOVE_DEVICE;
}
if (i < (mdn->NumDevNodes - 1))
ss_make_device_node.flags |= SS_CSINITDEV_MORE_DEVICES;
if (SocketServices(CSInitDev, &ss_make_device_node) != SUCCESS) {
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_OUT_OF_RESOURCE);
}
}
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_SUCCESS);
}
static int
cs_remove_device_node(client_handle_t client_handle, remove_device_node_t *rdn)
{
return (cs_make_device_node(client_handle, (make_device_node_t *)rdn));
}
static int
cs_ddi_info(cs_ddi_info_t *cdi)
{
cs_socket_t *sp;
client_t *client;
int client_lock_acquired;
if (cdi->driver_name == NULL)
return (CS_BAD_ATTRIBUTE);
#ifdef CS_DEBUG
if (cs_debug > 0) {
cmn_err(CE_CONT, "cs_ddi_info: socket %d client [%s]\n",
(int)cdi->Socket, cdi->driver_name);
}
#endif
if (!CHECK_SOCKET_NUM(cdi->Socket, cs_globals.max_socket_num)) {
#ifdef CS_DEBUG
if (cs_debug > 0) {
cmn_err(CE_CONT, "cs_ddi_info: socket %d client [%s] "
"SOCKET IS OUT OF RANGE\n",
(int)cdi->Socket,
cdi->driver_name);
}
#endif
return (CS_BAD_SOCKET);
}
if ((sp = cs_get_sp(cdi->Socket)) == NULL)
return (CS_BAD_SOCKET);
EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp);
client = sp->client_list;
while (client) {
#ifdef CS_DEBUG
if (cs_debug > 0) {
cmn_err(CE_CONT, "cs_ddi_info: socket %d checking client [%s] "
"handle 0x%x\n",
(int)cdi->Socket,
client->driver_name,
(int)client->client_handle);
}
#endif
if (client->driver_name != NULL) {
if (!(strcmp(client->driver_name, cdi->driver_name))) {
cdi->dip = client->dip;
cdi->instance = client->instance;
#ifdef CS_DEBUG
if (cs_debug > 0) {
cmn_err(CE_CONT, "cs_ddi_info: found client [%s] "
"instance %d handle 0x%x\n",
client->driver_name, client->instance,
(int)client->client_handle);
}
#endif
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_SUCCESS);
}
}
client = client->next;
}
EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp);
return (CS_BAD_SOCKET);
}
static int
cs_sys_ctl(cs_sys_ctl_t *csc)
{
cs_socket_t *sp;
client_t *cp;
int sn, ret = CS_UNSUPPORTED_MODE;
switch (csc->Action) {
case CS_SYS_CTL_SEND_EVENT:
if (csc->Flags & CS_SYS_CTL_EVENT_SOCKET)
sn = CS_GET_SOCKET_NUMBER(csc->Socket);
else
sn = GET_CLIENT_SOCKET(csc->client_handle);
if ((sp = cs_get_sp(sn)) == NULL)
return (CS_BAD_SOCKET);
mutex_enter(&sp->client_lock);
mutex_enter(&sp->lock);
csc->Events &= CS_EVENT_CLIENT_EVENTS_MASK;
if (csc->Flags & CS_SYS_CTL_EVENT_SOCKET)
sp->events |= csc->Events;
if (csc->Flags & CS_SYS_CTL_EVENT_CLIENT) {
if ((cp = cs_find_client(csc->client_handle, &ret)) ==
NULL) {
mutex_exit(&sp->lock);
mutex_exit(&sp->client_lock);
return (ret);
}
cp->events |= (csc->Events &
(cp->event_mask | cp->global_mask));
}
if (csc->Flags & CS_SYS_CTL_WAIT_SYNC) {
sp->thread_state |= SOCKET_WAIT_SYNC;
mutex_exit(&sp->lock);
cv_broadcast(&sp->thread_cv);
cv_wait(&sp->caller_cv, &sp->client_lock);
} else {
mutex_exit(&sp->lock);
cv_broadcast(&sp->thread_cv);
}
mutex_exit(&sp->client_lock);
ret = CS_SUCCESS;
break;
default:
break;
}
return (ret);
}
static cs_socket_t *
cs_get_sp(uint32_t sn)
{
cs_socket_t *sp = cs_globals.sp;
if (!(cs_globals.init_state & GLOBAL_INIT_STATE_SS_READY))
return (NULL);
if ((sp = cs_find_sp(sn)) == NULL)
return (NULL);
if (sp->flags & SOCKET_IS_VALID)
return (sp);
return (NULL);
}
static cs_socket_t *
cs_find_sp(uint32_t sn)
{
cs_socket_t *sp = cs_globals.sp;
while (sp) {
if (sp->socket_num == CS_GET_SOCKET_NUMBER(sn))
return (sp);
sp = sp->next;
}
return (NULL);
}
static uint32_t
cs_add_socket(uint32_t sn)
{
cs_socket_t *sp;
sservice_t sservice;
get_cookies_and_dip_t *gcad;
win_req_t win_req;
convert_speed_t convert_speed;
set_socket_t set_socket;
cs_window_t *cw;
inquire_adapter_t inquire_adapter;
inquire_window_t inquire_window;
int ret, added_windows;
if (!(cs_globals.init_state & GLOBAL_INIT_STATE_SS_READY))
return (CS_BAD_SOCKET);
if ((sp = cs_find_sp(sn)) == NULL) {
cs_socket_t *spp = cs_globals.sp;
sp = (cs_socket_t *)kmem_zalloc(sizeof (cs_socket_t), KM_SLEEP);
if (cs_globals.sp == NULL)
cs_globals.sp = sp;
else
while (spp) {
if (spp->next == NULL) {
spp->next = sp;
break;
}
spp = spp->next;
}
} else {
if (sp->flags & SOCKET_IS_VALID)
return (CS_BAD_SOCKET);
}
sp->socket_num = sn;
SocketServices(SS_InquireAdapter, &inquire_adapter);
mutex_enter(&cs_globals.window_lock);
added_windows = inquire_adapter.NumWindows - cs_globals.num_windows;
if (added_windows > 0) {
if (cs_add_windows(added_windows,
cs_globals.num_windows) != CS_SUCCESS) {
mutex_exit(&cs_globals.window_lock);
return (CS_BAD_WINDOW);
}
cs_globals.num_windows = inquire_adapter.NumWindows;
}
sp->cis_win_num = PCMCIA_MAX_WINDOWS;
convert_speed.Attributes = CONVERT_NS_TO_DEVSPEED;
convert_speed.nS = CIS_DEFAULT_SPEED;
(void) cs_convert_speed(&convert_speed);
win_req.win_params.AccessSpeed = convert_speed.devspeed;
win_req.Attributes = (WIN_MEMORY_TYPE_AM | WIN_DATA_WIDTH_8);
win_req.Attributes = (WIN_MEMORY_TYPE_AM | WIN_MEMORY_TYPE_CM);
win_req.Base.base = 0;
win_req.Size = 0;
if ((ret = cs_find_mem_window(sp->socket_num, &win_req,
&sp->cis_win_num)) != CS_SUCCESS) {
mutex_exit(&cs_globals.window_lock);
sp->cis_win_num = PCMCIA_MAX_WINDOWS;
cmn_err(CE_CONT, "cs_add_socket: socket %d can't get CIS "
"window - error 0x%x\n",
sp->socket_num, ret);
return (CS_BAD_WINDOW);
}
if ((cw = cs_get_wp(sp->cis_win_num)) == NULL) {
mutex_exit(&cs_globals.window_lock);
return (CS_BAD_WINDOW);
}
inquire_window.window = sp->cis_win_num;
SocketServices(SS_InquireWindow, &inquire_window);
if (inquire_window.mem_win_char.MemWndCaps & WC_SIZE) {
sp->cis_win_size = win_req.Size;
} else {
sp->cis_win_size = PAGESIZE;
}
cw->state |= (CW_CIS | CW_ALLOCATED);
cw->socket_num = sp->socket_num;
mutex_exit(&cs_globals.window_lock);
#if defined(CS_DEBUG)
if (cs_debug > 1) {
cmn_err(CE_CONT, "cs_add_socket: socket %d using CIS window %d "
"size 0x%x\n", (int)sp->socket_num,
(int)sp->cis_win_num,
(int)sp->cis_win_size);
}
#endif
gcad = &sservice.get_cookies;
gcad->socket = sp->socket_num;
if (SocketServices(CSGetCookiesAndDip, &sservice) != SUCCESS) {
cmn_err(CE_CONT, "cs_add_socket: socket %d CSGetCookiesAndDip "
"failure\n", sp->socket_num);
return (CS_BAD_SOCKET);
}
sp->iblk = gcad->iblock;
sp->idev = gcad->idevice;
sp->adapter.flags = 0;
(void) strcpy(sp->adapter.name, gcad->adapter_info.name);
sp->adapter.major = gcad->adapter_info.major;
sp->adapter.minor = gcad->adapter_info.minor;
sp->adapter.instance = ddi_get_instance(gcad->dip);
sp->adapter.number = gcad->adapter_info.number;
sp->adapter.num_sockets = gcad->adapter_info.num_sockets;
sp->adapter.first_socket = gcad->adapter_info.first_socket;
mutex_init(&sp->lock, NULL, MUTEX_DRIVER, *(gcad->iblock));
mutex_init(&sp->client_lock, NULL, MUTEX_DRIVER, NULL);
mutex_init(&sp->cis_lock, NULL, MUTEX_DRIVER, NULL);
mutex_init(&sp->ss_thread_lock, NULL, MUTEX_DRIVER, NULL);
sp->init_state |= SOCKET_INIT_STATE_MUTEX;
cv_init(&sp->thread_cv, NULL, CV_DRIVER, NULL);
cv_init(&sp->caller_cv, NULL, CV_DRIVER, NULL);
cv_init(&sp->reset_cv, NULL, CV_DRIVER, NULL);
cv_init(&sp->ss_thread_cv, NULL, CV_DRIVER, NULL);
cv_init(&sp->ss_caller_cv, NULL, CV_DRIVER, NULL);
sp->init_state |= SOCKET_INIT_STATE_CV;
if (!(cs_globals.init_state & GLOBAL_INIT_STATE_SOFTINTR)) {
if (ddi_add_softintr(gcad->dip, DDI_SOFTINT_HIGH,
&sp->softint_id,
NULL, NULL,
cs_socket_event_softintr,
(caddr_t)NULL) != DDI_SUCCESS) {
cmn_err(CE_CONT, "cs_add_socket: socket %d can't add "
"softintr\n", sp->socket_num);
return (CS_BAD_SOCKET);
}
mutex_enter(&cs_globals.global_lock);
cs_globals.softint_id = sp->softint_id;
cs_globals.init_state |= GLOBAL_INIT_STATE_SOFTINTR;
cs_globals.sotfint_tmo = timeout(cs_event_softintr_timeout,
NULL, SOFTINT_TIMEOUT_TIME);
mutex_exit(&cs_globals.global_lock);
} else {
sp->softint_id = cs_globals.softint_id;
}
sp->init_state |= SOCKET_INIT_STATE_SOFTINTR;
sp->event_mask = CS_EVENT_CARD_INSERTION;
set_socket.socket = sp->socket_num;
set_socket.SCIntMask = SBM_CD;
set_socket.IREQRouting = 0;
set_socket.IFType = IF_MEMORY;
set_socket.CtlInd = 0;
set_socket.State = (unsigned)~0;
(void) cs_convert_powerlevel(sp->socket_num, 0, VCC,
&set_socket.VccLevel);
(void) cs_convert_powerlevel(sp->socket_num, 0, VPP1,
&set_socket.Vpp1Level);
(void) cs_convert_powerlevel(sp->socket_num, 0, VPP2,
&set_socket.Vpp2Level);
if ((ret = SocketServices(SS_SetSocket, &set_socket)) != SUCCESS) {
cmn_err(CE_CONT, "cs_add_socket: socket %d SS_SetSocket "
"failure %d\n", sp->socket_num, ret);
return (CS_BAD_SOCKET);
}
mutex_enter(&cs_globals.global_lock);
cs_globals.num_sockets++;
cs_globals.max_socket_num =
max(cs_globals.max_socket_num, sp->socket_num + 1);
mutex_exit(&cs_globals.global_lock);
sp->flags = SOCKET_IS_VALID;
sp->event_thread = CREATE_SOCKET_EVENT_THREAD(cs_event_thread,
(uintptr_t)sn);
mutex_enter(&sp->lock);
sp->init_state |= SOCKET_INIT_STATE_THREAD;
mutex_exit(&sp->lock);
sp->ss_thread = CREATE_SOCKET_EVENT_THREAD(cs_ss_thread,
(uintptr_t)sn);
mutex_enter(&sp->lock);
sp->init_state |= (SOCKET_INIT_STATE_SS_THREAD |
SOCKET_INIT_STATE_READY);
sp->event_mask = CS_EVENT_CARD_INSERTION;
mutex_exit(&sp->lock);
return (CS_SUCCESS);
}
static uint32_t
cs_drop_socket(uint32_t sn)
{
#ifdef XXX
cs_socket_t *sp;
mutex_enter(&sp->client_lock);
sp->thread_state |= SOCKET_THREAD_EXIT;
cv_broadcast(&sp->thread_cv);
cv_wait(&sp->caller_cv, &sp->client_lock);
mutex_exit(&sp->client_lock);
sp->flags &= ~SOCKET_IS_VALID;
#endif
return (CS_BAD_SOCKET);
}
static uint32_t
cs_get_socket(client_handle_t client_handle, uint32_t *socket,
uint32_t *function, cs_socket_t **csp, client_t **clp)
{
cs_socket_t *sp;
client_t *client;
uint32_t sn, fn;
int ret;
sn = *socket;
if (CLIENT_HANDLE_IS_SS(client_handle)) {
fn = CS_GET_FUNCTION_NUMBER(sn);
sn = CS_GET_SOCKET_NUMBER(sn);
} else {
fn = GET_CLIENT_FUNCTION(client_handle);
sn = GET_CLIENT_SOCKET(client_handle);
}
if (!(CHECK_SOCKET_NUM(sn, cs_globals.max_socket_num)))
return (CS_BAD_SOCKET);
if ((sp = cs_get_sp(sn)) == NULL)
return (CS_BAD_SOCKET);
if (csp)
*csp = sp;
mutex_enter(&sp->lock);
if (!(client = cs_find_client(client_handle, &ret))) {
mutex_exit(&sp->lock);
return (ret);
}
if (client->flags & CLIENT_CSI_CLIENT)
fn = CS_GET_FUNCTION_NUMBER(*socket);
mutex_exit(&sp->lock);
if (clp)
*clp = client;
*socket = CS_MAKE_SOCKET_NUMBER(sn, fn);
if (function)
*function = fn;
return (CS_SUCCESS);
}
static cs_window_t *
cs_get_wp(uint32_t wn)
{
cs_window_t *cw;
if (!(cs_globals.init_state & GLOBAL_INIT_STATE_SS_READY))
return (NULL);
if ((cw = cs_find_wp(wn)) == NULL)
return (NULL);
if (cw->state & CW_WINDOW_VALID)
return (cw);
#ifdef CS_DEBUG
if (cs_debug > 0) {
cmn_err(CE_CONT, "cs_get_wp(): wn=%d cw=%p\n",
(int)wn, (void *)cw);
}
#endif
return (NULL);
}
static cs_window_t *
cs_find_wp(uint32_t wn)
{
cs_window_t *cw = cs_globals.cw;
while (cw) {
if (cw->window_num == wn)
return (cw);
cw = cw->next;
}
#ifdef CS_DEBUG
if (cs_debug > 0) {
cmn_err(CE_CONT, "cs_find_wp(): wn=%d window_num=%d cw=%p\n",
(int)wn, (int)cw->window_num, (void *)cw);
}
#endif
return (NULL);
}
static int
cs_add_windows(int aw, uint32_t bn)
{
cs_window_t *cwp = cs_globals.cw;
cs_window_t *cw, *cwpp;
if (aw <= 0)
return (CS_BAD_WINDOW);
while (cwp) {
cwpp = cwp;
cwp = cwp->next;
}
while (aw--) {
cw = (cs_window_t *)kmem_zalloc(sizeof (cs_window_t), KM_SLEEP);
if (cs_globals.cw == NULL) {
cs_globals.cw = cw;
cwpp = cs_globals.cw;
} else {
cwpp->next = cw;
cwpp = cwpp->next;
}
cwpp->window_num = bn++;
cwpp->state = CW_WINDOW_VALID;
}
return (CS_SUCCESS);
}
static uint32_t
cs_ss_init()
{
cs_register_cardservices_t rcs;
csregister_t csr;
uint32_t ret;
csr.cs_magic = PCCS_MAGIC;
csr.cs_version = PCCS_VERSION;
csr.cs_card_services = CardServices;
csr.cs_event = NULL;
CIS_PARSER(CISP_CIS_SETUP, &csr);
rcs.magic = CS_STUBS_MAGIC;
rcs.function = CS_ENTRY_REGISTER;
rcs.cardservices = CardServices;
if ((ret = csx_register_cardservices(&rcs)) != CS_SUCCESS) {
cmn_err(CE_CONT, "cs_ss_init: can't register with "
"cs_stubs, retcode = 0x%x\n", ret);
return (ret);
}
return (CS_SUCCESS);
}
static int
cs_create_cis(cs_socket_t *sp)
{
uint32_t ret;
ret = (uint32_t)(uintptr_t)CIS_PARSER(CISP_CIS_LIST_CREATE,
cis_cistpl_std_callout, sp);
#ifdef CS_DEBUG
if (ret == CS_NO_CIS) {
if (cs_debug > 0)
cmn_err(CE_CONT, "cs_create_cis: socket %d has no CIS\n",
sp->socket_num);
} else if (ret != CS_SUCCESS) {
if (cs_debug > 0)
cmn_err(CE_CONT, "cs_create_cis: socket %d ERROR = 0x%x\n",
sp->socket_num, ret);
return (ret);
}
#else
if (ret != CS_NO_CIS)
if (ret != CS_SUCCESS)
return (ret);
#endif
if (!(sp->cis_flags & CW_VALID_CIS))
return (CS_SUCCESS);
if (!(sp->cis_flags & CW_MULTI_FUNCTION_CIS)) {
bcopy((caddr_t)&sp->cis[CS_GLOBAL_CIS],
(caddr_t)&sp->cis[0], sizeof (cis_info_t));
bzero((caddr_t)&sp->cis[CS_GLOBAL_CIS], sizeof (cis_info_t));
}
return (CS_SUCCESS);
}
static int
cs_destroy_cis(cs_socket_t *sp)
{
CIS_PARSER(CISP_CIS_LIST_DESTROY, sp);
return (CS_SUCCESS);
}
static int
cs_get_client_info(client_handle_t client_handle, client_info_t *ci)
{
cs_socket_t *sp;
client_t *client;
client_info_t *cinfo;
int ret = CS_SUCCESS;
if (CLIENT_HANDLE_IS_SS(client_handle)) {
ci->Attributes = (CS_CLIENT_INFO_SOCKET_SERVICES |
CS_CLIENT_INFO_VALID);
return (CS_SUCCESS);
}
if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL)
return (CS_BAD_SOCKET);
mutex_enter(&sp->client_lock);
mutex_enter(&sp->lock);
if ((client = cs_find_client(client_handle, &ret)) == NULL) {
mutex_exit(&sp->lock);
mutex_exit(&sp->client_lock);
return (ret);
}
if (!((client->event_mask | client->global_mask) &
CS_EVENT_CLIENT_INFO)) {
mutex_exit(&sp->lock);
mutex_exit(&sp->client_lock);
return (CS_NO_MORE_ITEMS);
}
cinfo = &client->event_callback_args.client_info;
bzero((caddr_t)cinfo, sizeof (client_info_t));
cinfo->Attributes = (ci->Attributes & CS_CLIENT_INFO_SUBSVC_MASK);
client->events |= CS_EVENT_CLIENT_INFO;
sp->thread_state |= SOCKET_WAIT_SYNC;
mutex_exit(&sp->lock);
cv_broadcast(&sp->thread_cv);
cv_wait(&sp->caller_cv, &sp->client_lock);
if (cinfo->Attributes & CS_CLIENT_INFO_VALID) {
bcopy((caddr_t)cinfo, (caddr_t)ci, sizeof (client_info_t));
ci->Attributes &= (CS_CLIENT_INFO_FLAGS_MASK |
CS_CLIENT_INFO_SUBSVC_MASK);
ci->Attributes &= ~(CS_CLIENT_INFO_CLIENT_MASK |
INFO_CARD_FLAGS_MASK |
CS_CLIENT_INFO_CLIENT_ACTIVE);
ci->Attributes |= (client->flags & (CS_CLIENT_INFO_CLIENT_MASK |
INFO_CARD_FLAGS_MASK));
(void) strcpy(ci->DriverName, client->driver_name);
if (cs_card_for_client(client))
ci->Attributes |= CS_CLIENT_INFO_CLIENT_ACTIVE;
} else {
ret = CS_NO_MORE_ITEMS;
}
mutex_exit(&sp->client_lock);
return (ret);
}
static int
cs_get_firstnext_client(get_firstnext_client_t *fnc, uint32_t flags)
{
cs_socket_t *sp;
client_t *client;
uint32_t sn = 0;
int ret = CS_SUCCESS;
switch (flags) {
case CS_GET_FIRST_FLAG:
if (fnc->Attributes & CS_GET_FIRSTNEXT_CLIENT_ALL_CLIENTS) {
while (sn < cs_globals.max_socket_num) {
if ((sp = cs_get_sp(sn)) != NULL) {
mutex_enter(&sp->client_lock);
if ((client = sp->client_list) != NULL)
break;
mutex_exit(&sp->client_lock);
}
sn++;
}
if (sn == cs_globals.max_socket_num)
return (CS_NO_MORE_ITEMS);
} else if (fnc->Attributes &
CS_GET_FIRSTNEXT_CLIENT_SOCKET_ONLY) {
if ((sp = cs_get_sp(CS_GET_SOCKET_NUMBER(fnc->Socket))) ==
NULL)
return (CS_BAD_SOCKET);
mutex_enter(&sp->client_lock);
if ((client = sp->client_list) == NULL) {
mutex_exit(&sp->client_lock);
return (CS_NO_MORE_ITEMS);
}
} else {
return (CS_BAD_ATTRIBUTE);
}
fnc->client_handle = client->client_handle;
fnc->num_clients = sp->num_clients;
mutex_exit(&sp->client_lock);
break;
case CS_GET_NEXT_FLAG:
if (fnc->Attributes & CS_GET_FIRSTNEXT_CLIENT_ALL_CLIENTS) {
sn = GET_CLIENT_SOCKET(fnc->client_handle);
if ((sp = cs_get_sp(sn)) == NULL)
return (CS_BAD_SOCKET);
mutex_enter(&sp->client_lock);
if ((client = cs_find_client(fnc->client_handle,
&ret)) == NULL) {
mutex_exit(&sp->client_lock);
return (ret);
}
if ((client = client->next) == NULL) {
mutex_exit(&sp->client_lock);
sn++;
while (sn < cs_globals.max_socket_num) {
if ((sp = cs_get_sp(sn)) != NULL) {
mutex_enter(&sp->client_lock);
if ((client = sp->client_list) != NULL)
break;
mutex_exit(&sp->client_lock);
}
sn++;
}
if (sn == cs_globals.max_socket_num)
return (CS_NO_MORE_ITEMS);
}
} else if (fnc->Attributes &
CS_GET_FIRSTNEXT_CLIENT_SOCKET_ONLY) {
sp = cs_get_sp(GET_CLIENT_SOCKET(fnc->client_handle));
if (sp == NULL)
return (CS_BAD_SOCKET);
mutex_enter(&sp->client_lock);
if ((client = cs_find_client(fnc->client_handle,
&ret)) == NULL) {
mutex_exit(&sp->client_lock);
return (ret);
}
if ((client = client->next) == NULL) {
mutex_exit(&sp->client_lock);
return (CS_NO_MORE_ITEMS);
}
} else {
return (CS_BAD_ATTRIBUTE);
}
fnc->client_handle = client->client_handle;
fnc->num_clients = sp->num_clients;
mutex_exit(&sp->client_lock);
break;
default:
ret = CS_BAD_ATTRIBUTE;
break;
}
return (ret);
}
static void
cs_set_acc_attributes(set_window_t *sw, uint32_t Attributes)
{
sw->attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
switch (Attributes & WIN_ACC_ENDIAN_MASK) {
case WIN_ACC_LITTLE_ENDIAN:
sw->attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
break;
case WIN_ACC_BIG_ENDIAN:
sw->attr.devacc_attr_endian_flags = DDI_STRUCTURE_BE_ACC;
break;
case WIN_ACC_NEVER_SWAP:
default:
sw->attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
break;
}
switch (Attributes & WIN_ACC_ORDER_MASK) {
case WIN_ACC_UNORDERED_OK:
sw->attr.devacc_attr_dataorder = DDI_UNORDERED_OK_ACC;
break;
case WIN_ACC_MERGING_OK:
sw->attr.devacc_attr_dataorder = DDI_MERGING_OK_ACC;
break;
case WIN_ACC_LOADCACHING_OK:
sw->attr.devacc_attr_dataorder = DDI_LOADCACHING_OK_ACC;
break;
case WIN_ACC_STORECACHING_OK:
sw->attr.devacc_attr_dataorder = DDI_STORECACHING_OK_ACC;
break;
case WIN_ACC_STRICT_ORDER:
default:
sw->attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
break;
}
}