diff options
Diffstat (limited to 'src/read-cis.c')
-rw-r--r-- | src/read-cis.c | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/src/read-cis.c b/src/read-cis.c new file mode 100644 index 0000000..9ee71e2 --- /dev/null +++ b/src/read-cis.c @@ -0,0 +1,275 @@ +/* + * 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 + * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds + * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + * + * (C) 1999 David A. Hinds + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <syslog.h> +#include <linux/limits.h> + +#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<MAX_TUPLES; i++) { + ret = fgetc(fd); + if (ret == EOF) { + cis_length = i + 1; + break; + } + cis_copy[i] = (unsigned char) ret; + } + fclose(fd); + + if (cis_length < 4) + return -EINVAL; + + functions = 1; + + tuple.DesiredTuple = CISTPL_LONGLINK_MFC; + tuple.Attributes = TUPLE_RETURN_COMMON; + + ret = pcmcia_get_first_tuple(BIND_FN_ALL, &tuple); + if (ret) + functions = 1; + + tuple.TupleData = buf; + tuple.TupleOffset = 0; + tuple.TupleDataMax = 255; + ret = pcmcia_get_tuple_data(&tuple); + if (ret) + return -EBADF; + + functions = tuple.TupleData[0]; + + return 0; +} |