/* * read-cis.c * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * The initial developer of the original code is David A. Hinds * . Portions created by David A. Hinds * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. * * (C) 1999 David A. Hinds */ #include #include #include #include #include #include #include "cistpl.h" #define MAX_TUPLES 0x200 #define PATH_TO_SOCKET "/sys/class/pcmcia_socket/" /* Bits in attr field */ #define IS_ATTR 1 #define IS_INDIRECT 8 static unsigned int functions; static unsigned char cis_copy[MAX_TUPLES]; static unsigned int cis_length = MAX_TUPLES; #define SPACE(f) (((tuple_flags *)(&(f)))->space) #define HAS_LINK(f) (((tuple_flags *)(&(f)))->has_link) #define LINK_SPACE(f) (((tuple_flags *)(&(f)))->link_space) #define MFC_FN(f) (((tuple_flags *)(&(f)))->mfc_fn) static void read_cis(int attr, unsigned int addr, unsigned int len, void *ptr) { if (cis_length > addr+len) memcpy(ptr, cis_copy+addr, len); else memset(ptr, 0xff, len); return; } int pcmcia_get_next_tuple(unsigned int function, tuple_t *tuple); int pcmcia_get_first_tuple(unsigned int function, tuple_t *tuple) { tuple->TupleLink = tuple->Flags = 0; { /* Assume presence of a LONGLINK_C to address 0 */ tuple->CISOffset = tuple->LinkOffset = 0; SPACE(tuple->Flags) = HAS_LINK(tuple->Flags) = 1; } if ((functions > 1) && !(tuple->Attributes & TUPLE_RETURN_COMMON)) { unsigned char req = tuple->DesiredTuple; tuple->DesiredTuple = CISTPL_LONGLINK_MFC; if (!pcmcia_get_next_tuple(function, tuple)) { tuple->DesiredTuple = CISTPL_LINKTARGET; if (pcmcia_get_next_tuple(function, tuple)) return -ENODEV; } else tuple->CISOffset = tuple->TupleLink = 0; tuple->DesiredTuple = req; } return pcmcia_get_next_tuple(function, tuple); } static int follow_link(tuple_t *tuple) { char link[5]; unsigned int ofs; if (MFC_FN(tuple->Flags)) { /* Get indirect link from the MFC tuple */ read_cis(LINK_SPACE(tuple->Flags), tuple->LinkOffset, 5, link); ofs = *(u_int *)(link+1); SPACE(tuple->Flags) = (link[0] == CISTPL_MFC_ATTR); /* Move to the next indirect link */ tuple->LinkOffset += 5; MFC_FN(tuple->Flags)--; } else if (HAS_LINK(tuple->Flags)) { ofs = tuple->LinkOffset; SPACE(tuple->Flags) = LINK_SPACE(tuple->Flags); HAS_LINK(tuple->Flags) = 0; } else { return -1; } if (SPACE(tuple->Flags)) { /* This is ugly, but a common CIS error is to code the long link offset incorrectly, so we check the right spot... */ read_cis(SPACE(tuple->Flags), ofs, 5, link); if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) && (strncmp(link+2, "CIS", 3) == 0)) return ofs; /* Then, we try the wrong spot... */ ofs = ofs >> 1; } read_cis(SPACE(tuple->Flags), ofs, 5, link); if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) && (strncmp(link+2, "CIS", 3) == 0)) return ofs; return -1; } int pcmcia_get_next_tuple(unsigned int function, tuple_t *tuple) { unsigned char link[2], tmp; int ofs, i, attr; link[1] = tuple->TupleLink; ofs = tuple->CISOffset + tuple->TupleLink; attr = SPACE(tuple->Flags); for (i = 0; i < MAX_TUPLES; i++) { if (link[1] == 0xff) { link[0] = CISTPL_END; } else { read_cis(attr, ofs, 2, link); if (link[0] == CISTPL_NULL) { ofs++; continue; } } /* End of chain? Follow long link if possible */ if (link[0] == CISTPL_END) { if ((ofs = follow_link(tuple)) < 0) return -ENODEV; attr = SPACE(tuple->Flags); read_cis(attr, ofs, 2, link); } /* Is this a link tuple? Make a note of it */ if ((link[0] == CISTPL_LONGLINK_A) || (link[0] == CISTPL_LONGLINK_C) || (link[0] == CISTPL_LONGLINK_MFC) || (link[0] == CISTPL_LINKTARGET) || (link[0] == CISTPL_INDIRECT) || (link[0] == CISTPL_NO_LINK)) { switch (link[0]) { case CISTPL_LONGLINK_A: HAS_LINK(tuple->Flags) = 1; LINK_SPACE(tuple->Flags) = attr | IS_ATTR; read_cis(attr, ofs+2, 4, &tuple->LinkOffset); break; case CISTPL_LONGLINK_C: HAS_LINK(tuple->Flags) = 1; LINK_SPACE(tuple->Flags) = attr & ~IS_ATTR; read_cis(attr, ofs+2, 4, &tuple->LinkOffset); break; case CISTPL_INDIRECT: HAS_LINK(tuple->Flags) = 1; LINK_SPACE(tuple->Flags) = IS_ATTR | IS_INDIRECT; tuple->LinkOffset = 0; break; case CISTPL_LONGLINK_MFC: tuple->LinkOffset = ofs + 3; LINK_SPACE(tuple->Flags) = attr; if (function == BIND_FN_ALL) { /* Follow all the MFC links */ read_cis(attr, ofs+2, 1, &tmp); MFC_FN(tuple->Flags) = tmp; } else { /* Follow exactly one of the links */ MFC_FN(tuple->Flags) = 1; tuple->LinkOffset += function * 5; } break; case CISTPL_NO_LINK: HAS_LINK(tuple->Flags) = 0; break; } if ((tuple->Attributes & TUPLE_RETURN_LINK) && (tuple->DesiredTuple == RETURN_FIRST_TUPLE)) break; } else if (tuple->DesiredTuple == RETURN_FIRST_TUPLE) break; if (link[0] == tuple->DesiredTuple) break; ofs += link[1] + 2; } if (i == MAX_TUPLES) return -ENODEV; tuple->TupleCode = link[0]; tuple->TupleLink = link[1]; tuple->CISOffset = ofs + 2; return 0; } #define _MIN(a, b) (((a) < (b)) ? (a) : (b)) int pcmcia_get_tuple_data(tuple_t *tuple) { unsigned int len; if (tuple->TupleLink < tuple->TupleOffset) return -ENODEV; len = tuple->TupleLink - tuple->TupleOffset; tuple->TupleDataLen = tuple->TupleLink; if (len == 0) return 0; read_cis (SPACE(tuple->Flags), tuple->CISOffset + tuple->TupleOffset, _MIN(len, tuple->TupleDataMax), tuple->TupleData); return 0; } int read_out_cis (unsigned int socket_no, FILE *fd) { char file[PATH_MAX]; int ret, i; tuple_t tuple; unsigned char buf[256]; snprintf(file, sizeof(file), PATH_TO_SOCKET "pcmcia_socket%d/cis", socket_no); if (!fd) { fd = fopen(file, "r"); if (!fd) return -EIO; } for (i=0; i