/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * PCMCIA Card Services * The PCMCIA Card Services is a loadable module which * presents the Card Services interface to client device * drivers. * * Card Services uses Socket Services-like calls into the * PCMCIA nexus driver to manipulate socket and adapter * resources. * * Note that a bunch of comments are not indented correctly with the * code that they are commenting on. This is because cstyle is * is inflexible concerning 4-column indenting. */ #if defined(DEBUG) #define CS_DEBUG #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * The cs_strings header file is where all of the major strings that * Card Services uses are located. */ #include /* * Function declarations * * The main Card Services entry point */ int CardServices(int function, ...); /* * functions and globals used by Socket Services * * WAS: void *(*cis_parser)(int, ...) = NULL; */ void *(*cis_parser)(int, ...) = NULL; csfunction_t *cs_socket_services = NULL; /* * event handling functions */ 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); /* * SS<->CS communication and internal socket and window handling functions */ 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); /* * CIS handling functions */ 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 *); /* * client handling functions */ 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); /* * window handling functions */ 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 *); /* * IO, IRQ and configuration handling functions */ 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 *); /* * RESET and general info functions */ 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 *); /* * general functions */ 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 *); /* * global variables */ static int cs_max_client_handles = CS_MAX_CLIENTS; static client_t cs_socket_services_client; /* global SS 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 /* * cs_init - Initialize CS internal structures, databases, and state, * and register with SS * * XXX - Need to make sure that if we fail at any point that we free * any resources that we allocated, as well as kill any * threads that may have been started. */ int cs_init() { client_types_t *ct; client_t *client; /* * Initialize the CS global structure */ 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; /* * Set up the global Socket Services client, since we're going to * need it once we register with SS. */ 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); /* * Setup the client type structure - this is used in the socket event * thread to sequence the delivery of events to all clients on * the socket. */ 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); } /* * cs_deinit - Deinitialize CS * * This function cleans up any allocated resources, stops any running threads, * destroys any mutexes and condition variables, and finally frees up the * global socket and window structure arrays. */ 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 /* * Deregister with the Card Services kernel stubs module */ rcs.magic = CS_STUBS_MAGIC; rcs.function = CS_ENTRY_DEREGISTER; (void) csx_register_cardservices(&rcs); /* * Set the GLOBAL_INIT_STATE_NO_CLIENTS flag to prevent new clients * from registering. */ mutex_enter(&cs_globals.global_lock); cs_globals.init_state |= GLOBAL_INIT_STATE_NO_CLIENTS; mutex_exit(&cs_globals.global_lock); /* * Go through each socket and make sure that there are no clients * on any of the sockets. If there are, we can't deinit until * all the clients for every socket are gone. */ 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++; } } } /* * We don't allow unload if there are any clients registered * or if there are still sockets that are active. */ if ((have_clients > 0) || (have_sockets > 0)) return (BAD_FUNCTION); #ifdef XXX /* * If one or more sockets have been added, we need to deallocate * the resources associated with those sockets. */ /* * First, tell Socket Services that we're leaving, so that we * don't get any more event callbacks. */ SocketServices(CSUnregister); /* * Wait for the soft int timer to tell us it's done */ 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); /* * Remove the soft interrupt handler. */ 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); /* * Go through each socket and free any resource allocated to that * socket, as well as any mutexs and condition variables. */ for (sn = 0; sn < cs_globals.max_socket_num; sn++) { set_socket_t set_socket; if ((sp = cs_get_sp(sn)) != NULL) { /* * untimeout possible pending ready/busy timer */ 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); /* * Tell the event handler thread that we want it to exit, then * wait around until it tells us that it has exited. */ 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); /* * Tell the SS work thread that we want it to exit, then * wait around until it tells us that it has exited. */ 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); /* * Free the mutexii and condition variables that we used. */ 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 /* * Free the memory-mapped IO structure if we allocated one. */ if (sp->io_mmap_window) kmem_free(sp->io_mmap_window, sizeof (io_mmap_window_t)); #endif /* USE_IOMMAP_WINDOW */ /* * Return the socket to memory-only mode and turn off the * socket power. */ 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; /* turn off controls and indicators */ set_socket.State = (unsigned)~0; /* clear latched state bits */ (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 we fail this call, there's not much we can do, so * just continue with the resource deallocation. */ 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); } } /* cs_get_sp */ } /* for (sn) */ #endif /* XXX */ /* * Destroy the global mutexii. */ mutex_destroy(&cs_globals.global_lock); mutex_destroy(&cs_globals.window_lock); #ifdef XXX /* * Free the global "super-client" structure */ 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 /* XXX */ return (CS_SUCCESS); } /* * ==== drip, drip, drip - the Card Services waterfall :-) ==== */ /* * CardServices - general Card Services entry point for CS clients * and Socket Services; the address of this * function is handed to SS via the CSRegister * SS call */ 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); /* * Here's the Card Services waterfall */ switch (function) { /* * We got here as a result of the CIS module calling us * in response to cs_ss_init() calling the CIS module * at CIS_PARSER(CISP_CIS_SETUP, ...) */ 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 { /* * Replace the CIS Parser entry point if * necessary. */ if (cisr->cis_parser != NULL) cis_parser = cisr->cis_parser; cis_cistpl_std_callout = cisr->cistpl_std_callout; retcode = CS_SUCCESS; } } break; case CISUnregister: /* XXX - should we do some more checking */ /* XXX - need to protect this by a mutex */ 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; } /* switch(function) */ 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); } /* * ==== tuple and CIS handling section ==== */ /* * cs_parse_tuple - This function supports the CS ParseTuple function call. * * returns: CS_SUCCESS - if tuple parsed sucessfully * CS_NO_CARD - if no card in socket * CS_BAD_ARGS - if passed CIS list pointer is NULL * CS_UNKNOWN_TUPLE - if unknown tuple passed to CIS parser * CS_BAD_CIS - if generic parser error * CS_NO_CIS - if no CIS for card/function * * See notes for the cs_get_firstnext_tuple function. */ 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 there's no card in the socket or the card in the socket is not * for this client, then return an error. */ if (!(client->flags & CLIENT_CARD_INSERTED)) return (CS_NO_CARD); /* * Sanity check to be sure that we've got a non-NULL CIS list * pointer. */ if (!(tuple->CISOffset)) return (CS_BAD_ARGS); mutex_enter(&sp->cis_lock); /* * Check to see if there is a valid CIS for this function. * There is an implicit assumption here that if this * is a multi-function CIS and the specified function * number is not CS_GLOBAL_CIS that in order for there * to be a valid function-specific CIS, there also must * be a valid global CIS. This means that we don't need * to know whether this tuple came from the global CIS * or from the function-specific CIS. */ 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; } /* if (CW_VALID_CIS) */ return (ret); } /* * cs_get_firstnext_tuple - returns the first/next tuple of the specified type * this is to support the GetFirstTuple and * GetNextTuple function call * * flags - one of: * CS_GET_FIRST_FLAG causes function to support GetFirstTuple * CS_GET_NEXT_FLAG causes function to support GetNextTuple * * tuple_t->Attributes flags: * TUPLE_RETURN_LINK - XXX Not implemented, see notes below. * TUPLE_RETURN_IGNORED_TUPLES - return tuples with * CISTPLF_IGNORE_TUPLE set in the * cistpl_t->flags member. * * Notes for regular PC card driver callers: * * On a single-function card, the caller will get back all the tuples in * the CIS. * * On a multi-function card, the caller will get the tuples from the * global CIS followed by the tuples in the function-specific CIS. The * caller will not get any tuples from a function-specific CIS that * does not belong to the caller's function. * * Notes for Socket Services, the "super-client" or CSI driver callers: * * On a single-function card, the operation is the same as for regular * PC card driver callers with the addition that if the function number * is set to CS_GLOBAL_CIS this function will return CS_NO_CIS. * * On a multi-function card, the operation is the same as for regular * PC card driver callers with the addition that if the function number * is set to CS_GLOBAL_CIS the caller will only get tuples from the * global CIS. If a particular function nubmer does not exist, this * function will return CS_NO_CIS for that function. * * General notes: * * On both a single-function card and a multi-function card, if the tuple * comes from the global CIS chain, the CISTPLF_GLOBAL_CIS flag will be * set in the tuple_t->flags member. * * On a multi-function card, if the tuple comes from the function-specific * CIS chain, the CISTPLF_MF_CIS flag will be set in the tuple_t->flags * member. * * For other flags that are set in the tuple_t->flags member, see the * comments for the cis_list_lcreate function in the cis.c file. * * The CIS parser may not include all the tuples that are in the CIS in * the private CIS list that it creates and maintains. See the CIS * parser documentation for a list of tuples that the parser does not * include in the list. * * If a tuple has the CISTPLF_IGNORE_TUPLE flag set and the flags * parameter CIS_GET_LTUPLE_IGNORE is not set, that tuple will not * be returned to the caller. Instead, the next tuple that matches * the calling criteria will be returned (or NULL if no other tuples * match the calling criteria). If CIS_GET_LTUPLE_IGNORE is set in * the flags paramter, tuples in the CIS list that match the calling * criteria will be returned. * * XXX The PC Card 95 Standard says that if the TUPLE_RETURN_LINK flag in * the tuple_t->Attributes member is not set, then we don't return * any of the link tuples. This function ignores this flag and always * returns link tuples. * * Return codes: * CS_SUCCESS - if tuple sucessfully found and returned * CS_NO_CARD - if no card inserted * CS_NO_CIS - if no CIS for the specified card/function * CS_NO_MORE_ITEMS - if tuple not found or no more tuples * to return * * See notes for cs_get_socket for a description of valid client, socket * and function number combinations. */ 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 there's no card in the socket or the card in the socket is not * for this client, then return an error. */ if (!(client->flags & CLIENT_CARD_INSERTED)) return (CS_NO_CARD); mutex_enter(&sp->cis_lock); /* * If there's no CIS on this card or no CIS for the specified * function, then we can't do much. */ if ((!(sp->cis_flags & CW_VALID_CIS)) || (!(sp->cis[fn].flags & CW_VALID_CIS))) { mutex_exit(&sp->cis_lock); return (CS_NO_CIS); } /* * This will set the CIS_GET_LTUPLE_IGNORE flag if the * TUPLE_RETURN_IGNORED_TUPLES flag is set. The * assumption here is that the CIS_GET_LTUPLE_IGNORE * flag and the TUPLE_RETURN_IGNORED_TUPLES flag * shares the same bit position. If this ever changes, * we'll ahve to re-work this section of code. */ if (tuple->Attributes & TUPLE_RETURN_IGNORED_TUPLES) flags |= CIS_GET_LTUPLE_IGNORE; /* * Are we GetFirstTuple or GetNextTuple? */ if ((flags & CIS_GET_LTUPLE_OPMASK) & CS_GET_FIRST_FLAG) { /* * Initialize the tuple structure; we need this information when * we have to process a GetNextTuple or ParseTuple call. * If this card has a multi-function CIS, then we always start out * delivering tuples from the global CIS chain. If this card does * not have a multi-function CIS, then the function 0 CIS chain * will contain the complete CIS list. * If this is a multi-function card, then use the GET_FIRST_LTUPLE * macro to return the first tuple in the CIS list - we do this * since we don't want to return tuples with CISTPLF_IGNORE_TUPLE * set unless CIS_GET_LTUPLE_IGNORE is set in the flags parameter. * Note that we don't have to cross over into the fucntion-specific * CIS chain if GET_FIRST_LTUPLE returns NULL, since a MF CIS will * always have at least a CISTPL_LONGLINK_MFC tuple in the global * CIS chain - the test for NULL is just a sanity check. */ 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); } /* GET_FIRST_LTUPLE */ } else { tuple->CISOffset = sp->cis[0].cis; } /* CW_MULTI_FUNCTION_CIS */ } else { cistpl_t *tp; /* * Check to be sure that we have a non-NULL tuple list pointer. * This is necessary in the case where the caller calls us * with get next tuple requests but we don't have any more * tuples to give back. */ if (tuple->CISOffset == NULL) { mutex_exit(&sp->cis_lock); return (CS_NO_MORE_ITEMS); } /* * Point to the next tuple in the list. If we're searching for * a particular tuple, FIND_LTUPLE_FWD will find it. * * If there are no more tuples in the chain that we're looking * at, then if we're looking at the global portion of a * multi-function CIS, switch to the function-specific list * and start looking there. */ 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); } /* CISTPLF_GLOBAL_CIS */ } /* CW_MULTI_FUNCTION_CIS */ } /* GET_NEXT_TUPLE */ /* * If there are no more tuples in the chain, then return. */ if ((tuple->CISOffset = tp) == NULL) { mutex_exit(&sp->cis_lock); return (CS_NO_MORE_ITEMS); } } /* CS_GET_FIRST_FLAG */ /* * Check if we want to get the first of a particular type of tuple * or just the first tuple in the chain. * If there are no more tuples of the type we're searching for in * the chain that we're looking at, then if we're looking at * the global portion of a multi-function CIS, switch to the * function-specific list and start looking there. */ 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); } /* CISTPLF_GLOBAL_CIS */ } /* CW_MULTI_FUNCTION_CIS */ } /* FIND_LTUPLE_FWD */ /* * If there are no more tuples in the chain, then return. */ if ((tuple->CISOffset = tp) == NULL) { mutex_exit(&sp->cis_lock); return (CS_NO_MORE_ITEMS); } } /* !RETURN_FIRST_TUPLE */ /* * We've got a tuple, now fill out the rest of the tuple_t * structure. Callers can use the flags member to * determine whether or not the tuple data was copied * to the linked list or if it's still on the card. */ 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); } /* * cs_get_tuple_data - get the data portion of a tuple; this is to * support the GetTupleData function call. * * Note that if the data body of a tuple was not read from the CIS, * then this function will return CS_NO_MORE_ITEMS. * * For flags that are set in the tuple_t->flags member, see the * comments for the cis_list_lcreate function in the cis.c file. * These flags are copied into the tuple_t->flags member by the * cs_get_firstnext_tuple function call. * * See notes for the cs_get_firstnext_tuple function. */ 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 there's no card in the socket or the card in the socket is not * for this client, then return an error. */ 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)) { /* * Check to be sure that we have a non-NULL pointer to * a CIS list. */ if (!(tuple->CISOffset)) { mutex_exit(&sp->cis_lock); return (CS_NO_MORE_ITEMS); } /* * Since the tuple data buffer that the caller calls us with * is preallocated in the tuple_t structure, we ignore any * TupleDataMax value that the caller has setup and use the * actual size of the tuple data buffer in the structure. */ tuple->TupleDataMax = sizeof (tuple->TupleData); /* * Make sure the requested offset is not past the end of the * tuple data body nor past the end of the user-supplied * buffer. */ 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); } /* * The tuple data destination is always the tuple_t->TupleData * buffer in the tuple_t structure no matter where we read the * tuple data from. */ tdd = tuple->TupleData; bzero((caddr_t)tdd, sizeof (tuple->TupleData)); /* * Do we have a copy of the tuple data? If not, we have to * get a pointer to the CIS and read the tuple data from the * card itself. */ 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; /* * Setup the proper space flags as well as setup the * address offset to point to the start of the tuple * data area; we need to do the latter since the * cis_store_cis_addr function in cis.c sets up the * tuple->CISOffset->offset offset to point to the * start of the tuple. */ 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); } /* cs_init_cis_window */ while (nbytes--) { *tdd++ = csx_Get8(cis_handle, newoffset++); if (tuple->CISOffset->flags & CISTPLF_AM_SPACE) newoffset++; } /* while */ break; default: mutex_exit(&sp->cis_lock); return (CS_GENERAL_FAILURE); } /* switch */ ret = CS_SUCCESS; } else { ret = CS_NO_CIS; } /* if (CW_VALID_CIS) */ mutex_exit(&sp->cis_lock); return (ret); } /* * cs_validate_cis - validates the CIS on a card in the given socket; this * is to support the ValidateCIS function call. * * Notes for regular PC card driver callers: * * Regular PC card drivers calling ValidateCIS will get the meaning of * the structure members as specified in the standard. * * Notes for Socket Services, the "super-client" or CSI driver callers: * * with: Function Number = CS_GLOBAL_CIS * * For a single-function card, CS_NO_CIS will be returned and the * cisinfo_t->Chains and cisinfo_t->Tuples members will be set to 0. * * For a multi-function card, cisinfo_t->Chains will contain a count of * the number of CIS chains in the global portion of the CIS, and * cisinfo_t->Tuples will contain a count of the number of tuples in * the global portion of the CIS. * * with: 0 <= Function Number < CIS_MAX_FUNCTIONS * * For a single-function card, if the function number is equal to 0 and * has a CIS, cisinfo_t->Chains will contain a count of the number of * CIS chains in the CIS, and cisinfo_t->Tuples will contain a count of * the number of tuples in the CIS. If the card does not have a CIS, or * if the function number is not equal to 0, CS_NO_CIS will be returned * and the cisinfo_t->Chains and cisinfo_t->Tuples members will be set * to 0. * * For a multi-function card, cisinfo_t->Chains will contain a count of * the number of CIS chains in the global and function-specific * portions of the CIS, and cisinfo_t->Tuples will contain a count of * the number of tuples in the global and function-specific portions of * the CIS. If the function does not exist or has no CIS, CS_NO_CIS * will be returned and the cisinfo_t->Chains and cisinfo_t->Tuples * members will be set to 0. * * General notes: * * If the card does not have a CIS, or if the function does not exist * or has no CIS, CS_NO_CIS will be returned and the cisinfo_t->Chains * and cisinfo_t->Tuples members will be set to 0. * * Most of the work of validating the CIS has already been done by the * CIS parser module, so we don't have to do much here except for * looking at the various flags and tuple/chain counts that were already * setup by the CIS parser. * * See notes for the cs_get_firstnext_tuple function. */ 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 there's no card in the socket or the card in the socket is not * for this client, then return an error. */ 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; } /* !CS_GLOBAL_CIS */ ret = CS_SUCCESS; } else { cisinfo->Chains = 0; cisinfo->Tuples = 0; ret = CS_NO_CIS; } mutex_exit(&sp->cis_lock); return (ret); } /* * cs_init_cis_window - initializes the CIS window for the passed socket * * calling: *sp - pointer to the per-socket structure * *offset - offset from start of AM or CM space * *hp - pointer to acc_handle_t to store modified * window access handle in * flags - one of: * CISTPLF_AM_SPACE - set window to AM space * CISTPLF_CM_SPACE - set window to CM space * * returns: CS_SUCCESS if CIS window was set up * *offset - contains adjusted offset to use to access * requested space * CS_BAD_WINDOW if CIS window could not be setup * CS_GENERAL_FAILURE if socket has a CIS window number * but the window flags are wrong * * Note: This function will check to be sure that there is a valid * CIS window allocated to this socket. * If there is an error in setting up the window hardware, the * CIS window information for this socket is cleared. * This function is also used by routines that need to get * a pointer to the base of AM space to access the card's * configuration registers. * The passed offset is the un-window-size-aligned offset. */ 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; /* * Check to be sure that we have a valid CIS window */ 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); } /* * Check to be sure that this window is allocated for CIS use */ 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); } /* * Get the characteristics of this window - we use this to * determine whether we need to re-map the window or * just move the window offset on the card. */ iw.window = sp->cis_win_num; SocketServices(SS_InquireWindow, &iw); /* * We've got a window, now set up the hardware. If we've got * a variable sized window, then all we need to do is to * get a valid mapping to the base of the window using * the current window size; if we've got a fixed-size * window, then we need to get a mapping to the window * starting at offset zero of the window. */ 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); } /* * Return a normalized base offset; this takes care of the case * where the required offset is greater than the window size. * BugID 1236404 * code was: * *offset = *offset & (set_page.offset - 1); */ *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; /* * The PCMCIA SS spec specifies this be expressed in * a device speed format per 5.2.7.1.3 but * our implementation of SS_SetWindow uses * actual nanoseconds. */ sw.speed = CIS_DEFAULT_SPEED; sw.base = 0; /* * Set up the window - if this fails, then just set the * CIS window number back to it's initialized value so * that we'll fail when we break out of the loop. */ if (SocketServices(SS_SetWindow, &sw) != SUCCESS) { sp->cis_win_num = PCMCIA_MAX_WINDOWS; cw->state = 0; /* XXX do we really want to do this? */ 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; /* XXX do we really want to do this? */ return (CS_BAD_WINDOW); } /* if (SS_SetPage) */ } /* if (SS_SetWindow) */ /* * Get the window information for the CIS window for this socket. */ gw.window = sp->cis_win_num; gw.socket = sp->socket_num; /* XXX - SS_GetWindow should set this */ if (SocketServices(SS_GetWindow, &gw) != SUCCESS) return (CS_BAD_WINDOW); *hp = (acc_handle_t)gw.handle; return (CS_SUCCESS); } /* * ==== client registration/deregistration section ==== */ /* * cs_register_client - This supports the RegisterClient call. * * Upon successful registration, the client_handle_t * handle argument will * contain the new client handle and we 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; /* * See if we're not supposed to register any new clients. */ if (cs_globals.init_state & GLOBAL_INIT_STATE_NO_CLIENTS) return (CS_OUT_OF_RESOURCE); /* * Do a version check - if the client expects a later version of * Card Services than what we are, return CS_BAD_VERSION. * XXX - How do we specify just a PARTICULAR version of CS?? */ if (CS_VERSION < cr->Version) return (CS_BAD_VERSION); /* * Check to be sure that the client has given us a valid set of * client type flags. We also use this opportunity to see * if the registering client is Socket Services or is a * "super-client" or a CSI client. * * Note that SS can not set any flag in the Attributes field other * than the INFO_SOCKET_SERVICES flag. * * Valid combinations of cr->Attributes and cr->EventMask flags: * * for Socket Services: * cr->Attributes: * set: * INFO_SOCKET_SERVICES * clear: * {all other flags} * cr->EventMask: * don't care: * {all flags} * * for regular clients: * cr->Attributes: * only one of: * INFO_IO_CLIENT * INFO_MTD_CLIENT * INFO_MEM_CLIENT * don't care: * INFO_CARD_SHARE * INFO_CARD_EXCL * cr->EventMask: * clear: * CS_EVENT_ALL_CLIENTS * don't care: * {all other flags} * * for CSI clients: * cr->Attributes: * set: * INFO_IO_CLIENT * INFO_CSI_CLIENT * clear: * INFO_MTD_CLIENT * INFO_MEM_CLIENT * don't care: * INFO_CARD_SHARE * INFO_CARD_EXCL * cr->EventMask: * don't care: * {all flags} * * for "super-clients": * cr->Attributes: * set: * INFO_IO_CLIENT * INFO_MTD_CLIENT * INFO_SOCKET_SERVICES * INFO_CARD_SHARE * clear: * INFO_MEM_CLIENT * INFO_CARD_EXCL * cr->EventMask: * don't care: * {all flags} */ switch (cr->Attributes & INFO_CLIENT_TYPE_MASK) { /* * Check first to see if this is Socket Services registering; if * so, we don't do anything but return the client handle that is * in the global SS client. */ case INFO_SOCKET_SERVICES: *ch = cs_socket_services_client.client_handle; return (CS_SUCCESS); /* NOTREACHED */ /* CSI clients */ case (INFO_CSI_CLIENT | INFO_IO_CLIENT): break; /* regular clients */ case INFO_IO_CLIENT: case INFO_MTD_CLIENT: case INFO_MEM_CLIENT: if (cr->EventMask & CS_EVENT_ALL_CLIENTS) return (CS_BAD_ATTRIBUTE); break; /* "super-client" clients */ 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); /* * We only allow one "super-client" per system. */ 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); } /* switch (cr->Attributes) */ /* * Now, actually create the client node on the socket; this will * also return the new client handle if there were no errors * creating the client node. * The DIP2SOCKET_NUM macro will return the socket and function * number using the encoding specified in the cs_priv.h file. */ 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)); } /* CLIENT_SUPER_CLIENT */ /* * This registering client is a "super-client", so we create one * client node for each socket in the system. We use the * client_reg_t.priv structure member to point to a struct * that the "super-client" client knows about. The client * handle pointer is not used in this case. * We return CS_SUCCESS if at least one client node could be * created. The client must check the error codes in the * error code array to determine which clients could not * be created on which sockets. * We return CS_BAD_HANDLE if no client nodes could be created. */ 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 we couldn't create any client nodes at all, then * return an error. */ if (!scr->num_clients) { /* * XXX - The global superclient lock now gets * cleared in cs_deregister_client */ /* cs_clear_superclient_lock(super_client); */ return (CS_BAD_HANDLE); } return (CS_SUCCESS); } /* * cs_add_client_to_socket - this function creates the client node on the * requested socket. * * Note that if we return an error, there is no state that can be cleaned * up. The only way that we can return an error with allocated resources * would be if one of the client handle functions had an internal error. * Since we wouldn't get a valid client handle in this case anyway, there * would be no way to find out what was allocated and what wasn't. */ 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); /* * Run through all of the registered clients and compare the passed * dip to the dip of each client to make sure that this client * is not trying to register more than once. If they are, then * display a message and return an error. * XXX - we should really check all the sockets in case the client * manipulates the instance number in the dip. * XXX - if we check each socket, we ned to also check for the * "super-client" since it will use the same dip for all * of it's client nodes. */ 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; } /* while (client) */ mutex_exit(&sp->lock); /* * Create a unique client handle then make sure that we can find it. * This has the side effect of getting us a pointer to the * client structure as well. * Create a client list entry - cs_create_client_handle will use this * as the new client node. * We do it here so that we can grab the sp->lock mutex for the * duration of our manipulation of the client list. * If this function fails, then it will not have added the newly * allocated client node to the client list on this socket, * so we have to free the node that we allocated. */ 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); } /* * Make sure that this is a valid client handle. We should never * fail this since we just got a valid client handle. * If this fails, then we have an internal error so don't bother * trying to clean up the allocated client handle since the * whole system is probably hosed anyway and will shortly * esplode. * It doesn't make sense to call cs_deregister_client at this point * to clean up this broken client since the deregistration * code will also call cs_find_client and most likely fail. */ 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); } /* * Save the DDI information. */ 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); /* * Copy over the interesting items that the client gave us. */ 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)); /* * Set the client handle since the client needs a client handle * when they call us for their event handler. */ client->event_callback_args.client_handle = *ch; /* * Initialize the IO window numbers; if an IO window number is equal * to PCMCIA_MAX_WINDOWS it means that IO range is not in use. */ client->io_alloc.Window1 = PCMCIA_MAX_WINDOWS; client->io_alloc.Window2 = PCMCIA_MAX_WINDOWS; /* * Give the client the iblock and idevice cookies to use in * the client's event handler high priority mutex. */ cr->iblk_cookie = sp->iblk; cr->idev_cookie = sp->idev; /* * Set up the global event mask information; we copy this directly * from the client; since we are the only source of events, * any bogus bits that the client puts in here won't matter * because we'll never look at them. */ client->global_mask = cr->EventMask; /* * If this client registered as a CSI client, set the appropriate * flag in the client's flags area. */ if (cr->Attributes & INFO_CSI_CLIENT) client->flags |= CLIENT_CSI_CLIENT; /* * If this client registered as a "super-client" set the appropriate * flag in the client's flags area. */ if (super_client == CLIENT_SUPER_CLIENT) client->flags |= CLIENT_SUPER_CLIENT; /* * Save other misc information that this client gave us - it is * used in the GetClientInfo function. */ client->flags |= (cr->Attributes & INFO_CARD_FLAGS_MASK); /* * Determine if we should give artificial card insertion events and * a registration complete event. Since we don't differentiate * between sharable and exclusive use cards when giving clients * event notification, we modify the definition of the share/excl * flags as follows: * * If either INFO_CARD_SHARE or INFO_CARD_EXCL is set, * the client will receive artificial card insertion * events (if the client's card is currently in the * socket) and a registration complete event. * * If neither of the INFO_CARD_SHARE or INFO_CARD_EXCL is * set, the client will not receive an artificial card * insertion event nor a registration complete event * due to the client's call to register client. * * The client's event mask is not affected by the setting * of these two bits. */ if (cr->Attributes & (INFO_CARD_SHARE | INFO_CARD_EXCL)) client->pending_events = CS_EVENT_REGISTRATION_COMPLETE; /* * Check to see if the card for this client is currently in * the socket. If it is, then set CLIENT_CARD_INSERTED * since clients that are calling GetStatus at attach * time will typically check to see if their card is * currently installed. * If this is the CSI client, we also need to check to see * if there is any card inserted in the socket, since * the cs_card_for_client function will always return * TRUE for a CSI client. * XXX What about super-clients? */ 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); } /* SS_GetStatus */ if (!(cs_sbm2cse(get_ss_status.CardState) & CS_EVENT_CARD_INSERTION)) cie = 0; } /* CLIENT_CSI_CLIENT */ if (cs_card_for_client(client) && (cie != 0)) { client->pending_events |= CS_EVENT_CARD_INSERTION; client->flags |= CLIENT_CARD_INSERTED; } /* cs_card_for_client */ sp->num_clients++; mutex_exit(&sp->lock); EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_SUCCESS); } /* * cs_deregister_client - This supports the DeregisterClient call. */ 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; /* * Check to see if this is the Socket Services client handle; if it * is, we don't do anything except for return success. */ if (CLIENT_HANDLE_IS_SS(client_handle)) return (CS_SUCCESS); /* * Get a pointer to this client's socket structure. */ if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL) return (CS_BAD_SOCKET); EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp); /* * Make sure that this is a valid client handle. */ if (!(client = cs_find_client(client_handle, &error))) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (error); } /* * Make sure that any resources allocated by this client are * not still allocated, and that if this is an MTD that * no MTD operations are still in progress. */ 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); } /* * Any previously allocated resources are not allocated anymore, and * no MTD operations are in progress, so if this is an MTD client * then do any MTD-specific client deregistration, and then * nuke this client. * We expect cs_deregister_mtd to never fail. */ 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); /* * If this was the "super-client" deregistering, then this * will clear the global "super-client" lock. * XXX - move this outside the per-socket code. */ cs_clear_superclient_lock(super_client); return (error); } /* * cs_create_next_client_minor - returns the next available client minor * number or 0 if none available * * Note that cs_find_client will always return a valid pointer to the * global Socket Services client which has a client minor number * of 0; this means that this function can never return a 0 as the * next valid available client minor number. */ 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); } /* * cs_find_client - finds the client pointer associated with the client handle * or NULL if client not found * * returns: (client_t *)NULL - if client not found or an error occured * If the error argument is not NULL, * it is set to: * CS_BAD_SOCKET - socket number in client_handle_t is * invalid * CS_BAD_HANDLE - client not found * If no error, the error argument is not modified. * (client_t *) - pointer to client_t structure * * Note that each socket always has a pseudo client with a client minor number * of 0; this client minor number is used for Socket Services access to * Card Services functions. The client pointer returned for client minor * number 0 is the global Socket Services client pointer. */ static client_t * cs_find_client(client_handle_t client_handle, int *error) { cs_socket_t *sp; client_t *clp; /* * If we are being asked to see if a client with a minor number * of 0 exists, always return a pointer to the global Socket * Services client, since this client always exists, and is * only for use by Socket Services. There is no socket * associated with this special client handle. */ if (CLIENT_HANDLE_IS_SS(client_handle)) return (&cs_socket_services_client); /* * Check to be sure that the socket number is in range */ 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); } /* * cs_destroy_client_handle - destroys client handle and client structure of * passed client handle * * returns: CS_SUCCESS - if client handle sucessfully destroyed * CS_BAD_HANDLE - if client handle is invalid or if trying * to destroy global SS client * {other errors} - other errors from cs_find_client() */ static int cs_destroy_client_handle(client_handle_t client_handle) { client_t *clp; cs_socket_t *sp; int error = CS_BAD_HANDLE; /* * See if we were passed a valid client handle or if we're being asked * to destroy the Socket Services client */ 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); /* * Recycle this client's minor number. This will most likely * be the next client minor number we use, but it is also * a hint to cs_create_client_handle, and that function * may actually create a new client handle using a minor * number different that this number. */ mutex_enter(&sp->lock); sp->next_cl_minor = GET_CLIENT_MINOR(client_handle); /* * See if we're the first or not in the client list; if we're * not first, then just adjust the client behind us to * point to the client ahead of us; this could be NULL * if we're the last client in the list. */ if (clp->prev) { clp->prev->next = clp->next; } else { /* * We are first, so adjust the client list head pointer * in the socket to point to the client structure that * follows us; this could turn out to be NULL if we're * the only client on this socket. */ sp->client_list = clp->next; } /* * If we're not the last client in the list, point the next * client to the client behind us; this could turn out * to be NULL if we're the first client on this socket. */ if (clp->next) clp->next->prev = clp->prev; sp->num_clients--; mutex_exit(&sp->lock); /* * Free this client's memory. */ kmem_free(clp, sizeof (client_t)); return (CS_SUCCESS); } /* * cs_create_client_handle - create a new client handle for the passed * socket and function number * * returns: 0 - if can't create client for some reason * client_handle_t - new client handle */ 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); /* * Get the next available minor number that we can use. We use the * next_cl_minor number as a hint to cs_create_next_client_minor * and in most cases this will be the minor number we get back. * If for some reason we can't get a minor number, return an error. * The only way we could get an error would be if there are * already the maximum number of clients for this socket. Since * the maximum number of clients per socket is pretty large, * this error is unlikely to occur. */ if (!(next_minor = cs_create_next_client_minor(socket_num, sp->next_cl_minor))) return (0); /* * Got a new client minor number, now create a new client handle. */ client_handle = MAKE_CLIENT_HANDLE(CS_GET_SOCKET_NUMBER(socket_num), CS_GET_FUNCTION_NUMBER(socket_num), next_minor); /* * If this client handle exists, then we have an internal * error; this should never happen, BTW. This is really * a double-check on the cs_create_next_client_minor * function, which also calls cs_find_client. */ 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 we don't have any clients on this socket yet, create * a new client and hang it on the socket client list. */ if (!sp->client_list) { sp->client_list = cclp; clp = sp->client_list; } else { /* * There are other clients on this socket, so look for * the last client and add our new client after it. */ clp = sp->client_list; while (clp->next) { clp = clp->next; } clp->next = cclp; clp->next->prev = clp; clp = clp->next; } /* if (!sp->client_list) */ /* * Assign the new client handle to this new client structure. */ clp->client_handle = client_handle; /* * Create the next available client minor number for this socket * and save it away. */ sp->next_cl_minor = cs_create_next_client_minor(socket_num, sp->next_cl_minor); return (client_handle); } /* * cs_clear_superclient_lock - clears the global "super-client" lock * * Note: this function uses the cs_globals.global_lock so observe proper * nexting of locks!! */ static void cs_clear_superclient_lock(int super_client) { /* * If this was a "super-client" registering then we need * to clear the GLOBAL_SUPER_CLIENT_REGISTERED flag * so that other "super-clients" can register. */ 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); } } /* * ==== event handling section ==== */ /* * cs_event - CS event hi-priority callback handler * * This function gets called by SS and is passed the event type in * the "event" argument, and the socket number in the "sn" * argument. The "sn" argument is a valid logical socket * number for all events except the PCE_SS_READY event. * * The PCE_SS_INIT_STATE, PCE_ADD_SOCKET and PCE_DROP_SOCKET events * are never called at high priority. These events return * the following return codes: * * CS_SUCCESS - operation sucessful * CS_BAD_SOCKET - unable to complete operation * CS_UNSUPPORTED_FUNCTION - bad subfunction of * PCE_SS_INIT_STATE * * The caller MUST look at these return codes! * * This function is called at high-priority interrupt time for standard * Card Services events, and the only standard Card Services * event that it handles directly is the CS_EVENT_CARD_REMOVAL * event, which gets shuttled right into the client's event * handler. All other events are just queued up and the socket * event thread is woken up via the soft interrupt handler. * Note that CS_EVENT_CARD_INSERTION events are not set in the clients' * event field, since the CS card insertion/card ready processing * code is responsible for setting this event in a client's * event field. * */ /*ARGSUSED*/ 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; /* * Handle special SS<->CS events */ 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; } /* switch (sn) */ 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)); } /* switch (event) */ if ((sp = cs_get_sp(sn)) == NULL) return (CS_BAD_SOCKET); /* * Check to see if CS wants to unload - we do this since it's possible * to disable certain sockets. Do NOT acquire any locks yet. */ 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 /* * Convert SS events to CS events; handle the PRR if necessary. */ sp->events |= ss_to_cs_events(sp, event); /* * We want to maintain the required event dispatching order as * specified in the PCMCIA spec, so we cycle through all * clients on this socket to make sure that they are * notified in the correct order of any high-priority * events. */ ct = &client_types[0]; while (ct) { /* * Point to the head of the client list for this socket, and go * through each client to set up the client events as well as * call the client's event handler directly if we have a high * priority event that we need to tell the client about. */ 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 /* * Handle the suspend and card removal events * specially here so that the client can receive * these events at high-priority. */ 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); } /* if (CLIENT_CARD_INSERTED) */ client->events &= ~CS_EVENT_PM_SUSPEND; } /* if (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); /* * Check to see if the client wants low priority * removal events as well. */ if ((client->event_mask | client->global_mask) & CS_EVENT_CARD_REMOVAL_LOWP) { client->events |= CS_EVENT_CARD_REMOVAL_LOWP; } } /* if (CLIENT_CARD_INSERTED) */ client->events &= ~CS_EVENT_CARD_REMOVAL; } /* if (CS_EVENT_CARD_REMOVAL) */ } /* if (ct->type) */ if (ct->order & CLIENT_EVENTS_LIFO) { client = client->prev; } else { client = client->next; } } /* while (client) */ ct = ct->next; } /* while (ct) */ /* * Set the SOCKET_NEEDS_THREAD flag so that the soft interrupt * handler will wakeup this socket's event thread. */ if (sp->events) sp->flags |= SOCKET_NEEDS_THREAD; /* * Fire off a soft interrupt that will cause the socket thread * to be woken up and any remaining events to be sent to * the clients on this socket. */ 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); } /* * cs_card_insertion - handle card insertion and card ready events * * We read the CIS, if present, and store it away, then tell SS that * we have read the CIS and it's ready to be parsed. Since card * insertion and card ready events are pretty closely intertwined, * we handle both here. For card ready events that are not the * result of a card insertion event, we expect that the caller has * already done the appropriate processing and that we will not be * called unless we received a card ready event right after a card * insertion event, i.e. that the SOCKET_WAIT_FOR_READY flag in * sp->thread_state was set or if we get a CARD_READY event right * after a CARD_INSERTION event. * * calling: sp - pointer to socket structure * event - event to handle, one of: * CS_EVENT_CARD_INSERTION * CS_EVENT_CARD_READY * CS_EVENT_SS_UPDATED */ static int cs_card_insertion(cs_socket_t *sp, event_t event) { int ret; /* * Since we're only called while waiting for the card insertion * and card ready sequence to occur, we may have a pending * card ready timer that hasn't gone off yet if we got a * real card ready event. */ 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 /* * Handle card insertion processing */ if (event & CS_EVENT_CARD_INSERTION) { set_socket_t set_socket; get_ss_status_t gs; /* * Check to be sure that we have a valid CIS window */ 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); } /* * Apply power to the socket, enable card detect and card ready * events, then reset the socket. */ 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; /* turn off controls and indicators */ set_socket.State = (unsigned)~0; /* clear latched state bits */ (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); } /* * Clear the ready and ready_timeout events since they are now * bogus since we're about to reset the socket. * XXX - should these be cleared right after the RESET?? */ 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); /* * We are required by the PCMCIA spec to wait some number of * milliseconds after reset before we access the card, so * we set up a timer here that will wake us up and allow us * to continue with our card initialization. */ 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 we have a pending CS_EVENT_CARD_REMOVAL event it * means that we likely got CD line bounce on the * insertion, so terminate this processing. */ 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); } /* if (CS_EVENT_CARD_REMOVAL) */ /* * If we got a card ready event after the reset, then don't * bother setting up a card ready timer, since we'll blast * right on through to the card ready processing. * Get the current card status to see if it's ready; if it * is, we probably won't get a card ready event. */ 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; } /* if (CS_EVENT_CARD_READY) */ mutex_exit(&sp->lock); } /* if (CS_EVENT_CARD_INSERTION) */ /* * Handle card ready processing. This is only card ready processing * for card ready events in conjunction with a card insertion. */ if (event == CS_EVENT_CARD_READY) { get_socket_t get_socket; set_socket_t set_socket; /* * The only events that we want to see now are card removal * events. */ 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; /* XXX (is ~0 correct here?) to reset latched values */ 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); } /* * Grab the cis_lock mutex to protect the CIS-to-be and * the CIS window, then fire off the CIS parser to * create a local copy of the card's CIS. */ 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 we have a pending CS_EVENT_CARD_REMOVAL event it * means that we likely got CD line bounce on the * insertion, so destroy the CIS and terminate this * processing. We'll get called back to handle the * insertion again later. */ if (sp->events & CS_EVENT_CARD_REMOVAL) { mutex_enter(&sp->cis_lock); (void) cs_destroy_cis(sp); mutex_exit(&sp->cis_lock); } else { /* * Schedule the call to the Socket Services work thread. */ 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 (CS_EVENT_CARD_REMOVAL) */ } /* if (CS_EVENT_CARD_READY) */ /* * Socket Services has parsed the CIS and has done any other * work to get the client driver loaded and attached if * necessary, so setup the per-client state. */ if (event == CS_EVENT_SS_UPDATED) { client_t *client; /* * Now that we and SS are done handling the card insertion * semantics, go through each client on this socket and set * the CS_EVENT_CARD_INSERTION event in each client's event * field. We do this here instead of in cs_event so that * when a client gets a CS_EVENT_CARD_INSERTION event, the * card insertion and ready processing has already been done * and SocketServices has had a chance to create a dip for * the card in this socket. */ 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; } /* while (client) */ mutex_exit(&sp->lock); } /* if (CS_EVENT_SS_UPDATED) */ return (CS_SUCCESS); } /* * cs_card_removal - handle card removal events * * Destroy the CIS. * * calling: sp - pointer to socket structure * */ 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 /* * Remove any pending card ready timer */ UNTIMEOUT(sp->rdybsy_tmo_id); /* * Clear various flags so that everyone else knows that there's * nothing on this socket anymore. Note that we clear the * SOCKET_CARD_INSERTED and SOCKET_IS_IO flags in the * ss_to_cs_events event mapping function. */ mutex_enter(&sp->lock); sp->thread_state &= ~(SOCKET_WAIT_FOR_READY | SOCKET_RESET_TIMER); /* * Turn off socket power and set the socket back to memory mode. * Disable all socket events except for CARD_INSERTION events. */ 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; /* turn off controls and indicators */ set_socket.State = (unsigned)~0; /* clear latched state bits */ (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 /* * Destroy the CIS and tell Socket Services that we're done * handling the card removal event. */ 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); } /* * ss_to_cs_events - convert Socket Services events to Card Services event * masks; this function will not read the PRR if the * socket is in IO mode; this happens in cs_event_thread * * This function returns a bit mask of events. * * Note that we do some simple hysterious on card insertion and card removal * events to prevent spurious insertion and removal events from being * propogated down the chain. */ 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; /* * If we're processing a removal event, it makes * no sense to keep any insertion or ready events, * so nuke them here. This will not clear any * insertion events in the per-client event field. */ sp->events &= ~(CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_READY | CS_EVENT_READY_TIMEOUT); /* * We also don't need to wait for READY anymore since * it probably won't show up, or if it does, it will * be a bogus READY event as the card is sliding out * of the socket. Since we never do a cv_wait on the * card ready timer, it's OK for that timer to either * never go off (via an UNTIMEOUT in cs_card_removal) * or to go off but not do a cv_broadcast (since the * SOCKET_WAIT_FOR_READY flag is cleared here). */ 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; } /* switch(event) */ return (revent); } /* * cs_ready_timeout - general purpose READY/BUSY and RESET timer * * Note that we really only expect one of the two events to be asserted when * we are called. XXX - Perhaps this might be a problem later on?? * * There is also the problem of cv_broadcast dropping the interrupt * priority, even though we have our high-priority mutex held. If * we hold our high-priority mutex (sp->lock) over a cv_broadcast, and * we get a high-priority interrupt during this time, the system will * deadlock or panic. Thanks to Andy Banta for finding this out in * the SPC/S (stc.c) driver. * * This callback routine can not grab the sp->client_lock mutex or deadlock * will result. */ 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); } /* * cs_event_softintr_timeout - wrapper function to call cs_socket_event_softintr */ /* ARGSUSED */ void cs_event_softintr_timeout(void *arg) { /* * If we're trying to unload this module, then don't do * anything but exit. * We acquire the cs_globals.global_lock mutex here so that * we can correctly synchronize with cs_deinit when it * is telling us to shut down. XXX - is this bogus?? */ 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); } } /* * cs_socket_event_softintr - This function just does a cv_broadcast on behalf * of the high-priority interrupt handler. * * Note: There is no calling argument. */ /*ARGSUSED*/ uint32_t cs_socket_event_softintr(caddr_t notused) { cs_socket_t *sp; uint32_t sn; int ret = DDI_INTR_UNCLAIMED; /* * If the module is on it's way out, then don't bother * to do anything else except return. */ 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); /* * Note that we return DDI_INTR_UNCLAIMED here * since we don't want to be constantly * called back. */ return (ret); } else { cs_globals.init_state |= GLOBAL_IN_SOFTINTR; mutex_exit(&cs_globals.global_lock); } /* * Go through each socket and dispatch the appropriate events. * We have to funnel everything through this one routine because * we can't do a cv_broadcast from a high level interrupt handler * and we also can't have more than one soft interrupt handler * on a single dip and using the same handler address. */ 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 we're being asked to unload CS, then don't bother * waking up the socket event thread handler. */ 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); } /* if (SOCKET_NEEDS_THREAD) */ } /* if (SOCKET_INIT_STATE_READY) */ } /* cs_get_sp */ } /* for (sn) */ mutex_enter(&cs_globals.global_lock); cs_globals.init_state &= ~GLOBAL_IN_SOFTINTR; mutex_exit(&cs_globals.global_lock); return (ret); } /* * cs_event_thread - This is the per-socket event thread. */ 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); /* * Check to see if there are any special thread operations that * we are being asked to perform. */ 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); /* wakes up cs_deinit */ mutex_exit(&sp->client_lock); return; } /* if (SOCKET_THREAD_EXIT) */ #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 /* * Handle CS_EVENT_CARD_INSERTION events */ if (sp->events & CS_EVENT_CARD_INSERTION) { mutex_enter(&sp->lock); sp->events &= ~CS_EVENT_CARD_INSERTION; mutex_exit(&sp->lock); /* * If we have a pending CS_EVENT_CARD_REMOVAL event it * means that we likely got CD line bounce on the * insertion, so terminate this processing. */ 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 (CS_EVENT_CARD_INSERTION) */ /* * Handle CS_EVENT_CARD_READY and CS_EVENT_READY_TIMEOUT events */ 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 (SOCKET_WAIT_FOR_READY) */ } /* if (CS_EVENT_CARD_READY) */ /* * Handle CS_EVENT_SS_UPDATED events */ 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 (CS_EVENT_SS_UPDATED) */ /* * Handle CS_EVENT_STATUS_CHANGE events */ 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; /* * Go through each client and add any events that we saw to * the client's event list if the client has that event * enabled in their event mask. * Remove any events that may be pending for this client if * the client's event mask says that the client doesn't * want to see those events anymore. This handles the * case where the client had an event enabled in it's * event mask when the event came in but between that * time and the time we're called here the client * disabled that event. */ client = sp->client_list; while (client) { /* * Read the PRR (if it exists) and check for any events. * The PRR will only be read if the socket is in IO * mode, if there is a card in the socket, and if there * is a PRR. * We don't have to clear revent before we call the * cs_read_event_status function since it will * clear it before adding any current events. */ 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_CARD_INSERTED */ client = client->next; } /* while (client) */ mutex_exit(&sp->lock); mutex_exit(&sp->cis_lock); } /* if (CS_EVENT_STATUS_CHANGE) */ /* * We want to maintain the required event dispatching order as * specified in the PCMCIA spec, so we cycle through all * clients on this socket to make sure that they are * notified in the correct order. */ ct = &client_types[0]; while (ct) { /* * Point to the head of the client list for this socket, and go * through each client to set up the client events as well * as call the client's event handler directly if we have * a high priority event that we need to tell the client * about. */ 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)) { /* * Clients always receive registration complete * events, even if there is no card of * their type currently in the socket. */ case CS_EVENT_REGISTRATION_COMPLETE: CLIENT_EVENT_CALLBACK(client, event, CS_EVENT_PRI_LOW); break; /* * The client only gets a card insertion event * if there is currently a card in the * socket that the client can control. * The nexus determines this. We also * prevent the client from receiving * multiple CS_EVENT_CARD_INSERTION * events without receiving intervening * CS_EVENT_CARD_REMOVAL events. */ 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); } /* if (!CLIENT_SENT_INSERTION) */ } break; /* * The CS_EVENT_CARD_REMOVAL_LOWP is a low * priority CS_EVENT_CARD_REMOVAL event. */ 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; /* * The hardware card removal events are handed * to the client in cs_event at high * priority interrupt time; this card * removal event is a software-generated * event. */ 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; /* * Write protect events require the info field * of the client's event callback args to * be zero if the card is not write * protected and one if it is. */ 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); } /* CLIENT_CARD_INSERTED */ 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; } /* switch */ mutex_enter(&sp->lock); CS_BIT_CLEAR(client->events, bit); mutex_exit(&sp->lock); bit++; } /* while (client->events) */ } /* if (ct->type) */ if (ct->order & CLIENT_EVENTS_LIFO) { client = client->prev; } else { client = client->next; } } /* while (client) */ ct = ct->next; } /* while (ct) */ /* * Handle CS_EVENT_CARD_REMOVAL events */ 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 (CS_EVENT_CARD_REMOVAL) */ /* * If someone is waiting for us to complete, signal them now. */ 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); } /* SOCKET_WAIT_SYNC */ } /* for (;;) */ } /* * cs_card_for_client - checks to see if a card that the client can control * is currently inserted in the socket. Socket Services * has to tell us if this is the case. */ static int cs_card_for_client(client_t *client) { /* * If the client has set the CS_EVENT_ALL_CLIENTS it means that they * want to get all events for all clients, irrespective of * whether or not there is a card in the socket. Such clients * have to be very careful if they touch the card hardware in * any way to prevent causing problems for other clients on the * same socket. This flag will typically only be set by the * "super-client" or CSI types of clients that wish to get * information on other clients or cards in the system. * Note that the CS_EVENT_ALL_CLIENTS must be set in either the * client's global event mask or client event mask. * The client must also have registered as a "super-client" or as a * CSI client for this socket. */ if ((client->flags & (CLIENT_SUPER_CLIENT | CLIENT_CSI_CLIENT)) && ((client->global_mask | client->event_mask) & CS_EVENT_ALL_CLIENTS)) return (1); /* * Look for the PCM_DEV_ACTIVE property on this client's dip; if * it's found, it means that this client can control the card * that is currently in the socket. This is a boolean * property managed by Socket Services. */ 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); } /* * cs_ss_thread - This is the Socket Services work thread. We fire off * any calls to Socket Services here that we want * to run on a thread that is seperate from the * per-socket event thread. */ static void cs_ss_thread(uint32_t sn) { cs_socket_t *sp; if ((sp = cs_get_sp(sn)) == NULL) return; /* * Tell CPR that we've started a new thread. */ 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); /* * Check to see if there are any special thread operations * that we are being asked to perform. */ 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); /* wake up cs_deinit */ mutex_exit(&sp->ss_thread_lock); return; } /* if (SOCKET_THREAD_EXIT) */ #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 /* * Call SocketServices(CSCISInit) to have SS parse the * CIS and load/attach any client drivers necessary. */ 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 /* * Tell SS that we have a complete CIS and that it can now * be parsed. * Note that in some cases the client driver may block in * their attach routine, causing this call to block until * the client completes their attach. */ SocketServices(CSCISInit, sp->socket_num); /* * Set the CS_EVENT_SS_UPDATED event for this socket so that the * event thread can continue any card insertion processing * that it has to do. */ mutex_enter(&sp->lock); sp->events |= CS_EVENT_SS_UPDATED; mutex_exit(&sp->lock); /* * Wake up this socket's event thread so that clients can * continue any card insertion or attach processing * that they need to do. */ cv_broadcast(&sp->thread_cv); } /* if ST_CSCISInit */ } /* for (;;) */ } /* * cs_request_socket_mask - set the client's event mask as well as causes * any events pending from RegisterClient to * be scheduled to be sent to the client */ 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; /* * Check to see if this is the Socket Services client handle; if it * is, we don't do anything except for return success. */ if (CLIENT_HANDLE_IS_SS(client_handle)) return (CS_SUCCESS); /* * Get a pointer to this client's socket structure. */ if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL) return (CS_BAD_SOCKET); EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp); /* * Make sure that this is a valid client handle. */ if (!(client = cs_find_client(client_handle, &error))) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (error); } mutex_enter(&sp->lock); /* * If this client has already done a RequestSocketMask without * a corresponding ReleaseSocketMask, then return an error. */ if (client->flags & REQ_SOCKET_MASK_DONE) { mutex_exit(&sp->lock); EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_IN_USE); } /* * Set up the event mask information; we copy this directly from * the client; since we are the only source of events, any * bogus bits that the client puts in here won't matter * because we'll never look at them. */ client->event_mask = se->EventMask; /* * If RegisterClient left us some events to process, set these * events up here. */ 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; /* * Merge all the clients' event masks and set the socket * to generate the appropriate events. */ (void) cs_set_socket_event_mask(sp, cs_merge_event_masks(sp, client)); mutex_exit(&sp->lock); /* * Wakeup the event thread if there are any client events to process. */ 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); } /* * cs_release_socket_mask - clear the client's event mask * * Once this function returns, the client is guaranteed * not to get any more event callbacks. */ /*ARGSUSED*/ 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; /* * Check to see if this is the Socket Services client handle; if it * is, we don't do anything except for return success. */ if (CLIENT_HANDLE_IS_SS(client_handle)) return (CS_SUCCESS); /* * Get a pointer to this client's socket structure. */ if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL) return (CS_BAD_SOCKET); EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp); /* * Make sure that this is a valid client handle. */ if (!(client = cs_find_client(client_handle, &error))) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (error); } mutex_enter(&sp->lock); /* * If this client has already done a RequestSocketMask without * a corresponding ReleaseSocketMask, then return an error. */ if (!(client->flags & REQ_SOCKET_MASK_DONE)) { mutex_exit(&sp->lock); EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_BAD_SOCKET); } /* * Clear both the client event mask and the global event mask. * We clear both since the semantics of this function are * that once it returns, the client will not be called at * it's event handler for any events until RequestSocketMask * is called again. */ client->event_mask = 0; client->global_mask = 0; client->flags &= ~REQ_SOCKET_MASK_DONE; /* * Merge all the clients' event masks and set the socket * to generate the appropriate events. */ (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); } /* * cs_get_event_mask - return the event mask for this client */ 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; /* * Check to see if this is the Socket Services client handle; if it * is, we don't do anything except for return success. */ if (CLIENT_HANDLE_IS_SS(client_handle)) return (CS_SUCCESS); /* * Get a pointer to this client's socket structure. */ if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL) return (CS_BAD_SOCKET); EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp); /* * Make sure that this is a valid client handle. */ 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 there's no card in the socket or the card in the socket is not * for this client, then return an error. * XXX - how can a client get their event masks if their card * goes away? */ if (!(client->flags & CLIENT_CARD_INSERTED)) { mutex_exit(&sp->lock); EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_NO_CARD); } #endif /* * We are only allowed to get the client event mask if a * RequestSocketMask has been called previously. We * are allowed to get the global event mask at any * time. * The global event mask is initially set by the client * in the call to RegisterClient. The client event * mask is set by the client in calls to SetEventMask * and RequestSocketMask and gotten in calls to * GetEventMask. */ 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); } /* * cs_set_event_mask - set the event mask for this client */ 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; /* * Check to see if this is the Socket Services client handle; if it * is, we don't do anything except for return success. */ if (CLIENT_HANDLE_IS_SS(client_handle)) return (CS_SUCCESS); /* * Get a pointer to this client's socket structure. */ if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL) return (CS_BAD_SOCKET); EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp); /* * Make sure that this is a valid client handle. */ 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 there's no card in the socket or the card in the socket is not * for this client, then return an error. */ if (!(client->flags & CLIENT_CARD_INSERTED)) { mutex_exit(&sp->lock); EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_NO_CARD); } #endif /* * We are only allowed to set the client event mask if a * RequestSocketMask has been called previously. We * are allowed to set the global event mask at any * time. * The global event mask is initially set by the client * in the call to RegisterClient. The client event * mask is set by the client in calls to SetEventMask * and RequestSocketMask and gotten in calls to * GetEventMask. */ 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; } /* * Merge all the clients' event masks and set the socket * to generate the appropriate events. */ (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); } /* * cs_read_event_status - handles PRR events and returns card status * * calling: *sp - socket struct point * *client - client to check events on * *revent - pointer to event mask to update; if NULL, will * not be updated, if non-NULL, will be updated * with CS-format events; it is NOT necessary * to clear this value before calling this * function * *gs - pointer to a get_ss_status_t used for the SS GetStatus * call; it is not necessary to initialize any * members in this structure; set to NULL if * not used * flags - if CS_RES_IGNORE_NO_CARD is set, the check for a * card present will not be done * * returns: CS_SUCCESS * CS_NO_CARD - if no card is in the socket and the flags arg * is not set to CS_RES_IGNORE_NO_CARD * CS_BAD_SOCKET - if the SS_GetStatus function returned an * error * * Note that if the client that configured this socket has told us that * the READY pin in the PRR isn't valid and the socket is in IO * mode, we always return that the card is READY. * * Note that if gs is not NULL, the current card state will be returned * in the gs->CardState member; this will always reflect the * current card state and the state will come from both the * SS_GetStatus call and the PRR, whichever is appropriate for * the mode that the socket is currently in. */ 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; /* * SOCKET_IS_IO will only be set if a RequestConfiguration * has been done by at least one client on this socket. * If there isn't a card in the socket or the caller wants to ignore * whether the card is in the socket or not, get the current * card status. */ 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; /* * Get a handle to the CIS window */ 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); } /* cs_init_cis_window */ 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 /* * The caller wants the event changes sent back and * the PRR event change bits cleared. */ if (revent) { get_socket_t get_socket; set_socket_t set_socket; /* * Bug ID: 1193636 - Card Services sends bogus * events on CS_EVENT_STATUS_CHANGE events * Clear this before we OR-in any values. */ *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); /* * We now have to reenable the status change interrupts * if there are any valid bits in the PRR. Since * the BVD1 signal becomes the STATUS_CHANGE * signal when the socket is in IO mode, we just * have to set the SBM_BVD1 enable bit in the * event mask. */ 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 (client->pin) */ } /* if (revent) */ } /* if (CONFIG_PINREPL_REG_PRESENT) */ } /* if (SOCKET_IS_IO) */ /* * The caller wants the current card state; we just read * it and return a copy of it but do not clear any of * the event changed bits (if we're reading the PRR). */ 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) { /* * If the socket is in IO mode, then clear the * gs->CardState bits that are now in the PRR */ gs->CardState &= ~(SBM_WP | SBM_BVD1 | SBM_BVD2 | SBM_RDYBSY); /* * Convert PRR status to SS_GetStatus status */ 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 the client has indicated that there is no * PRR or that the READY bit in the PRR isn't * valid, then we simulate the READY bit by * always returning READY. */ 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 } /* if (SOCKET_IS_IO) */ } /* if (gs) */ return (CS_SUCCESS); } /* if (SOCKET_CARD_INSERTED) */ return (CS_NO_CARD); } /* * cs_get_status - gets live card status and latched card status changes * supports the GetStatus CS call * * returns: CS_SUCCESS * CS_BAD_HANDLE if the passed client handle is invalid * * Note: This function resets the latched status values maintained * by Socket Services */ 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; /* * Check to see if this is the Socket Services client handle; if it * is, we don't do anything except for return success. */ if (CLIENT_HANDLE_IS_SS(client_handle)) return (CS_SUCCESS); /* * Get a pointer to this client's socket structure. */ if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL) return (CS_BAD_SOCKET); EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp); /* * Make sure that this is a valid client handle. */ if (!(client = cs_find_client(client_handle, &error))) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (error); } /* * Get the current card status as well as the latched card * state. Set the CS_RES_IGNORE_NO_CARD so that even * if there is no card in the socket we'll still get * a valid status. * Note that it is not necessary to initialize any values * in the get_ss_status structure. */ 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); /* * Assign the "live" card state to the "real" card state. If there's * no card in the socket or the card in the socket is not * for this client, then we lie and tell the caller that the * card is not inserted. */ 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; /* XXX (is ~0 correct here?) reset latched values */ set_socket.State = (unsigned)~0; if (SocketServices(SS_SetSocket, &set_socket) != SUCCESS) return (CS_BAD_SOCKET); return (CS_SUCCESS); } /* * cs_cse2sbm - converts a CS event mask to an SS (SBM_XXX) event mask */ static event_t cs_cse2sbm(event_t event_mask) { event_t sbm_event = 0; /* * XXX - we need to handle PM_CHANGE and RESET here as well */ 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); } /* * cs_sbm2cse - converts SBM_xxx state to CS event bits * * This function should never set any of the following bits: * * CS_EVENT_MTD_REQUEST * CS_EVENT_CLIENT_INFO * CS_EVENT_TIMER_EXPIRED * CS_EVENT_CARD_REMOVAL * CS_EVENT_CARD_REMOVAL_LOWP * CS_EVENT_ALL_CLIENTS * CS_EVENT_READY_TIMEOUT * * These bits are defined in the CS_STATUS_XXX series and are * used by GetStatus. */ static uint32_t cs_sbm2cse(uint32_t state) { uint32_t rstate = 0; /* * XXX - we need to handle PM_CHANGE and RESET here as well */ 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); } /* * cs_merge_event_masks - merge the CS global socket event mask with the * passed client's event masks */ static unsigned cs_merge_event_masks(cs_socket_t *sp, client_t *client) { unsigned SCIntMask; uint32_t event_mask; /* * We always want to see card detect and status change events. */ 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 the socket is in IO mode and there is a PRR present, * then we may need to enable PCE_CARD_STATUS_CHANGE * events. */ 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; } /* if (CONFIG_PINREPL_REG_PRESENT) */ } /* if (!SOCKET_IS_IO) */ return (SCIntMask); } /* * cs_set_socket_event_mask - set the event mask for the socket */ 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; /* XXX (is ~0 correct here?) reset latched values */ set_socket.State = (unsigned)~0; if (SocketServices(SS_SetSocket, &set_socket) != SUCCESS) return (CS_BAD_SOCKET); return (CS_SUCCESS); } /* * ==== MTD handling section ==== */ 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); } /* * ==== memory window handling section ==== */ /* * cs_request_window - searches through window list for the socket to find a * memory window that matches the requested criteria; * this is RequestWindow * * calling: cs_request_window(client_handle_t, *window_handle_t, win_req_t *) * * On sucessful return, the window_handle_t * pointed to will * contain a valid window handle for this window. * * returns: CS_SUCCESS - if window found * CS_OUT_OF_RESOURCE - if no windows match requirements * CS_BAD_HANDLE - client handle is invalid * CS_BAD_SIZE - if requested size can not be met * CS_BAD_WINDOW - if an internal error occured * CS_UNSUPPORTED_FUNCTION - if SS is trying to call us * CS_NO_CARD - if no card is in socket * CS_BAD_ATTRIBUTE - if any of the unsupported Attrbute * flags are set */ 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; /* * Check to see if this is the Socket Services client handle; if it * is, we don't support SS using this call. */ if (CLIENT_HANDLE_IS_SS(client_handle)) return (CS_UNSUPPORTED_FUNCTION); /* * Make sure that none of the unsupported flags are set. */ if (rw->Attributes & (/* Compatability */ WIN_PAGED | WIN_SHARED | WIN_FIRST_SHARED | WIN_BINDING_SPECIFIC | /* CS internal */ WIN_DATA_WIDTH_VALID | /* IO window flags */ WIN_MEMORY_TYPE_IO | /* CardBus flags */ WIN_DATA_WIDTH_32 | WIN_PREFETCH_CACHE_MASK | WIN_BAR_MASK)) return (CS_BAD_ATTRIBUTE); mutex_enter(&cs_globals.window_lock); /* * Get a pointer to this client's socket structure. */ if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL) return (CS_BAD_SOCKET); EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp); /* * Make sure that this is a valid client handle. */ 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 there's no card in the socket or the card in the socket is not * for this client, then return an error. */ 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)); /* * See if we can find a window that matches the caller's criteria. * If we can't, then thre's not much more that we can do except * for return an error. */ 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); } /* * We got a window, now synthesize a new window handle for this * client and get a pointer to the global window structs * and assign this window to this client. * We don't have to check for errors from cs_create_window_handle * since that function always returns a valid window handle * if it is given a valid window number. */ *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); } /* * Get any required card offset and pass it back to the client. * This is not defined in the current PCMCIA spec. It is * an aid to clients that want to use it to generate an * optimum card offset. */ 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; /* * Increment the client's memory window count; this is how we know * when a client has any allocated memory windows. */ client->memwin_count++; EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); mutex_exit(&cs_globals.window_lock); return (CS_SUCCESS); } /* * cs_release_window - deallocates the window associated with the passed * window handle; this is ReleaseWindow * * returns: CS_SUCCESS if window handle is valid and window was * sucessfully deallocated * CS_BAD_HANDLE if window handle is invalid or if window * handle is valid but window is not allocated */ 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); } /* * Check to see if this is the Socket Services client handle; if it * is, we don't support SS using this call. */ if (CLIENT_HANDLE_IS_SS(cw->client_handle)) { mutex_exit(&cs_globals.window_lock); return (CS_UNSUPPORTED_FUNCTION); } /* * Get a pointer to this client's socket structure. */ if ((sp = cs_get_sp(GET_CLIENT_SOCKET(cw->client_handle))) == NULL) return (CS_BAD_SOCKET); EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp); /* * Make sure that this is a valid client handle. */ 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); } /* * Mark this window as not in use anymore. */ cw->state &= ~CW_WIN_IN_USE; /* * Decrement the client's memory window count; this is how we know * when a client has any allocated memory windows. */ 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); } /* * cs_modify_window - modifies a window's characteristics; this is ModifyWindow */ 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); /* * Do some sanity checking - make sure that we can find a pointer * to the window structure, and if we can, get the client that * has allocated that window. */ if (!(cw = cs_find_window(wh))) { mutex_exit(&cs_globals.window_lock); return (CS_BAD_HANDLE); } /* * Get a pointer to this client's socket structure. */ 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 there's no card in the socket or the card in the socket is not * for this client, then return an error. */ 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); } /* * cs_modify_mem_window - modifies a window's characteristics; used internally * by Card Services * * If *wr is NULL, it means that we're being called by ModifyWindow * If *wr is non-NULL, it means that we are being called by RequestWindow * and so we can't use SS_GetWindow. */ 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 the win_req_t struct pointer is NULL, it means that * we're being called by ModifyWindow, so get the * current window characteristics. */ 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 we're being called by RequestWindow, we must always have * WIN_ACCESS_SPEED_VALID set since get_window_t is not * defined. */ 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); /* * Return the current base address of this window */ 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); } /* * cs_map_mem_page - sets the card offset of the mapped window */ 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; /* * We don't support paged windows, so never allow a page number * of other than 0 */ if (mmp->Page) return (CS_BAD_PAGE); mutex_enter(&cs_globals.window_lock); /* * Do some sanity checking - make sure that we can find a pointer * to the window structure, and if we can, get the client that * has allocated that window. */ if (!(cw = cs_find_window(wh))) { mutex_exit(&cs_globals.window_lock); return (CS_BAD_HANDLE); } /* * Get a pointer to this client's socket structure. */ 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 there's no card in the socket or the card in the socket is not * for this client, then return an error. */ 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_find_window - finds the window associated with the passed window * handle; if the window handle is invalid or no * windows match the passed window handle, NULL * is returned. Note that the window must be * allocated for this function to return a valid * window pointer. * * returns: cs_window_t * pointer to the found window * NULL if window handle invalid or window not allocated */ 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); } /* * cs_create_window_handle - creates a unique window handle based on the * passed window number. */ static window_handle_t cs_create_window_handle(uint32_t aw) { return (WINDOW_HANDLE_MAGIC | (aw & WINDOW_HANDLE_MASK)); } /* * cs_find_mem_window - tries to find a memory window matching the caller's * criteria * * We return the first window that matches the requested criteria. * * returns: CS_SUCCESS - if memory window found * CS_OUT_OF_RESOURCE - if no windows match requirements * CS_BAD_SIZE - if requested size can not be met * CS_BAD_WINDOW - if an internal error occured */ /* BEGIN CSTYLED */ 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 we can't get a pointer to this window, we should contine * with scanning the next window, since this window might have * been dropped. */ 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 { /* WC_SIZE */ 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; } /* for (tws) */ } else { for (tws = MinSize; tws <= MaxSize; tws += ReqGran) { if (rw->Size == tws) { min_size = tws; window_num = wn; goto found_window; } } /* for (tws) */ } /* if (!WC_POW2) */ } /* if (Size >= MinSize) */ } /* if (!ReqGran) */ } /* if (WC_SIZE) */ } /* if (rw->Size) */ } /* if (cs_space_and_map_ok) */ } /* if (cs_valid_window_speed) */ } /* if (WINDOW_FOR_SOCKET) */ } /* if (cs_get_wp) */ } /* for (wn) */ /* * If we got here and the window_num wasn't set by any window * matches in the above code, it means that we didn't * find a window matching the caller's criteria. * If the error is CS_BAD_TYPE, it means that the last reason * that we couldn't match a window was because the caller's * requested speed was out of range of the last window that * we checked. We convert this error code to CS_OUT_OF_RESOURCE * to conform to the RequestWindow section of the PCMCIA * Card Services spec. */ 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); } /* END CSTYLED */ /* * cs_memwin_space_and_map_ok - checks to see if the passed window mapping * capabilities and window speeds are in the * range of the passed window. * * returns: 0 - if the capabilities are out of range * 1 - if the capabilities are in range */ 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); } /* * cs_valid_window_speed - checks to see if requested window speed * is in range of passed window * * The inquire_window_t struct gives us speeds in nS, and we * get speeds in the AccessSpeed variable as a devspeed code. * * returns: CS_BAD_SPEED - if AccessSpeed is invalid devspeed code * CS_BAD_TYPE - if AccessSpeed is not in range of valid * speed for this window * CS_SUCCESS - if window speed is in range */ 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); } /* * ==== IO window handling section ==== */ /* * cs_request_io - provides IO resources for clients; this is RequestIO * * calling: cs_request_io(client_handle_t, io_req_t *) * * returns: CS_SUCCESS - if IO resources available for client * CS_OUT_OF_RESOURCE - if no windows match requirements * CS_BAD_HANDLE - client handle is invalid * CS_UNSUPPORTED_FUNCTION - if SS is trying to call us * CS_NO_CARD - if no card is in socket * CS_BAD_ATTRIBUTE - if any of the unsupported Attribute * flags are set * CS_BAD_BASE - if either or both base port addresses * are invalid or out of range * CS_CONFIGURATION_LOCKED - a RequestConfiguration has * already been done * CS_IN_USE - IO ports already in use or function has * already been called * CS_BAD_WINDOW - if failure while trying to set window * characteristics */ 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; /* * Check to see if this is the Socket Services client handle; if it * is, we don't support SS using this call. */ if (CLIENT_HANDLE_IS_SS(client_handle)) return (CS_UNSUPPORTED_FUNCTION); /* * If the client has only requested one IO range, then make sure * that the Attributes2 filed is clear. */ if (!ior->NumPorts2) ior->Attributes2 = 0; /* * Make sure that none of the unsupported or reserved flags are set. */ if ((ior->Attributes1 | ior->Attributes2) & (IO_SHARED | IO_FIRST_SHARED | IO_FORCE_ALIAS_ACCESS | IO_DEALLOCATE_WINDOW | IO_DISABLE_WINDOW)) return (CS_BAD_ATTRIBUTE); /* * Make sure that we have a port count for the first region. */ if (!ior->NumPorts1) return (CS_BAD_BASE); /* * If we're being asked for multiple IO ranges, then both base port * members must be non-zero. */ if ((ior->NumPorts2) && !(ior->BasePort1.base && ior->BasePort2.base)) return (CS_BAD_BASE); mutex_enter(&cs_globals.window_lock); /* * Get a pointer to this client's socket structure. */ if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL) return (CS_BAD_SOCKET); EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp); /* * Make sure that this is a valid client handle. */ 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 RequestConfiguration has already been done, we don't allow * this call. */ 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 RequestIO has already been done, we don't allow this call. */ 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 there's no card in the socket or the card in the socket is not * for this client, then return an error. */ 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 we're only being asked for one IO range, then set BasePort2 to * zero, since we use it later on. */ if (!ior->NumPorts2) ior->BasePort2.base = 0; /* * See if we can allow Card Services to select the base address * value for this card; if the client has specified a non-zero * base IO address but the card doesn't decode enough IO * address lines to uniquely use that address, then we have * the flexibility to choose an alternative base address. * Note that if the client specifies that the card decodes zero * IO address lines, then we have to use the NumPortsX * values to figure out how many address lines the card * actually decodes, and we have to round the NumPortsX * values up to the closest power of two. */ 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 /* * Here is where the code diverges, depending on the type of IO windows * that this socket supports. If this socket supportes memory * mapped IO windows, as determined by cs_init allocating an * io_mmap_window_t structure on the socket structure, then we * use one IO window for all the clients on this socket. We can * do this safely since a memory mapped IO window implies that * only this socket shares the complete IO space of the card. * See the next major block of code for a description of what we do * if a socket doesn't support memory mapped IO windows. */ if (sp->io_mmap_window) { cs_window_t *cw; io_mmap_window_t *imw = sp->io_mmap_window; uint32_t offset; /* * If we haven't allocated an IO window yet, do it now. * Try to allocate the IO window that cs_init found for us; * if that fails, then call cs_find_io_win to find a window. */ 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); } /* cs_find_io_win */ } /* if (!WINDOW_AVAILABLE_FOR_IO) */ 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); /* XXX - what to d here? XXX */ 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; /* * Check the caller's port requirements to be sure that they * fit within our found IO window. */ 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); } /* if (!imw->count) */ imw->count++; /* * All common access handles for this type of adapter are * duped. We never give the original back to the caller. */ /* XXX need to set endianess and data ordering flags */ 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) { /* XXX need to set endianess and data ordering flags */ csx_DupHandle(imw->handle, &ior->BasePort2.handle, 0); csx_GetHandleOffset(ior->BasePort2.handle, &offset); csx_SetHandleOffset(ior->BasePort2.handle, ior->BasePort1.base + offset); } /* * We don't really use these two values if we've got a memory * mapped IO window since the assigned window number is stored * in imw->number. */ client->io_alloc.Window1 = imw->number; client->io_alloc.Window2 = PCMCIA_MAX_WINDOWS; /* * This socket supports only IO port IO windows. */ } else { #else /* USE_IOMMAP_WINDOW */ { #endif /* USE_IOMMAP_WINDOW */ 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 (cs_allocate_io_win(1)) */ /* * Setup the window hardware; if this fails, then we need to * deallocate the previously allocated window. */ 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); } /* if (cs_setup_io_win(1)) */ ior->BasePort1.handle = (acc_handle_t)baseaddru.handle; ior->BasePort1.base = baseaddru.base; /* * See if the client wants two IO ranges. */ if (ior->NumPorts2) { baseaddru_t baseaddru; baseaddru.base = ior->BasePort2.base; /* * If we fail to allocate this window, then we must deallocate * the previous IO window that is already allocated. */ 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 (cs_allocate_io_win(2)) */ /* * Setup the window hardware; if this fails, then we need to * deallocate the previously allocated window. */ 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); } /* if (cs_setup_io_win(2)) */ ior->BasePort2.handle = (acc_handle_t)baseaddru.handle; ior->BasePort2.base = baseaddru.base; } else { client->io_alloc.Window2 = PCMCIA_MAX_WINDOWS; } /* if (ior->NumPorts2) */ } /* if (sp->io_mmap_window) */ /* * Save a copy of the client's port information so that we * can use it in the RequestConfiguration call. We set * the IO window number(s) allocated in the respective * section of code, above. */ 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; /* * Mark this client as having done a successful RequestIO call. */ 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); } /* * cs_release_io - releases IO resources allocated by RequestIO; this is * ReleaseIO * * calling: cs_release_io(client_handle_t, io_req_t *) * * returns: CS_SUCCESS - if IO resources sucessfully deallocated * CS_BAD_HANDLE - client handle is invalid * CS_UNSUPPORTED_FUNCTION - if SS is trying to call us * CS_CONFIGURATION_LOCKED - a RequestConfiguration has been * done without a ReleaseConfiguration * CS_IN_USE - no RequestIO has been done */ 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 /* * Check to see if this is the Socket Services client handle; if it * is, we don't support SS using this call. */ if (CLIENT_HANDLE_IS_SS(client_handle)) return (CS_UNSUPPORTED_FUNCTION); mutex_enter(&cs_globals.window_lock); /* * Get a pointer to this client's socket structure. */ if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL) return (CS_BAD_SOCKET); EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp); /* * Make sure that this is a valid client handle. */ 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 RequestConfiguration has already been done, we don't allow * this call. */ 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 RequestIO has not been done, we don't allow this call. */ 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 /* * Check the passed IO allocation with the stored allocation; if * they don't match, then return an error. */ 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 /* * The code diverges here depending on if this socket supports * memory mapped IO windows or not. See comments in the * cs_request_io function for a description of what's * going on here. */ if (sp->io_mmap_window) { io_mmap_window_t *imw = sp->io_mmap_window; /* * We should never see this; if we do, it's an internal * consistency error. */ 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); } /* * All common access handles for this type of adapter are * duped. We never give the original back to the caller, * so it's OK to unconditionally free the handle here. */ csx_FreeHandle(&ior->BasePort1.handle); /* * If the IO window referance count is zero, then deallocate * and disable this window. */ if (!--(imw->count)) { (void) cs_setup_io_win(socket_num, imw->number, NULL, NULL, NULL, ( IO_DEALLOCATE_WINDOW | IO_DISABLE_WINDOW)); } /* if (imw->count) */ } else { #endif /* USE_IOMMAP_WINDOW */ (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 } /* if (sp->io_mmap_window) */ #endif /* USE_IOMMAP_WINDOW */ /* * Mark the client as not having any IO resources allocated. */ 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); } /* * cs_find_io_win - finds an IO window that matches the parameters specified * in the flags argument * * calling: sn - socket number to look for IO window on * *iwc - other window characteristics to match * *assigned_window - pointer to where we return the assigned * window number if we found a window or * undefined otherwise * *size - if non-NULL, the found window size will be stored here * * returns: CS_SUCCESS - if IO window found * CS_OUT_OF_RESOURCE - if no windows match requirements */ 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); } /* if (WINDOW_FOR_SOCKET) */ } /* cs_get_wp */ } /* for (wn) */ return (CS_OUT_OF_RESOURCE); } /* * cs_allocate_io_win - finds and allocates an IO window * * calling: sn - socket number to look for window on * Attributes - window attributes in io_req_t.Attributes format * *assigned_window - pointer to return assigned window number * * returns: CS_SUCCESS - IO window found and allocated * CS_OUT_OF_RESOURCE - if cs_find_io_win couldn't find a * window that matches the passed criteria * * Note: This fucntion will find and allocate an IO window. The caller is * responsible for deallocating the window. */ 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); } /* * cs_setup_io_win - setup and destroy an IO window * * calling: sn - socket number * wn - window number * XXX Base - pointer to XXX * *NumPorts - pointer to number of allocated ports to return * IOAddrLines - number of IO address lines decoded by this card * Attributes - either io_req_t attributes, or a combination of * the following flags: * IO_DEALLOCATE_WINDOW - deallocate the window * IO_DISABLE_WINDOW - disable the window * When either of these two flags are set, *Base * and NumPorts should be NULL. * * returns: CS_SUCCESS - if no failure * CS_BAD_WINDOW - if error while trying to configure window * * Note: We use the IOAddrLines value to determine what base address to pass * to Socket Services. */ 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; } /* IO_DEALLOCATE_WINDOW */ 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); } /* IO_DISABLE_WINDOW */ return (CS_SUCCESS); } /* if (IO_DEALLOCATE_WINDOW | IO_DISABLE_WINDOW) */ /* * See if we can allow Socket Services to select the base address * value for this card; if the client has specified a non-zero * base IO address but the card doesn't decode enough IO * address lines to uniquely use that address, then we have * the flexibility to choose an alternative base address. * XXX - Is this really correct in all cases? */ 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); } /* * ==== IRQ handling functions ==== */ /* * cs_request_irq - add's client's IRQ handler; supports RequestIRQ * * calling: irq_req_t.Attributes - must have the IRQ_TYPE_EXCLUSIVE * flag set, and all other flags clear, or * CS_BAD_ATTRIBUTE will be returned * * returns: CS_SUCCESS - if IRQ resources available for client * CS_BAD_IRQ - if IRQ can not be allocated * CS_BAD_HANDLE - client handle is invalid * CS_UNSUPPORTED_FUNCTION - if SS is trying to call us * CS_NO_CARD - if no card is in socket * CS_BAD_ATTRIBUTE - if any of the unsupported Attribute * flags are set * CS_CONFIGURATION_LOCKED - a RequestConfiguration has * already been done * CS_IN_USE - IRQ ports already in use or function has * already been called * * Note: We only allow level-mode interrupts. */ 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; /* * Check to see if this is the Socket Services client handle; if it * is, we don't support SS using this call. */ if (CLIENT_HANDLE_IS_SS(client_handle)) return (CS_UNSUPPORTED_FUNCTION); /* * Make sure that none of the unsupported or reserved flags are set. */ 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); /* * Get a pointer to this client's socket structure. */ if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL) return (CS_BAD_SOCKET); EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp); /* * Make sure that this is a valid client handle. */ if (!(client = cs_find_client(client_handle, &error))) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (error); } /* * If RequestConfiguration has already been done, we don't allow * this call. */ if (client->flags & REQ_CONFIGURATION_DONE) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_CONFIGURATION_LOCKED); } /* * If RequestIRQ has already been done, we don't allow this call. */ if (client->flags & REQ_IRQ_DONE) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_IN_USE); } /* * If there's no card in the socket or the card in the socket is not * for this client, then return an error. */ if (!(client->flags & CLIENT_CARD_INSERTED)) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_NO_CARD); } /* * Set up the parameters and ask Socket Services to give us an IRQ * for this client. We don't really do much, since the IRQ * resources are managed by SS and the kernel. We also don't * care which IRQ level we are given. */ 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; /* * Save the allocated IRQ information for this client. */ 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 /* * Mark this client as having done a successful RequestIRQ call. */ client->flags |= (REQ_IRQ_DONE | CLIENT_IRQ_ALLOCATED); EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_SUCCESS); } /* * cs_release_irq - releases IRQ resources allocated by RequestIRQ; this is * ReleaseIRQ * * calling: cs_release_irq(client_handle_t, irq_req_t *) * * returns: CS_SUCCESS - if IRQ resources sucessfully deallocated * CS_BAD_IRQ - if IRQ can not be deallocated * CS_BAD_HANDLE - client handle is invalid * CS_UNSUPPORTED_FUNCTION - if SS is trying to call us * CS_CONFIGURATION_LOCKED - a RequestConfiguration has been * done without a ReleaseConfiguration * CS_IN_USE - no RequestIRQ has been done */ 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 /* * Check to see if this is the Socket Services client handle; if it * is, we don't support SS using this call. */ if (CLIENT_HANDLE_IS_SS(client_handle)) return (CS_UNSUPPORTED_FUNCTION); /* * Get a pointer to this client's socket structure. */ if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL) return (CS_BAD_SOCKET); EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp); /* * Make sure that this is a valid client handle. */ if (!(client = cs_find_client(client_handle, &error))) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (error); } /* * If RequestConfiguration has already been done, we don't allow * this call. */ if (client->flags & REQ_CONFIGURATION_DONE) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_CONFIGURATION_LOCKED); } /* * If RequestIRQ has not been done, we don't allow this call. */ if (!(client->flags & REQ_IRQ_DONE)) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_IN_USE); } /* * Tell Socket Services that we want to deregister this client's * IRQ handler. */ 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; /* * At this point, we should never fail this SS call; if we do, it * means that there is an internal consistancy error in either * Card Services or Socket Services. */ if ((error = SocketServices(SS_ClearIRQHandler, &clear_irq_handler)) != SUCCESS) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_BAD_IRQ); } /* * Mark the client as not having any IRQ resources allocated. */ client->flags &= ~(REQ_IRQ_DONE | CLIENT_IRQ_ALLOCATED); EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_SUCCESS); } /* * ==== configuration handling functions ==== */ /* * cs_request_configuration - sets up socket and card configuration on behalf * of the client; this is RequestConfiguration * * returns: CS_SUCCESS - if configuration sucessfully set * CS_BAD_SOCKET - if Socket Services returns an error * CS_UNSUPPORTED_FUNCTION - if SS is trying to call us * CS_BAD_ATTRIBUTE - if any unsupported or reserved flags * are set * CS_BAD_TYPE - if the socket doesn't support a mem and IO * interface (SOCKET_INTERFACE_MEMORY_AND_IO set) * CS_CONFIGURATION_LOCKED - a RequestConfiguration has * already been done * CS_BAD_VCC - if Vcc value is not supported by socket * CS_BAD_VPP1 - if Vpp1 value is not supported by socket * CS_BAD_VPP2 - if Vpp2 value is not supported by socket * * Bug ID: 1193637 - Card Services RequestConfiguration does not conform * to PCMCIA standard * We allow clients to do a RequestConfiguration even if they haven't * done a RequestIO or RequestIRQ. */ 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; /* * Check to see if this is the Socket Services client handle; if it * is, we don't support SS using this call. */ if (CLIENT_HANDLE_IS_SS(client_handle)) return (CS_UNSUPPORTED_FUNCTION); #ifdef XXX /* * If the client specifies Vcc = 0 and any non-zero value for * either of the Vpp members, that's an illegal condition. */ if (!(cr->Vcc) && (cr->Vpp1 || cr->Vpp2)) return (CS_BAD_VCC); #endif /* * Get a pointer to this client's socket structure. */ if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL) return (CS_BAD_SOCKET); /* * If the client is asking for a memory and IO interface on this * socket, then check the socket capabilities to be sure that * this socket supports this configuration. */ 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); } /* if (SOCKET_INTERFACE_MEMORY_AND_IO) */ EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp); /* * Make sure that this is a valid client handle. */ if (!(client = cs_find_client(client_handle, &error))) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (error); } /* * If RequestConfiguration has already been done, we don't allow * this call. */ if (client->flags & REQ_CONFIGURATION_DONE) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_CONFIGURATION_LOCKED); } /* * If there's no card in the socket or the card in the socket is not * for this client, then return an error. */ if (!(client->flags & CLIENT_CARD_INSERTED)) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_NO_CARD); } /* * At this point, most of the client's calling parameters have been * validated, so we can go ahead and configure the socket and * the card. */ mutex_enter(&sp->cis_lock); /* * Configure the socket with the interface type and voltages requested * by the client. */ 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; /* don't reset latched values */ 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; /* * The Cirrus Logic PD6710/672X/others? adapters will write * protect the CIS if the socket is in MEMORY mode and the * WP/IOCS16 pin is true. When this happens, the CIS registers * will fail to be written. Go ahead and set the socket, * even though the event mask isn't complete yet, so we can * configure the adapter. Afterwards, set the socket again * to make sure the event mask is correct. */ 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); /* * Get a pointer to a window that contains the configuration * registers. */ 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); } /* * Setup the config register pointers. * Note that these pointers are not the complete virtual address; * the complete address is constructed each time the registers * are accessed. */ mutex_enter(&sp->lock); crt = &client->config_regs; client->present = cr->Present; bzero((char *)crt, sizeof (config_regs_t)); /* Configuration Option Register */ if (client->present & CONFIG_OPTION_REG_PRESENT) crt->cor_p = (newoffset + CONFIG_OPTION_REG_OFFSET); /* Configuration and Status Register */ if (client->present & CONFIG_STATUS_REG_PRESENT) crt->ccsr_p = (newoffset + CONFIG_STATUS_REG_OFFSET); /* Pin Replacement Register */ if (client->present & CONFIG_PINREPL_REG_PRESENT) crt->prr_p = (newoffset + CONFIG_PINREPL_REG_OFFSET); /* Socket and Copy Register */ if (client->present & CONFIG_COPY_REG_PRESENT) crt->scr_p = (newoffset + CONFIG_COPY_REG_OFFSET); /* Extended Status Register */ if (client->present & CONFIG_EXSTAT_REG_PRESENT) crt->exstat_p = (newoffset + CONFIG_EXSTAT_REG_OFFSET); /* IO Base 0 Register */ if (client->present & CONFIG_IOBASE0_REG_PRESENT) crt->iobase0_p = (newoffset + CONFIG_IOBASE0_REG_OFFSET); /* IO Base 1 Register */ if (client->present & CONFIG_IOBASE1_REG_PRESENT) crt->iobase1_p = (newoffset + CONFIG_IOBASE1_REG_OFFSET); /* IO Base 2 Register */ if (client->present & CONFIG_IOBASE2_REG_PRESENT) crt->iobase2_p = (newoffset + CONFIG_IOBASE2_REG_OFFSET); /* IO Base 3 Register */ if (client->present & CONFIG_IOBASE3_REG_PRESENT) crt->iobase3_p = (newoffset + CONFIG_IOBASE3_REG_OFFSET); /* IO Limit Register */ if (client->present & CONFIG_IOLIMIT_REG_PRESENT) crt->iolimit_p = (newoffset + CONFIG_IOLIMIT_REG_OFFSET); /* * Setup the bits in the PRR mask that are valid; this is easy, just * copy the Pin value that the client gave us. Note that for * this to work, the client must set both of the XXX_STATUS * and the XXX_EVENT bits in the Pin member. */ 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 the socket isn't in IO mode, WP is asserted, and we're going to * write any of the config registers, issue a warning. */ 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"); } /* * Write any configuration registers that the client tells us are * present to the card; save a copy of what we wrote so that we * can return them if the client calls GetConfigurationInfo. * The order in which we write the configuration registers is * specified by the PCMCIA spec; we must write the socket/copy * register first (if it exists), and then we can write the * registers in any arbitrary order. */ /* Socket and Copy Register */ if (client->present & CONFIG_COPY_REG_PRESENT) { crt->scr = cr->Copy; csx_Put8(cis_handle, crt->scr_p, crt->scr); } /* Pin Replacement Register */ if (client->present & CONFIG_PINREPL_REG_PRESENT) { crt->prr = cr->Pin; csx_Put8(cis_handle, crt->prr_p, crt->prr); } /* Configuration and Status Register */ /* XXX should we set CCSR_SIG_CHG in the CCSR? XXX */ if (client->present & CONFIG_STATUS_REG_PRESENT) { crt->ccsr = cr->Status; csx_Put8(cis_handle, crt->ccsr_p, crt->ccsr); } /* Extended Status Register */ if (client->present & CONFIG_EXSTAT_REG_PRESENT) { crt->exstat = cr->ExtendedStatus; csx_Put8(cis_handle, crt->exstat_p, crt->exstat); } /* * If any IO base and limit registers exist, and this client * has done a RequestIO, setup the IO Base and IO Limit * registers. */ 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); } /* CONFIG_IOBASE0_REG_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); } /* for */ csx_Put8(cis_handle, crt->iolimit_p, limit); } /* CONFIG_IOLIMIT_REG_PRESENT */ } /* REQ_IO_DONE */ /* * Mark the socket as being in IO mode. */ if (cr->IntType & SOCKET_INTERFACE_MEMORY_AND_IO) sp->flags |= SOCKET_IS_IO; mutex_exit(&sp->lock); /* * Enable the interrupt if needed */ if (cr->Attributes & CONF_ENABLE_IRQ_STEERING) set_socket.IREQRouting |= IRQ_ENABLE; /* * Now that we know if the PRR is present and if it is, which * bits in the PRR are valid, we can construct the correct * socket event mask. */ set_socket.SCIntMask = cs_merge_event_masks(sp, client); /* * Configuration Option Register - we handle this specially since * we don't allow the client to manipulate the RESET or * INTERRUPT bits (although a client can manipulate these * bits via an AccessConfigurationRegister call - explain * THAT logic to me). * XXX - we force level-mode interrupts (COR_LEVEL_IRQ) * XXX - we always enable the function on a multi-function card */ 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; } /* CW_MULTI_FUNCTION_CIS */ #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); } /* CONFIG_OPTION_REG_PRESENT */ if (cs_rc1_delay) drv_usecwait(cs_rc1_delay * 1000); /* * Set the socket to the parameters that the client requested. */ if (SocketServices(SS_SetSocket, &set_socket) != SUCCESS) { if (client->present & CONFIG_OPTION_REG_PRESENT) { crt->cor = 0; /* XXX is 0 the right thing here? */ 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); /* * Mark this client as having done a successful RequestConfiguration * call. */ client->flags |= REQ_CONFIGURATION_DONE; mutex_exit(&sp->cis_lock); EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_SUCCESS); } /* * cs_release_configuration - releases configuration previously set via the * RequestConfiguration call; this is ReleaseConfiguration * * returns: CS_SUCCESS - if configuration sucessfully released * CS_UNSUPPORTED_FUNCTION - if SS is trying to call us * CS_BAD_SOCKET - if Socket Services returns an error * CS_BAD_HANDLE - a RequestConfiguration has not been done */ /*ARGSUSED*/ 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; /* * Check to see if this is the Socket Services client handle; if it * is, we don't support SS using this call. */ if (CLIENT_HANDLE_IS_SS(client_handle)) return (CS_UNSUPPORTED_FUNCTION); /* * Get a pointer to this client's socket structure. */ if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL) return (CS_BAD_SOCKET); EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp); /* * Make sure that this is a valid client handle. */ if (!(client = cs_find_client(client_handle, &error))) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (error); } /* * If RequestConfiguration has not been done, we don't allow * this call. */ 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); /* * Set the card back to a memory-only interface byte writing a zero * to the COR. Note that we don't update our soft copy of the * COR state since the PCMCIA spec only requires us to maintain * the last value that was written to that register during a * call to RequestConfiguration. */ 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) { /* * For the Multifunction cards do not reset the socket * to a memory only interface but do clear the * Configuration Option Register and mark this client * as not having a configuration by clearing the * REQ_CONFIGURATION_DONE flag. */ 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); } /* * Set the socket back to a memory-only interface; don't change * any other parameter of the socket. */ 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; /* don't reset latched values */ 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); } /* * Mark this client as not having a configuration. */ client->flags &= ~REQ_CONFIGURATION_DONE; mutex_exit(&sp->cis_lock); EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_SUCCESS); } /* * cs_modify_configuration - modifies a configuration established by * RequestConfiguration; this is ModifyConfiguration * * returns: CS_SUCCESS - if configuration sucessfully modified * CS_BAD_SOCKET - if Socket Services returns an error * CS_UNSUPPORTED_FUNCTION - if SS is trying to call us * CS_BAD_HANDLE - a RequestConfiguration has not been done * CS_NO_CARD - if no card in socket * CS_BAD_ATTRIBUTE - if any unsupported or reserved flags * are set * CS_BAD_VCC - if Vcc value is not supported by socket * CS_BAD_VPP1 - if Vpp1 value is not supported by socket * CS_BAD_VPP2 - if Vpp2 value is not supported by socket */ 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; /* * Check to see if this is the Socket Services client handle; if it * is, we don't support SS using this call. */ if (CLIENT_HANDLE_IS_SS(client_handle)) return (CS_UNSUPPORTED_FUNCTION); /* * Get a pointer to this client's socket structure. */ if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL) return (CS_BAD_SOCKET); EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp); /* * Make sure that this is a valid client handle. */ if (!(client = cs_find_client(client_handle, &error))) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (error); } /* * If RequestConfiguration has not been done, we don't allow * this call. */ if (!(client->flags & REQ_CONFIGURATION_DONE)) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_BAD_HANDLE); } /* * If there's no card in the socket or the card in the socket is not * for this client, then return an error. */ if (!(client->flags & CLIENT_CARD_INSERTED)) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_NO_CARD); } /* * Get the current socket parameters so that we can modify them. */ 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; /* don't reset latched values */ set_socket.IFType = get_socket.IFType; set_socket.IREQRouting = get_socket.IRQRouting; /* * Modify the IRQ routing if the client wants it modified. */ 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; /* * Get a pointer to a window that contains the configuration * registers. */ 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); } /* cs_init_cis_window */ 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); } /* CW_MULTI_FUNCTION_CIS */ if (mc->Attributes & CONF_ENABLE_IRQ_STEERING) set_socket.IREQRouting |= IRQ_ENABLE; } /* CONF_IRQ_CHANGE_VALID */ /* * Modify the voltage levels that the client specifies. */ 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; } /* * Setup the modified socket configuration. */ 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); } /* * cs_access_configuration_register - provides a client access to the card's * configuration registers; this is AccessConfigurationRegister * * returns: CS_SUCCESS - if register accessed successfully * CS_UNSUPPORTED_FUNCTION - if SS is trying to call us * CS_BAD_ARGS - if arguments are out of range * CS_NO_CARD - if no card in socket * CS_BAD_BASE - if no config registers base address * CS_UNSUPPORTED_MODE - if no RequestConfiguration has * been done yet */ 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; /* * Check to see if this is the Socket Services client handle; if it * is, we don't support SS using this call. */ if (CLIENT_HANDLE_IS_SS(client_handle)) return (CS_UNSUPPORTED_FUNCTION); /* * Make sure that the specifed offset is in range. */ if (acr->Offset > ((CISTPL_CONFIG_MAX_CONFIG_REGS * 2) - 2)) return (CS_BAD_ARGS); /* * Get a pointer to this client's socket structure. */ if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL) return (CS_BAD_SOCKET); EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp); /* * Make sure that this is a valid client handle. */ if (!(client = cs_find_client(client_handle, &error))) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (error); } /* * If there's no card in the socket or the card in the socket is not * for this client, then return an error. */ if (!(client->flags & CLIENT_CARD_INSERTED)) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_NO_CARD); } /* * If RequestConfiguration has not been done, we don't allow * this call. */ if (!(client->flags & REQ_CONFIGURATION_DONE)) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_UNSUPPORTED_MODE); } mutex_enter(&sp->cis_lock); /* * Get a pointer to the CIS window */ 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); } /* * Create the address for the config register that the client * wants to access. */ 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 /* * Determine what the client wants us to do. The client is * allowed to specify any valid offset, even if it would * cause an unimplemented configuration register to be * accessed. */ 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; } /* switch */ mutex_exit(&sp->lock); mutex_exit(&sp->cis_lock); EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (error); } /* * ==== RESET and general info functions ==== */ /* * cs_reset_function - RESET the requested function on the card; this * is ResetFunction * * Note: We don't support this functionality yet, and the standard * says it's OK to reutrn CS_IN_USE if we can't do this * operation. */ /*ARGSUSED*/ static int cs_reset_function(client_handle_t ch, reset_function_t *rf) { return (CS_IN_USE); } /* * cs_get_configuration_info - return configuration info for the passed * socket and function number to the caller; * this is GetConfigurationInfo */ /*ARGSUSED*/ 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; /* * Get a pointer to this client's socket structure. */ 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 there's no card in the socket or the card in the * socket is not for this client, then return * an error. */ 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); } /* GET_CLIENT_FUNCTION == fn */ client = client->next; } /* while (client) */ mutex_exit(&sp->lock); EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_BAD_SOCKET); } /* * cs_get_cardservices_info - return info about Card Services to the * caller; this is GetCardServicesInfo */ /*ARGSUSED*/ 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); } /* * cs_get_physical_adapter_info - returns information about the requested * physical adapter; this is * GetPhysicalAdapterInfo * * calling: client_handle_t: * NULL - use map_log_socket_t->LogSocket member * to specify logical socket number * !NULL - extract logical socket number from * client_handle_t * * returns: CS_SUCCESS * CS_BAD_SOCKET - if client_handle_t is NULL and invalid * socket number is specified in * map_log_socket_t->LogSocket * CS_BAD_HANDLE - if client_handle_t is !NULL and invalid * client handle is specified */ 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); /* * Determine if the passed socket number is valid or not. */ 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 we were passed a client handle, determine if it's valid or not. */ if (ch != 0) { if (cs_find_client(ch, NULL) == NULL) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_BAD_HANDLE); } /* cs_find_client */ } /* ch != NULL */ 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); } /* * ==== general functions ==== */ /* * cs_map_log_socket - returns the physical socket number associated with * either the passed client handle or the passed * logical socket number; this is MapLogSocket * * calling: client_handle_t: * NULL - use map_log_socket_t->LogSocket member * to specify logical socket number * !NULL - extract logical socket number from * client_handle_t * * returns: CS_SUCCESS * CS_BAD_SOCKET - if client_handle_t is NULL and invalid * socket number is specified in * map_log_socket_t->LogSocket * CS_BAD_HANDLE - if client_handle_t is !NULL and invalid * client handle is specified * * Note: We provide this function since the instance number of a client * driver doesn't necessary correspond to the physical * socket number */ 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); /* * Determine if the passed socket number is valid or not. */ 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 we were passed a client handle, determine if it's valid or not. */ if (ch != 0) { if (cs_find_client(ch, NULL) == NULL) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_BAD_HANDLE); } /* cs_find_client */ } /* ch != NULL */ mls->PhyAdapter = sp->adapter.number; EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_SUCCESS); } /* * cs_convert_speed - convers nS to devspeed and devspeed to nS * * The actual function is is in the CIS parser module; this * is only a wrapper. */ static int cs_convert_speed(convert_speed_t *cs) { return ((int)(uintptr_t)CIS_PARSER(CISP_CIS_CONV_DEVSPEED, cs)); } /* * cs_convert_size - converts a devsize value to a size in bytes value * or a size in bytes value to a devsize value * * The actual function is is in the CIS parser module; this * is only a wrapper. */ static int cs_convert_size(convert_size_t *cs) { return ((int)(uintptr_t)CIS_PARSER(CISP_CIS_CONV_DEVSIZE, cs)); } /* * cs_convert_powerlevel - converts a power level in tenths of a volt * to a power table entry for the specified socket * * returns: CS_SUCCESS - if volts converted to a valid power level * CS_BAD_ADAPTER - if SS_InquireAdapter fails * CS_BAD_ARGS - if volts are not supported on this socket * and adapter */ 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); } /* * cs_event2text - returns text string(s) associated with the event; this * function supports the Event2Text CS call. * * calling: event2text_t * - pointer to event2text struct * int event_source - specifies event type in event2text_t: * 0 - SS event * 1 - CS event * * returns: CS_SUCCESS */ static int cs_event2text(event2text_t *e2t, int event_source) { event_t event; char *sepchar = "|"; /* * If event_source is 0, this is a SS event */ 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 { /* * This is a CS event */ 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 (cs_ss_event_text) */ } /* for (event) */ if (e2t->text[0]) e2t->text[strlen(e2t->text)-1] = '\0'; } /* if (!event_source) */ return (CS_SUCCESS); } /* * cs_error2text - returns a pointer to a text string containing the name * of the passed Card Services function or return code * * This function supports the Error2Text CS call. */ 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); } /* * cs_make_device_node - creates/removes device nodes on a client's behalf; * this is MakeDeviceNode and RemoveDeviceNode * * returns: CS_SUCCESS - if all device nodes successfully created/removed * CS_BAD_ATTRIBUTE - if NumDevNodes is not zero when Action * is REMOVAL_ALL_DEVICES * CS_BAD_ARGS - if an invalid Action code is specified * CS_UNSUPPORTED_FUNCTION - if SS is trying to call us * CS_OUT_OF_RESOURCE - if can't create/remove device node */ 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; /* * Check to see if this is the Socket Services client handle; if it * is, we don't support SS using this call. */ if (CLIENT_HANDLE_IS_SS(client_handle)) return (CS_UNSUPPORTED_FUNCTION); /* * Get a pointer to this client's socket structure. */ if ((sp = cs_get_sp(GET_CLIENT_SOCKET(client_handle))) == NULL) return (CS_BAD_SOCKET); EVENT_THREAD_MUTEX_ENTER(client_lock_acquired, sp); /* * Make sure that this is a valid client handle. */ if (!(client = cs_find_client(client_handle, &error))) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (error); } #ifdef XXX /* * If there's no card in the socket or the card in the socket is not * for this client, then return an error. */ if (!(client->flags & CLIENT_CARD_INSERTED)) { EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_NO_CARD); } #endif /* * Setup the client's dip, since we use it later on. */ ss_make_device_node.dip = client->dip; /* * Make sure that we're being given a valid Action. Set the default * error code as well. */ error = CS_BAD_ARGS; /* for default case */ 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; } /* FALLTHROUGH */ default: EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (error); /* NOTREACHED */ } /* switch */ /* * Loop through the device node descriptions and create or destroy * the device node. */ 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; /* * Set the appropriate flag for the action that we want * SS to perform. Note that if we ever OR-in the flag * here, we need to be sure to clear the flags member * since we sometimes OR-in other flags below. */ 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 this is not the last device to process, then we need * to tell SS that more device process requests are on * their way after this one. */ 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); } /* CSInitDev */ } /* for (mdn->NumDevNodes) */ EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_SUCCESS); } /* * cs_remove_device_node - removes device nodes * * (see cs_make_device_node for a description of the calling * and return parameters) */ static int cs_remove_device_node(client_handle_t client_handle, remove_device_node_t *rdn) { /* * XXX - Note the assumption here that the make_device_node_t and * remove_device_node_t structures are identical. */ return (cs_make_device_node(client_handle, (make_device_node_t *)rdn)); } /* * cs_ddi_info - this function is used by clients that need to support * the xxx_getinfo function; this is CS_DDI_Info */ 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 /* * Check to see if the socket number is in range - the system * framework may cause a client driver to call us with * a socket number that used to be present but isn't * anymore. This is not a bug, and it's OK to return * an error if the socket number is out of range. */ 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 (!CHECK_SOCKET_NUM) */ /* * Get a pointer to this client's socket structure. */ 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); } /* strcmp */ } /* driver_name != NULL */ client = client->next; } /* while (client) */ EVENT_THREAD_MUTEX_EXIT(client_lock_acquired, sp); return (CS_BAD_SOCKET); } /* * cs_sys_ctl - Card Services system control; this is CS_Sys_Ctl */ 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); } /* cs_find_client */ /* * Setup the events that we want to send to the client. */ cp->events |= (csc->Events & (cp->event_mask | cp->global_mask)); } /* CS_SYS_CTL_EVENT_CLIENT */ 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); } /* CS_SYS_CTL_WAIT_SYNC */ mutex_exit(&sp->client_lock); ret = CS_SUCCESS; break; default: break; } /* switch */ return (ret); } /* * cs_get_sp - returns pointer to per-socket structure for passed * socket number * * return: (cs_socket_t *) - pointer to socket structure * NULL - invalid socket number passed in */ 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); } /* * cs_find_sp - searches socket list and returns pointer to passed socket * number * * return: (cs_socket_t *) - pointer to socket structure if found * NULL - socket not found */ 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; } /* while */ return (NULL); } /* * cs_add_socket - add a socket * * call: sn - socket number to add * * return: CS_SUCCESS - operation sucessful * CS_BAD_SOCKET - unable to add socket * CS_BAD_WINDOW - unable to get CIS window for socket * * We get called here once for each socket that the framework wants to * add. When we are called, the framework guarentees that until we * complete this routine, no other adapter instances will be allowed * to attach and thus no other PCE_ADD_SOCKET events will occur. * It is safe to call SS_InquireAdapter to get the number of * windows that the framework currently knows about. */ 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); /* * See if this socket has already been added - if it has, we * fail this. If we can't find the socket, then allocate * a new socket structure. If we do find the socket, then * check to see if it's already added; if it is, then * this is an error and return CS_BAD_SOCKET; if not, * then traverse the socket structure list and add this * next socket strcture to the end of the list. * XXX What about locking this list while we update it? Is * that necessary since we're using the SOCKET_IS_VALID * flag and since we never delete a socket from the * list once it's been added? */ 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; } /* if */ spp = spp->next; } /* while */ } else { if (sp->flags & SOCKET_IS_VALID) return (CS_BAD_SOCKET); } /* cs_find_sp */ /* * Setup the socket number */ sp->socket_num = sn; /* * Find out how many windows the framework knows about * so far. If this number of windows is greater * than our current window count, bump up our * current window count. * XXX Note that there is a BIG assumption here and that * is that once the framework tells us that it has * a window (as reflected in the NumWindows * value) it can NEVER remove that window. * When we really get the drop socket and drop * window mechanism working correctly, we'll have * to revisit this. */ 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_add_windows */ cs_globals.num_windows = inquire_adapter.NumWindows; } /* if (added_windows) */ /* * Find a window that we can use for this socket's CIS window. */ 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); } /* cs_find_mem_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 the CIS window is a variable sized window, then use * the size that cs_find_mem_window returned to us, * since this will be the minimum size that we can * set this window to. If the CIS window is a fixed * sized window, then use the system pagesize as the * CIS window size. */ 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 /* * Get the adapter information associated with this socket so * that we can initialize the mutexes, condition variables, * soft interrupt handler and per-socket adapter info. */ 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); } /* CSGetCookiesAndDip */ /* * Save the iblock and idev cookies for RegisterClient */ sp->iblk = gcad->iblock; sp->idev = gcad->idevice; /* * Setup the per-socket adapter info */ 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; /* Setup for cs_event and cs_event_thread */ 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); /* Setup for Socket Services work thread */ mutex_init(&sp->ss_thread_lock, NULL, MUTEX_DRIVER, NULL); sp->init_state |= SOCKET_INIT_STATE_MUTEX; /* Setup for cs_event_thread */ 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); /* Setup for Socket Services work thread */ 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 we haven't installed it yet, then install the soft interrupt * handler and save away the softint id. */ 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); } /* ddi_add_softintr */ mutex_enter(&cs_globals.global_lock); cs_globals.softint_id = sp->softint_id; cs_globals.init_state |= GLOBAL_INIT_STATE_SOFTINTR; /* XXX this timer is hokey at best... */ cs_globals.sotfint_tmo = timeout(cs_event_softintr_timeout, NULL, SOFTINT_TIMEOUT_TIME); mutex_exit(&cs_globals.global_lock); } else { /* * We've already added the soft interrupt handler, so just * store away the softint id. */ sp->softint_id = cs_globals.softint_id; } /* if (!GLOBAL_INIT_STATE_SOFTINTR) */ /* * While this next flag doesn't really describe a per-socket * resource, we still set it for each socket. When the soft * interrupt handler finally gets removed in cs_deinit, this * flag will get cleared. */ sp->init_state |= SOCKET_INIT_STATE_SOFTINTR; /* * Socket Services defaults all sockets to power off and * clears all event masks. We want to receive at least * card insertion events, so enable them. Turn off power * to the socket as well. We will turn it on again when * we get a card insertion event. */ 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; /* turn off controls and indicators */ set_socket.State = (unsigned)~0; /* clear latched state bits */ (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); } /* SS_SetSocket */ /* * The various socket-specific variables are now set up, so * increment the global socket count and also mark the * socket as available. We need to set this before we * start any of the per-socket threads so that the threads * can get a valid socket pointer when they start. */ 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; /* * Create the per-socket event handler thread. */ 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); /* * Create the per-socket Socket Services work thread. */ 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); } /* * cs_drop_socket - drop a socket * * call: sn - socket number to drop * * return: CS_SUCCESS - operation sucessful * CS_BAD_SOCKET - unable to drop socket */ /*ARGSUSED*/ static uint32_t cs_drop_socket(uint32_t sn) { #ifdef XXX cs_socket_t *sp; /* * Tell the socket event thread to exit and then wait for it * to do so. */ 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); /* * Tell the socket SS thread to exit and then wait for it * to do so. */ /* * Mark the socket as dropped. */ sp->flags &= ~SOCKET_IS_VALID; #endif /* XXX */ /* XXX for now don't allow dropping sockets XXX */ return (CS_BAD_SOCKET); } /* * cs_get_socket - returns the socket and function numbers and a pointer * to the socket structure * * calling: client_handle_t client_handle - client handle to extract * socket number from * uint32_t *socket - pointer to socket number to use if * client_handle is for the SS client; * this value will be filled in on * return with the correct socket * and function numbers if we * return CS_SUCCESS * uint32_t *function - pointer to return function number into * if not NULL * cs_socket_t **sp - pointer to a pointer where a pointer * to the socket struct will be * placed if this is non-NULL * client_t **clp - pointer to a pointer where a pointer * to the client struct will be * placed if this is non-NULL * * The socket and function numbers are derived as follows: * * Client Type Socket Number Function Number * PC card client From client_handle From client_handle * Socket Services client From *socket From *socket * CSI client From client_handle From *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 this is the Socket Services client, then return the * socket and function numbers specified in the passed * socket number parameter, otherwise extract the socket * and function numbers from the client handle. */ 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); } /* * Check to be sure that the socket number is in range */ 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 we were given a pointer, then fill it in with a pointer * to this socket. */ if (csp) *csp = sp; /* * Search for the client; if it's not found, return an error. */ mutex_enter(&sp->lock); if (!(client = cs_find_client(client_handle, &ret))) { mutex_exit(&sp->lock); return (ret); } /* * If we're a CIS client, then extract the function number * from the socket number. */ if (client->flags & CLIENT_CSI_CLIENT) fn = CS_GET_FUNCTION_NUMBER(*socket); mutex_exit(&sp->lock); /* * Return the found client pointer if the caller wants it. */ if (clp) *clp = client; /* * Return a socket number that is made up of the socket number * and the function number. */ *socket = CS_MAKE_SOCKET_NUMBER(sn, fn); /* * Return the function number if the caller wants it. */ if (function) *function = fn; return (CS_SUCCESS); } /* * cs_get_wp - returns pointer to passed window number * * return: (cs_window_t *) - pointer to window structure * NULL - if invalid window number passed in */ 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); } /* * cs_find_wp - searches window list and returns pointer to passed window * number * * return: (cs_window_t *) - pointer to window structure * NULL - window not found */ 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; } /* while */ #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); } /* * cs_add_windows - adds number of windows specified in "aw" to * the global window list; start the window * numbering at "bn" * * return: CS_SUCCESS - if windows added sucessfully * CS_BAD_WINDOW - if unable to add windows * * Note: The window list must be protected by a lock by the caller. */ 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; } /* while (aw) */ return (CS_SUCCESS); } /* * cs_ss_init - initialize CS items that need to wait until we receive * a PCE_SS_INIT_STATE/PCE_SS_STATE_INIT event * * return: CS_SUCESS - if sucessfully initialized * (various) if error initializing * * At this point, we expect that Socket Services has setup the * following global variables for us: * * cs_socket_services - Socket Services entry point * cis_parser - CIS parser entry point */ static uint32_t cs_ss_init() { cs_register_cardservices_t rcs; csregister_t csr; uint32_t ret; /* * Fill out the parameters for CISP_CIS_SETUP */ csr.cs_magic = PCCS_MAGIC; csr.cs_version = PCCS_VERSION; csr.cs_card_services = CardServices; csr.cs_event = NULL; /* * Call into the CIS module and tell it what the private * Card Services entry point is. The CIS module will * call us back at CardServices(CISRegister, ...) * with the address of various CIS-specific global * data structures. */ CIS_PARSER(CISP_CIS_SETUP, &csr); /* * Register with the Card Services kernel stubs module */ 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); } /* csx_register_cardservices */ return (CS_SUCCESS); } /* * cs_create_cis - reads CIS on card in socket and creates CIS lists * * Most of the work is done in the CIS module in the CISP_CIS_LIST_CREATE * function. * * This function returns: * * CS_SUCCESS - if the CIS lists were created sucessfully * CS_BAD_WINDOW or CS_GENERAL_FAILURE - if CIS window could * not be setup * CS_BAD_CIS - if error creating CIS chains * CS_BAD_OFFSET - if the CIS parser tried to read past the * boundries of the allocated CIS window */ 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 this card didn't have any CIS at all, there's not much * else for us to do. */ if (!(sp->cis_flags & CW_VALID_CIS)) return (CS_SUCCESS); /* * If this is a single-function card, we need to move the CIS list * that is currently on CS_GLOBAL_CIS to the function zero * CIS list. */ 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)); } /* !CW_MULTI_FUNCTION_CIS */ return (CS_SUCCESS); } /* * cs_destroy_cis - destroys CIS list for socket */ static int cs_destroy_cis(cs_socket_t *sp) { CIS_PARSER(CISP_CIS_LIST_DESTROY, sp); return (CS_SUCCESS); } /* * cs_get_client_info - This function is GetClientInfo. * * calling: client_handle_t - client handle to get client info on * client_info_t * - pointer to a client_info_t structure * to return client information in * * returns: CS_SUCCESS - if client info retreived from client * CS_BAD_SOCKET, CS_BAD_HANDLE - if invalid client * handle passed in * CS_NO_MORE_ITEMS - if client does not handle the * CS_EVENT_CLIENT_INFO event * or if invalid client info * retreived from client */ 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); } /* CLIENT_HANDLE_IS_SS */ 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); } /* cs_find_client */ /* * If this client is not handling CS_EVENT_CLIENT_INFO events, * then don't bother to even wake up the event thread. */ 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); } /* !CS_EVENT_CLIENT_INFO */ 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; } /* CS_CLIENT_INFO_VALID */ mutex_exit(&sp->client_lock); return (ret); } /* * cs_get_firstnext_client - This function is GetFirstClient and * GetNextClient * * calling: get_firstnext_client_t * - pointer to a get_firstnext_client_t * structure to return client handle and * attributes in * flags - one of the following: * CS_GET_FIRST_FLAG - get first client handle * CS_GET_NEXT_FLAG - get next client handle * * returns: CS_SUCCESS - if client info retreived from client * CS_BAD_SOCKET, CS_BAD_HANDLE - if invalid client * handle passed in * CS_NO_MORE_ITEMS - if client does not handle the * CS_EVENT_CLIENT_INFO event * or if invalid client info * retreived from client */ 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); } /* if */ sn++; } /* while */ 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); } /* if */ sn++; } /* while */ if (sn == cs_globals.max_socket_num) return (CS_NO_MORE_ITEMS); } /* client = client->next */ } 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; } /* switch */ return (ret); } /* * cs_set_acc_attributes - converts Card Services endianness and * data ordering values to values * that Socket Services understands * * calling: *sw - pointer to a set_window_t to set attributes in * Attributes - CS attributes */ 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 */ 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; } /* switch */ }