/* * 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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * This is a collection of routines that make up the Card Information * Structure (CIS) interpreter. The algorigthms used are based * on the Release 2.01 PCMCIA standard. * * Note that a bunch of comments are not indented correctly with the * code that they are commenting on. This is because cstyle is * inflexible concerning 4-column indenting. */ #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 /* * Function declarations */ void *CISParser(int function, ...); static int (*cis_card_services)(int, ...) = NULL; static int cis_process_longlink(cistpl_callout_t *, cistpl_t *, cis_info_t *, cisparse_t *); static int cis_create_cis_chain(cs_socket_t *, cistpl_callout_t *, cisptr_t *, cis_info_t *, cisparse_t *); static void cis_store_cis_addr(cistpl_t *, cisptr_t *); extern cistpl_callout_t cistpl_std_callout[]; extern cistpl_devspeed_struct_t cistpl_devspeed_struct; #ifdef CIS_DEBUG int cis_debug = 0; #endif /* * cisp_init - initialize the CIS parser */ void cisp_init() { #ifdef XXX csregister_t csr; /* * Fill out the function for CISSetAddress */ csr.cs_magic = PCCS_MAGIC; csr.cs_version = PCCS_VERSION; csr.cs_event = (f_t *)CISParser; /* * We have to call SS instead of CS to register because we * can't do a _depends_on for CS */ SocketServices(CISSetAddress, &csr); #endif /* XXX */ } /* * cis_deinit - deinitialize the CIS parser */ void cis_deinit() { /* * Tell CS that we're gone. */ if (cis_card_services) CIS_CARD_SERVICES(CISUnregister); return; } /* * CISParser - this is the entrypoint for all of the CIS Interpreter * functions */ void * CISParser(int function, ...) { va_list arglist; void *retcode = (void *)CS_UNSUPPORTED_FUNCTION; #if defined(CIS_DEBUG) if (cis_debug > 1) { cmn_err(CE_CONT, "CISParser: called with function 0x%x\n", function); } #endif va_start(arglist, function); /* * ...and here's the CIS Interpreter waterfall */ switch (function) { case CISP_CIS_SETUP: { csregister_t *csr; cisregister_t cisr; csr = va_arg(arglist, csregister_t *); cis_card_services = csr->cs_card_services; cisr.cis_magic = PCCS_MAGIC; cisr.cis_version = PCCS_VERSION; cisr.cis_parser = NULL; /* let the framework do this */ cisr.cistpl_std_callout = cistpl_std_callout; /* * Tell CS that we're here and what our * entrypoint address is. */ CIS_CARD_SERVICES(CISRegister, &cisr); } /* CISP_CIS_SETUP */ break; case CISP_CIS_LIST_CREATE: { cistpl_callout_t *cistpl_callout; cs_socket_t *sp; cistpl_callout = va_arg(arglist, cistpl_callout_t *); sp = va_arg(arglist, cs_socket_t *); retcode = (void *) (uintptr_t)cis_list_create(cistpl_callout, sp); } break; case CISP_CIS_LIST_DESTROY: { cs_socket_t *sp; sp = va_arg(arglist, cs_socket_t *); retcode = (void *)(uintptr_t)cis_list_destroy(sp); } break; case CISP_CIS_GET_LTUPLE: { cistpl_t *tp; cisdata_t type; int flags; tp = va_arg(arglist, cistpl_t *); type = va_arg(arglist, uint_t); flags = va_arg(arglist, int); retcode = (void *)cis_get_ltuple(tp, type, flags); } break; case CISP_CIS_PARSE_TUPLE: { cistpl_callout_t *co; cistpl_t *tp; int flags; void *arg; cisdata_t subtype; co = va_arg(arglist, cistpl_callout_t *); tp = va_arg(arglist, cistpl_t *); flags = va_arg(arglist, int); arg = va_arg(arglist, void *); subtype = va_arg(arglist, uint_t); retcode = (void *)(uintptr_t)cis_tuple_handler(co, tp, flags, arg, subtype); } break; case CISP_CIS_CONV_DEVSPEED: retcode = (void *)(uintptr_t)cis_convert_devspeed( va_arg(arglist, convert_speed_t *)); break; case CISP_CIS_CONV_DEVSIZE: retcode = (void *)(uintptr_t)cis_convert_devsize( va_arg(arglist, convert_size_t *)); break; default: break; } va_end(arglist); return (retcode); } /* * cis_list_lcreate - read a PC card's CIS and create a local linked CIS list * * cistpl_callout_t *cistpl_callout - pointer to callout structure * array to use to find tuples. * cisptr_t cisptr - pointer to a structure containing the handle and * offset from where we should start reading * CIS bytes as well as misc flags. * cis_info_t *cis_info - pointer to a cis_info_t structure; pass * the cis_info->cis member as a NULL pointer * if you want to create a new list. * cisparse_t *cisparse - pointer to a cisparse_t struture to put * parsed longlink tuple data into. * cs_socket_t *sp - pointer to a cs_socket_t structure that describes * the socket and card in this socket. * * We return the a count of the number of tuples that we saw, not including * any CISTPL_END or CISTPL_NULL tuples if there were no problems * processing the CIS. If a tuple handler returns an error, we * immediately return with the error code from the handler. An * error return code will always have the HANDTPL_ERROR bit set * to allow the caller to distinguish an error from a valid tuple * count. * * The nchains and ntuples counters in the cis_info_t structure are also * updated to reflect the number of chains and number of tuples in * this chain. * * XXX need to add CISTPL_END and CISTPL_NULL tuples to the list, and need * to be sure that the tuple count reflects these tuples * * If we attempt to read beyond the end of the mapped in CIS address space, * the BAD_CIS_ADDR error code is returned. * * This function only interprets the CISTPL_END and CISTPL_NULL tuples as * well as any tuple with a link field of CISTPL_END. * * Tuples of type CISTPL_END or CISTPL_NULL are not added to the list. * * To append tuples to end of a local linked CIS list, pass a pointer to the * address of the last element in the list that you want tuples appended * to. This pointer should be passed in cis_info->cis. * * To process tuple chains with any long link targets, call this routine * for each tuple chain you want to process using the list append method * described above. The caller is responsible for vaildating any link * target tuples to be sure that they describe a valid CIS chain. * * The cis_info->flags member is updated as follows: * * CW_VALID_CIS - if the CIS is valid * CW_LONGLINK_MFC_FOUND - if a CISTPL_LONGLINK_MFC tuple * was seen * CW_LONGLINK_A_FOUND - if a CISTPL_LONGLINK_A tuple was * seen * CW_LONGLINK_C_FOUND - if a CISTPL_LONGLINK_C tuple was * seen * * If a CISTPL_LONGLINK_MFC, CISTPL_LONGLINK_A or CISTPL_LONGLINK_C * tuple is seen, the *cisparse argument will return an appropriate * parsed longlink structure as follows: * * CW_LONGLINK_MFC_FOUND: * *cisparse --> cistpl_longlink_mfc_t * * CW_LONGLINK_A_FOUND, CW_LONGLINK_C_FOUND: * *cisparse --> cistpl_longlink_ac_t * * * These flags are set and the tuples are parsed so that the caller does * not have to traverse the CIS list to find out if any of these tuples * have been seen. * * For each tuple that we see, the following flags in the tuple_t->flags member * are set/cleared: * * CISTPLF_COPYOK - OK to copy tuple data * CISTPLF_GLOBAL_CIS - tuple from global CIS * CISTPLF_MF_CIS - tuple from MF CIS chain * CISTPLF_FROM_AM - tuple read from AM space * CISTPLF_FROM_CM - tuple read from CM space * CISTPLF_LINK_INVALID - tuple link is invalid * CISTPLF_PARAMS_INVALID - tuple body is invalid * CISTPLF_AM_SPACE - this tuple is in AM space * CISTPLF_CM_SPACE - this tuple is in CM space * CISTPLF_LM_SPACE - this tuple is in local memory */ uint32_t cis_list_lcreate(cistpl_callout_t *cistpl_callout, cisptr_t *cisptr, cis_info_t *cis_info, cisparse_t *cisparse, cs_socket_t *sp) { cistpl_t *cp, *tp = NULL; cisdata_t tl, td, *dp; int done = 0, err; get_socket_t get_socket; /* * If we were passed a non-NULL list base, that means that we should * parse the CIS and add any tuples we find to the end of the list * we were handed a pointer to. */ if (cis_info->cis) { tp = cis_info->cis; } get_socket.socket = sp->socket_num; if (SocketServices(SS_GetSocket, &get_socket) != SUCCESS) { cmn_err(CE_CONT, "cis_list_lcreate: socket %d SS_GetSocket failed\n", sp->socket_num); return (CS_BAD_SOCKET); } /* * If this is primary CIS chain, the first tuple must be one * from the following list. * Ref. PC Card 95, Metaformat Specification, Page 7. * XXX Need to think this out a bit more to deal with 3.3V * cards and the description of where a CISTPL_DEVICE * can show up. */ #if defined(CIS_DEBUG) if (cis_debug > 1) { cmn_err(CE_CONT, "cis_list_lcreate: td=0x%x cisptr=%p\n", GET_CIS_DATA(cisptr), (void *)cisptr); cmn_err(CE_CONT, "\t flags=0x%x CW_CHECK_PRIMARY_CHAIN=0x%x\n", cis_info->flags, CW_CHECK_PRIMARY_CHAIN); cmn_err(CE_CONT, "\t IFType=0x%x IF_MEMORY=0x%x\n", get_socket.IFType, IF_MEMORY); } #endif if (cis_info->flags & CW_CHECK_PRIMARY_CHAIN) { switch (td = GET_CIS_DATA(cisptr)) { case CISTPL_DEVICE: case CISTPL_END: case CISTPL_LINKTARGET: break; case CISTPL_NULL: /* * Magicram memory cards without attribute memory * do not have a CIS and return CISTPL_NULL. */ if (get_socket.IFType == IF_MEMORY) return (0); break; default: return (0); } /* switch */ } /* CW_CHECK_PRIMARY_CHAIN */ /* * Update the number of chains counter */ cis_info->nchains++; /* * The main tuple processing loop. We'll exit this loop when either * a tuple's link field is CISTPL_END or we've seen a tuple type * field of CISTPL_END. * * Note that we also silently throw away CISTPL_NULL tuples, and don't * include them in the tuple count that we return. */ while (!done && ((td = GET_CIS_DATA(cisptr)) != (cisdata_t)CISTPL_END)) { #if defined(CIS_DEBUG) if ((cis_debug > 1) && (td != 0)) { cmn_err(CE_CONT, "cis_list_lcreate: td=0x%x cisptr=%p" "offset=0x%x\n", td, (void *)cisptr, cisptr->offset); } #endif /* * Ignore CISTPL_NULL tuples */ if (td != (cisdata_t)CISTPL_NULL) { /* * point to tuple link field and get the link value */ if (!NEXT_CIS_ADDR(cisptr)) return ((uint32_t)BAD_CIS_ADDR); tl = GET_CIS_DATA(cisptr); /* * This is an ugly PCMCIA hack - ugh! since the standard allows * a link byte of CISTPL_END to signify that this is the * last tuple. The problem is that this tuple might * actually contain useful information, but we don't know * the size of it. * We do know that it can't be more than CIS_MAX_TUPLE_DATA_LEN * bytes in length, however. So, we pretend that the link * byte is CIS_MAX_TUPLE_DATA_LEN and also set a flag so * that when we're done processing this tuple, we will * break out of the while loop. */ if (tl == (cisdata_t)CISTPL_END) { tl = CIS_MAX_TUPLE_DATA_LEN; done = 1; } /* * point to first byte of tuple data, allocate a new list * element and diddle with the list base and list * control pointers */ if (!NEXT_CIS_ADDR(cisptr)) return ((uint32_t)BAD_CIS_ADDR); cp = (cistpl_t *)CIS_MEM_ALLOC(sizeof (cistpl_t)); cp->next = NULL; /* * if we're not the first in the list, point to our * next */ if (tp) tp->next = cp; /* * will be NULL if we're the first element of the * list */ cp->prev = tp; tp = cp; /* * if this is the first element, save it's address */ if (!cis_info->cis) cis_info->cis = tp; tp->type = td; tp->len = tl; /* * Save the address in CIS space that this tuple * begins at, as well as set tuple flags. */ cis_store_cis_addr(tp, cisptr); /* * If this tuple has tuple data, we might need to * copy it. * Note that the tuple data pointer (tp->data) will * be set to NULL for a tuple with no data. */ #ifdef XXX if (tl) { #endif /* * Read the data in the tuple and store it * away locally if we're allowed to. If * the CISTPLF_COPYOK flag is set, it means * that it's OK to touch the data portion * of the tuple. * * We need to make this check since some * tuples might contain active registers * that can alter the device state if they * are read before the card is correctly * initialized. What a stupid thing to * allow in a standard, BTW. * * We first give the tuple handler a chance * to set any tuple flags that it wants * to, then we (optionally) do the data * copy, and give the tuple handler another * shot at the tuple. * * ref. PC Card Standard Release 2.01 in the * Card Metaformat section, section 5.2.6, * page 5-12. */ if ((err = cis_tuple_handler(cistpl_callout, tp, HANDTPL_SET_FLAGS, NULL, 0)) & HANDTPL_ERROR) return (err); if (tl > (unsigned)0) { /* * if we're supposed to make a local copy of * the tuple data, allocate space for it, * otherwise just record the PC card * starting address of this tuple. * The address was saved by cis_store_cis_addr. */ if (tp->flags & CISTPLF_COPYOK) { tp->data = (cisdata_t *)CIS_MEM_ALLOC(tl); dp = tp->data; } else { tp->data = GET_CIS_ADDR(tp); } while (tl--) { if (tp->flags & CISTPLF_COPYOK) *dp++ = GET_CIS_DATA(cisptr); if (!NEXT_CIS_ADDR(cisptr)) return ((uint32_t)BAD_CIS_ADDR); } /* * If we made a local copy of the tuple data, * then clear the AM and CM flags; if the * tuple data is still on the card, then * leave the flags alone. */ if (tp->flags & CISTPLF_COPYOK) { tp->flags &= ~CISTPLF_SPACE_MASK; tp->flags |= CISTPLF_LM_SPACE; } /* * This is a tuple with no data in it's body, so * we just set the data pointer to NULL. */ } else { tp->data = NULL; /* * tp->flags &= ~(CISTPLF_SPACE_MASK | * CISTPLF_FROM_MASK); */ } /* if (tl > 0) */ /* * The main idea behind this call is to give * the handler a chance to validate the * tuple. */ if ((err = cis_tuple_handler(cistpl_callout, tp, HANDTPL_COPY_DONE, NULL, 0)) & HANDTPL_ERROR) return (err); #ifdef XXX } else { /* if (tl) */ tp->data = NULL; } #endif /* * Check to see if this is a longlink tuple and if * so, do the necessary processing. */ if ((err = cis_process_longlink(cistpl_callout, tp, cis_info, cisparse)) & HANDTPL_ERROR) return (err); cis_info->ntuples++; } else { /* if (td == CISTPL_NULL) */ /* * If we're a CISTPL_NULL we need to skip to * the beginning of the next tuple. */ if (!NEXT_CIS_ADDR(cisptr)) return ((uint32_t)BAD_CIS_ADDR); } } /* while (!done && !CISTPL_END) */ #if defined(CIS_DEBUG) if (cis_debug > 1) { cmn_err(CE_CONT, "cis_list_lcreate: exit nchains=%x ntuples=%x\n", cis_info->nchains, cis_info->ntuples); } #endif return (cis_info->ntuples); } /* * cis_process_longlink - processes longlink tuples * * This function examines the passed-in tuple type and if it is a * longlink tuple, the tuple is parsed and the appropriate flags in * cis_info->flags are set. * * If there is an error parsing the tuple, HANDTPL_ERROR is returned * and the CW_LONGLINK_FOUND flags in cis_info->flags are cleared. */ static int cis_process_longlink(cistpl_callout_t *cistpl_callout, cistpl_t *tp, cis_info_t *cis_info, cisparse_t *cisparse) { /* * If this is a CISTPL_LONGLINK_A, CISTPL_LONGLINK_C * or CISTPL_LONGLINK_MFC tuple, parse the tuple * and set appropriate CW_LONGLINK_XXX_FOUND flags. * If this is a CISTPL_NO_LINK tuple, or if there is an * error parsing the tuple, clear all the * CW_LONGLINK_XXX_FOUND flags. */ switch (tp->type) { case CISTPL_LONGLINK_A: case CISTPL_LONGLINK_C: case CISTPL_LONGLINK_MFC: cis_info->flags &= ~CW_LONGLINK_FOUND; if (cis_tuple_handler(cistpl_callout, tp, HANDTPL_PARSE_LTUPLE, cisparse, NULL) & HANDTPL_ERROR) return (HANDTPL_ERROR); switch (tp->type) { case CISTPL_LONGLINK_A: cis_info->flags |= CW_LONGLINK_A_FOUND; break; case CISTPL_LONGLINK_C: cis_info->flags |= CW_LONGLINK_C_FOUND; break; case CISTPL_LONGLINK_MFC: cis_info->flags |= CW_LONGLINK_MFC_FOUND; break; } /* switch (tp->type) */ break; case CISTPL_NO_LINK: cis_info->flags &= ~CW_LONGLINK_FOUND; break; } /* switch (tp->type) */ return (HANDTPL_NOERROR); } /* * cis_list_ldestroy - function to destroy a linked tuple list * * cistpl_t *cistplbase - pointer to a pointer to the base of a * local linked CIS list to destroy; the * data that this pointer points to is * also destroyed * * Once this function returns, cistplbase is set to NULL. */ uint32_t cis_list_ldestroy(cistpl_t **cistplbase) { cistpl_t *cp, *tp; int tpcnt = 0; /* * First, check to see if we've got a * non-NULL list pointer. */ if ((tp = *cistplbase) == NULL) return (0); while (tp) { /* * Free any data that may be allocated */ if ((tp->flags & CISTPLF_COPYOK) && (tp->flags & CISTPLF_LM_SPACE) && (tp->data)) CIS_MEM_FREE((caddr_t)tp->data); cp = tp->next; /* * Free this tuple */ CIS_MEM_FREE((caddr_t)tp); tp = cp; tpcnt++; } /* * Now clear the pointer to the non-existant * linked list. */ *cistplbase = NULL; return (tpcnt); } /* * cis_get_ltuple - function to walk local linked CIS list and return * a tuple based on various criteria * * cistpl_t *tp - pointer to any valid tuple in the list * cisdata_t type - type of tuple to search for * int flags - type of action to perform (each is mutually exclusive) * GET_FIRST_LTUPLEF, GET_LAST_LTUPLEF: * Returns the {first|last} tuple in the list. * FIND_LTUPLE_FWDF, FIND_LTUPLE_BACKF: * FIND_NEXT_LTUPLEF, FIND_PREV_LTUPLEF: * Returns the first tuple that matches the passed tuple type, * searching the list {forward|backward}. * GET_NEXT_LTUPLEF, GET_PREV_LTUPLEF: * Returns the {next|previous} tuple in the list. * * The following bits can be set in the flags parameter: * CIS_GET_LTUPLE_IGNORE - return tuples with * CISTPLF_IGNORE_TUPLE set in cistpl_t->flags * * Note on searching: * When using the FIND_LTUPLE_FWDF and FIND_LTUPLE_BACKF flags, * the search starts at the passed tuple. Continually calling this * function with a tuple that is the same type as the passed type will * continually return the same tuple. * * When using the FIND_NEXT_LTUPLEF and FIND_PREV_LTUPLEF flags, * the search starts at the {next|previous} tuple from the passed tuple. * * returns: * cistpl_t * - pointer to tuple in list * NULL - if error while processing list or tuple not found */ #define GET_NEXT_LTUPLE(tp) ((tp->next)?tp->next:NULL) #define GET_PREV_LTUPLE(tp) ((tp->prev)?tp->prev:NULL) cistpl_t * cis_get_ltuple(cistpl_t *tp, cisdata_t type, uint32_t flags) { cistpl_t *ltp = NULL; if (!tp) return (NULL); switch (flags & CIS_GET_LTUPLE_OPMASK) { case GET_FIRST_LTUPLEF: /* return first tuple in list */ do { ltp = tp; } while ((tp = GET_PREV_LTUPLE(tp)) != NULL); if (!(flags & CIS_GET_LTUPLE_IGNORE)) while (ltp && (ltp->flags & CISTPLF_IGNORE_TUPLE)) ltp = GET_NEXT_LTUPLE(ltp); break; case GET_LAST_LTUPLEF: /* return last tuple in list */ do { ltp = tp; } while ((tp = GET_NEXT_LTUPLE(tp)) != NULL); if (!(flags & CIS_GET_LTUPLE_IGNORE)) while (ltp && (ltp->flags & CISTPLF_IGNORE_TUPLE)) ltp = GET_PREV_LTUPLE(ltp); break; case FIND_LTUPLE_FWDF: /* find tuple, fwd search from tp */ do { if (tp->type == type) if ((flags & CIS_GET_LTUPLE_IGNORE) || (!(tp->flags & CISTPLF_IGNORE_TUPLE))) return (tp); /* note return here */ } while ((tp = GET_NEXT_LTUPLE(tp)) != NULL); break; case FIND_LTUPLE_BACKF: /* find tuple, backward search from tp */ do { if (tp->type == type) if ((flags & CIS_GET_LTUPLE_IGNORE) || (!(tp->flags & CISTPLF_IGNORE_TUPLE))) return (tp); /* note return here */ } while ((tp = GET_PREV_LTUPLE(tp)) != NULL); break; case FIND_NEXT_LTUPLEF: /* find tuple, fwd search from tp+1 */ while ((tp = GET_NEXT_LTUPLE(tp)) != NULL) { if (tp->type == type) if ((flags & CIS_GET_LTUPLE_IGNORE) || (!(tp->flags & CISTPLF_IGNORE_TUPLE))) return (tp); /* note return here */ } /* while */ break; case FIND_PREV_LTUPLEF: /* find tuple, backward search from tp-1 */ while ((tp = GET_PREV_LTUPLE(tp)) != NULL) { if (tp->type == type) if ((flags & CIS_GET_LTUPLE_IGNORE) || (!(tp->flags & CISTPLF_IGNORE_TUPLE))) return (tp); /* note return here */ } /* while */ break; case GET_NEXT_LTUPLEF: /* return next tuple in list */ ltp = tp; while (((ltp = GET_NEXT_LTUPLE(ltp)) != NULL) && (!(flags & CIS_GET_LTUPLE_IGNORE)) && (ltp->flags & CISTPLF_IGNORE_TUPLE)) ; break; case GET_PREV_LTUPLEF: /* return prev tuple in list */ ltp = tp; while (((ltp = GET_PREV_LTUPLE(ltp)) != NULL) && (!(flags & CIS_GET_LTUPLE_IGNORE)) && (ltp->flags & CISTPLF_IGNORE_TUPLE)) ; break; default: /* ltp is already NULL in the initialization */ break; } /* switch */ return (ltp); } /* * cis_convert_devspeed - converts a devspeed value to nS or nS * to a devspeed entry */ uint32_t cis_convert_devspeed(convert_speed_t *cs) { cistpl_devspeed_struct_t *cd = &cistpl_devspeed_struct; unsigned exponent = 0, mantissa = 0; /* * Convert nS to a devspeed value */ if (cs->Attributes & CONVERT_NS_TO_DEVSPEED) { unsigned tnS, tmanv = 0, i; /* * There is no device speed code for 0nS */ if (!cs->nS) return (CS_BAD_SPEED); /* * Handle any nS value below 10nS specially since the code * below only works for nS values >= 10. Now, why anyone * would want to specify a nS value less than 10 is * certainly questionable, but it is allowed by the spec. */ if (cs->nS < 10) { tmanv = cs->nS * 10; mantissa = CISTPL_DEVSPEED_MAX_MAN; } /* find the exponent */ for (i = 0; i < CISTPL_DEVSPEED_MAX_EXP; i++) { if ((!(tnS = ((cs->nS)/10))) || (mantissa == CISTPL_DEVSPEED_MAX_MAN)) { /* find the mantissa */ for (mantissa = 0; mantissa < CISTPL_DEVSPEED_MAX_MAN; mantissa++) { if (cd->mantissa[mantissa] == tmanv) { cs->devspeed = ((((mantissa<<3) | (exponent & (CISTPL_DEVSPEED_MAX_EXP - 1))))); return (CS_SUCCESS); } } /* for (mantissanS; cs->nS = tnS; } /* if (!tnS) */ } /* for (iAttributes & CONVERT_DEVSPEED_TO_NS) { exponent = (cs->devspeed & (CISTPL_DEVSPEED_MAX_TBL - 1)); if ((mantissa = (((cs->devspeed)>>3) & (CISTPL_DEVSPEED_MAX_MAN - 1))) == NULL) { if ((cs->nS = cd->table[exponent]) == NULL) return (CS_BAD_SPEED); return (CS_SUCCESS); } else { if ((cs->nS = ((cd->mantissa[mantissa] * cd->exponent[exponent]) / 10)) == NULL) return (CS_BAD_SPEED); return (CS_SUCCESS); } } else { return (CS_BAD_ATTRIBUTE); } return (CS_BAD_SPEED); } /* * This array is for the cis_convert_devsize function. */ static uint32_t cistpl_device_size[8] = { 512, 2*1024, 8*1024, 32*1024, 128*1024, 512*1024, 2*1024*1024, 0 }; /* * cis_convert_devsize - converts a devsize value to a size in bytes value * or a size in bytes value to a devsize value */ uint32_t cis_convert_devsize(convert_size_t *cs) { int i; if (cs->Attributes & CONVERT_BYTES_TO_DEVSIZE) { if ((cs->bytes < cistpl_device_size[0]) || (cs->bytes > (cistpl_device_size[6] * 32))) return (CS_BAD_SIZE); for (i = 6; i >= 0; i--) if (cs->bytes >= cistpl_device_size[i]) break; cs->devsize = ((((cs->bytes/cistpl_device_size[i]) - 1) << 3) | (i & 7)); } else if (cs->Attributes & CONVERT_DEVSIZE_TO_BYTES) { if ((cs->devsize & 7) == 7) return (CS_BAD_SIZE); cs->bytes = cistpl_device_size[cs->devsize & 7] * ((cs->devsize >> 3) + 1); } else { return (CS_BAD_ATTRIBUTE); } return (CS_SUCCESS); } /* * cis_list_create - reads the card's CIS and creates local CIS lists for * each function on the card * * This function will read the CIS on the card, follow all CISTPL_LONGLINK_A, * CISTPL_LONGLINK_C and CISTPL_LONGLINK_MFC tuples and create local CIS * lists for each major CIS chain on the card. * * If there are no errors, the parameters returned are: * For a non-multifunction card: * sp->cis_flags - CW_VALID_CIS set * sp->nfuncs - set to 0x0 * sp->cis[CS_GLOBAL_CIS] - contains CIS list * sp->cis[CS_GLOBAL_CIS].cis_flags - CW_VALID_CIS set * * For a multifunction card: * Global CIS values: * sp->cis_flags - CW_VALID_CIS & CW_MULTI_FUNCTION_CIS set * sp->nfuncs - set to number of functions specified in * the CISTPL_LONGLINK_MFC tuple * sp->cis[CS_GLOBAL_CIS] - contains global CIS list * sp->cis[CS_GLOBAL_CIS].cis_flags - CW_VALID_CIS set * Function-specific CIS values: * sp->cis[0..sp->nfuncs-1] - contains function-specific CIS lists * sp->cis[0..sp->nfuncs-1].cis_flags - CW_VALID_CIS & * CW_MULTI_FUNCTION_CIS set * * returns: * CS_SUCCESS - if no errors * CS_NO_CIS - if no CIS on card * 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 cis_list_lcreate tried to read past the * boundries of the allocated CIS window */ extern cistpl_ignore_list_t cistpl_ignore_list[]; uint32_t cis_list_create(cistpl_callout_t *cistpl_callout, cs_socket_t *sp) { cisptr_t cisptr; cisparse_t cisparse; cis_info_t *cis_info; cistpl_longlink_ac_t *cistpl_longlink_ac; cistpl_longlink_mfc_t cistpl_longlink_mfc, *mfc; cistpl_ignore_list_t *cil; int fn, ret; /* * Initialize the CIS structures */ bzero((caddr_t)&sp->cis, ((sizeof (cis_info_t)) * CS_MAX_CIS)); /* * Start reading the primary CIS chain at offset 0x0 of AM. Assume * that there is a CISTPL_LONGLINK_C tuple that points to * offset 0x0 of CM space. * Since this is the primary CIS chain, set CW_CHECK_PRIMARY_CHAIN * so that we'll check for a valid first tuple. */ cis_info = &sp->cis[CS_GLOBAL_CIS]; cis_info->flags = (CW_LONGLINK_C_FOUND | CW_CHECK_PRIMARY_CHAIN); cisptr.flags = (CISTPLF_AM_SPACE | CISTPLF_GLOBAL_CIS); cisptr.size = sp->cis_win_size - 1; cisptr.offset = 0; cistpl_longlink_ac = (cistpl_longlink_ac_t *)&cisparse; cistpl_longlink_ac->flags = CISTPL_LONGLINK_AC_CM; cistpl_longlink_ac->tpll_addr = 0; if ((ret = cis_create_cis_chain(sp, cistpl_callout, &cisptr, cis_info, &cisparse)) != CS_SUCCESS) { return (ret); } /* cis_create_cis_chain */ /* * If there are no tuples in the primary CIS chain, it means that * this card doesn't have a CIS on it. */ if (cis_info->ntuples == 0) return (CS_NO_CIS); /* * Mark this CIS list as being valid. */ cis_info->flags |= CW_VALID_CIS; /* * Mark this socket as having at least one valid CIS chain. */ sp->cis_flags |= CW_VALID_CIS; sp->nfuncs = 0; /* * If the primary CIS chain specified that there are function-specific * CIS chains, we need to create each of these chains. If not, * then we're all done and we can return. */ if (!(cis_info->flags & CW_LONGLINK_MFC_FOUND)) return (CS_SUCCESS); /* * Mark this socket as having a multi-function CIS. */ sp->cis_flags |= CW_MULTI_FUNCTION_CIS; /* * At this point, cis_create_cis_chain has told us that the primary * CIS chain says that there are function-specific CIS chains * on the card that we need to follow. The cisparse variable now * contains the parsed output of the CISTPL_LONGLINK_MFC * tuple. We need to save that information and then process * each function-specific CIS chain. */ bcopy((caddr_t)&cisparse, (caddr_t)&cistpl_longlink_mfc, sizeof (cistpl_longlink_mfc_t)); mfc = &cistpl_longlink_mfc; sp->nfuncs = mfc->nregs; /* * Go through and create a CIS list for each function-specific * CIS chain on the card. Set CW_CHECK_LINKTARGET since all * function-specific CIS chains must begin with a valid * CISTPL_LINKTARGET tuple. Also set CW_RET_ON_LINKTARGET_ERROR * since we want to return an error if the CISTPL_LINKTARGET * tuple is invalid or missing. */ for (fn = 0; fn < sp->nfuncs; fn++) { cis_info = &sp->cis[fn]; cis_info->flags = (CW_CHECK_LINKTARGET | CW_RET_ON_LINKTARGET_ERROR); /* * If the function-specific CIS chain starts * in AM space, then multiply address by * 2 since only even bytes are counted in * the CIS when AM addresses are specified, * otherwise use the * address as specified. */ if (mfc->function[fn].tas == CISTPL_LONGLINK_MFC_TAS_AM) { cisptr.flags = (CISTPLF_AM_SPACE | CISTPLF_MF_CIS); cisptr.offset = mfc->function[fn].addr * 2; } else { cisptr.flags = (CISTPLF_CM_SPACE | CISTPLF_MF_CIS); cisptr.offset = mfc->function[fn].addr; } if ((ret = cis_create_cis_chain(sp, cistpl_callout, &cisptr, cis_info, &cisparse)) != CS_SUCCESS) { cmn_err(CE_CONT, "cis_list_create: socket %d ERROR_MFC = 0x%x\n", sp->socket_num, ret); return (ret); } /* cis_create_cis_chain */ /* * Mark this CIS list as being valid and as being a * function-specific CIS list. */ cis_info->flags |= (CW_VALID_CIS | CW_MULTI_FUNCTION_CIS); /* * Check for tuples that we want to ignore * in the global CIS. If the tuple exists * in the global CIS and in at least one * of the function-specific CIS lists, then * we flag the tuple * in the global CIS to be ignored. */ cil = &cistpl_ignore_list[0]; while (cil->type != CISTPL_NULL) { if (cis_get_ltuple(sp->cis[fn].cis, cil->type, FIND_LTUPLE_FWDF | CIS_GET_LTUPLE_IGNORE) != NULL) { cistpl_t *gtp = sp->cis[CS_GLOBAL_CIS].cis; while ((gtp = cis_get_ltuple(gtp, cil->type, FIND_LTUPLE_FWDF | CIS_GET_LTUPLE_IGNORE)) != NULL) { gtp->flags |= CISTPLF_IGNORE_TUPLE; gtp = cis_get_ltuple(gtp, NULL, GET_NEXT_LTUPLEF | CIS_GET_LTUPLE_IGNORE); } /* while */ } /* if (cis_get_ltuple(cis[fn])) */ cil++; } /* while */ } /* for */ return (CS_SUCCESS); } /* * cis_create_cis_chain - creates a single CIS chain * * This function reads the CIS on a card and follows any CISTPL_LONGLINK_A * and CISTPL_LONGLINK_C link tuples to create a single CIS chain. We * keep reading the CIS and following any CISTPL_LONGLINK_A and * CISTPL_LONGLINK_C tuples until we don't see anymore. If we see a * CISTPL_LONGLINK_MFC tuple, we return - the caller is responsible * for following CIS chains on a per-function level. * * The following parameters must be initialized by the caller: * * sp - pointer to a cs_socket_t structure that describes the socket * and card in this socket * cistpl_callout - pointer to a cistpl_callout_t array of structures * cisptr->flags - either CISTPLF_AM_SPACE or CISTPLF_CM_SPACE * cisptr->size - size of CIS window * cisptr->offset - offset in AM or CM space on card to start * reading tuples from * cis_info - pointer to a cis_info_t structure where this list will * be anchored on * cisparse - pointer to a cisparse_t structure where the last longlink * parsed tuple data will be returned * * To check the CISTPL_LINKTARGET tuple at the beginning of the first * CIS chain that this function encounters, set CW_CHECK_LINKTARGET * in cis_info->flags before calling this function. * * This function returns: * * CS_SUCCESS - if CIS chain was created sucessfully or there * were no tuples found on the first CIS chain * CS_BAD_WINDOW or CS_GENERAL_FAILURE - if CIS window could * not be setup * CS_BAD_CIS - if error creating CIS chain * CS_BAD_OFFSET - if cis_list_lcreate tried to read past the * boundries of the allocated CIS window * * Note that if the first tuple of the target CIS chain is supposed * to contain a CISTPL_LINKTARGET and the target chain does not * contain that tuple (or that tuple is invalid in some way) and * the CW_RET_ON_LINKTARGET_ERROR flag is not set, we don't flag * this as an error, we just return. This is to handle the case * where the target chain is in uninitialized memory and will be * initialized later. * To return an error if an invalid CISTPL_LINKTARGET tuple is seen, * set the CW_RET_ON_LINKTARGET_ERROR flag in cis_info->flags * before calling this function. */ static int cis_create_cis_chain(cs_socket_t *sp, cistpl_callout_t *cistpl_callout, cisptr_t *cisptr, cis_info_t *cis_info, cisparse_t *cisparse) { cistpl_t *tps = NULL; uint32_t ret; do { if ((ret = CIS_CARD_SERVICES(InitCISWindow, sp, &cisptr->offset, &cisptr->handle, cisptr->flags)) != CS_SUCCESS) return (ret); /* * If we're pointing at a CIS chain that * is the target of a longlink tuple, * we need to validate the target chain * before we try to process it. If the * CISTPL_LINKTARGET tuple is invalid, * and the CW_RET_ON_LINKTARGET_ERROR * is not set, don't flag it as an error, * just return. */ if (cis_info->flags & CW_CHECK_LINKTARGET) { cis_info->flags &= ~CW_CHECK_LINKTARGET; if (cis_validate_longlink_acm(cisptr) != CISTPLF_NOERROR) { if (tps != NULL) cis_info->cis = tps; if (cis_info->flags & CW_RET_ON_LINKTARGET_ERROR) { cis_info->flags &= ~CW_RET_ON_LINKTARGET_ERROR; return (CS_BAD_CIS); } else { return (CS_SUCCESS); } /* CW_RET_ON_LINKTARGET_ERROR */ } /* cis_validate_longlink_acm */ } /* CW_CHECK_LINKTARGET */ ret = cis_list_lcreate(cistpl_callout, cisptr, cis_info, cisparse, sp); #if defined(CIS_DEBUG) if (cis_debug > 1) { cmn_err(CE_CONT, "cis_create_cis_chain: ret=0x%x" " BAD_CIS_ADDR=0x%x CS_BAD_SOCKET=0x%x\n", ret, BAD_CIS_ADDR, CS_BAD_SOCKET); } #endif if ((ret & HANDTPL_ERROR) || (ret == (uint32_t)BAD_CIS_ADDR)) { if (tps != NULL) cis_info->cis = tps; if (ret == (uint32_t)BAD_CIS_ADDR) return (CS_BAD_OFFSET); else return (CS_BAD_CIS); } /* * If we're creating the primary CIS chain * and we haven't seen any tuples, * then return CS_SUCCESS. The caller will * have to check cis_info->ntuples to find * out if any tuples were found. * If we're processing the target of a longlink * tuple, then by now we have already validated * the CISTPL_LINKTARGET tuple so that we * know we'll have at least one tuple in * our list. */ if (cis_info->ntuples == 0) return (CS_SUCCESS); /* * If we've just created a new list, we need to * save the pointer to the start of the list. */ if (tps == NULL) tps = cis_info->cis; switch (cis_info->flags & CW_LONGLINK_FOUND) { cistpl_longlink_ac_t *cistpl_longlink_ac; case CW_LONGLINK_A_FOUND: cistpl_longlink_ac = (cistpl_longlink_ac_t *)cisparse; cisptr->flags &= ~(CISTPLF_SPACE_MASK | CISTPLF_FROM_MASK); cisptr->flags |= CISTPLF_AM_SPACE; /* * Multiply address by 2 since only * even bytes are counted in the CIS * when AM addresses are specified. */ cisptr->offset = cistpl_longlink_ac->tpll_addr * 2; cis_info->flags |= CW_CHECK_LINKTARGET; /* * Point to the last tuple in the list. */ cis_info->cis = cis_get_ltuple(cis_info->cis, NULL, GET_LAST_LTUPLEF); break; case CW_LONGLINK_C_FOUND: cistpl_longlink_ac = (cistpl_longlink_ac_t *)cisparse; cisptr->flags &= ~(CISTPLF_SPACE_MASK | CISTPLF_FROM_MASK); cisptr->flags |= CISTPLF_CM_SPACE; cisptr->offset = cistpl_longlink_ac->tpll_addr; cis_info->flags |= CW_CHECK_LINKTARGET; /* * Point to the last tuple in the list. */ cis_info->cis = cis_get_ltuple(cis_info->cis, NULL, GET_LAST_LTUPLEF); break; case CW_LONGLINK_MFC_FOUND: break; default: break; } /* switch (cis_info->flags) */ } while (cis_info->flags & (CW_LONGLINK_A_FOUND | CW_LONGLINK_C_FOUND)); /* * If we needed to save a pointer to the start of the list because * we saw a longlink tuple, restore the list head pointer now. */ if (tps != NULL) cis_info->cis = tps; return (CS_SUCCESS); } /* * cis_list_destroy - destroys the local CIS list */ uint32_t cis_list_destroy(cs_socket_t *sp) { int fn; /* * Destroy any CIS list that we may have created. It's OK to pass * a non-existant CIS list pointer to cis_list_ldestroy since * that function will not do anything if there is nothing in * the passed CIS list to cleanup. */ for (fn = 0; fn < CS_MAX_CIS; fn++) (void) cis_list_ldestroy(&sp->cis[fn].cis); /* * Clear out any remaining state. */ bzero((caddr_t)&sp->cis, ((sizeof (cis_info_t)) * CS_MAX_CIS)); sp->cis_flags = 0; sp->nfuncs = 0; return (CS_SUCCESS); } /* * cis_store_cis_addr - saves the current CIS address and space type * of the beginning of the tuple into the passed linked list element. * Note that this function will decrement the CIS address by two * elements prior to storing it to the linked list element to point * to the tuple type byte. * * This function also sets the following flags in tp->flags if they are set * in ptr->flags: * * CISTPLF_GLOBAL_CIS - tuple in global CIS * CISTPLF_MF_CIS - tuple in function-specific CIS */ static void cis_store_cis_addr(cistpl_t *tp, cisptr_t *ptr) { if (ptr->flags & CISTPLF_AM_SPACE) tp->offset = ptr->offset - 4; else tp->offset = ptr->offset - 2; tp->flags &= ~(CISTPLF_SPACE_MASK | CISTPLF_FROM_MASK | CISTPLF_GLOBAL_CIS | CISTPLF_MF_CIS); tp->flags |= (ptr->flags & (CISTPLF_SPACE_MASK | CISTPLF_GLOBAL_CIS | CISTPLF_MF_CIS)); if (tp->flags & CISTPLF_AM_SPACE) tp->flags |= CISTPLF_FROM_AM; if (tp->flags & CISTPLF_CM_SPACE) tp->flags |= CISTPLF_FROM_CM; }