blob: 0c2fb48f673c7277e1e5b8ade4b5f9623abe0d15 [file] [log] [blame]
/*
* 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 <sysfs/libsysfs.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;
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;
}
static int follow_link(tuple_t *tuple)
{
unsigned char link[5];
unsigned int ofs;
if (tuple->Flags.mfc_fn) {
/* Get indirect link from the MFC tuple */
read_cis(tuple->Flags.link_space,
tuple->LinkOffset, 5, link);
ofs = *(u_int *)(link+1);
tuple->Flags.space = (link[0] == CISTPL_MFC_ATTR);
/* Move to the next indirect link */
tuple->LinkOffset += 5;
tuple->Flags.mfc_fn--;
} else if (tuple->Flags.has_link) {
ofs = tuple->LinkOffset;
tuple->Flags.space = tuple->Flags.link_space;
tuple->Flags.has_link = 0;
} else {
return -1;
}
if (tuple->Flags.space) {
/* This is ugly, but a common CIS error is to code the long
link offset incorrectly, so we check the right spot... */
read_cis(tuple->Flags.space, 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(tuple->Flags.space, 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 = tuple->Flags.space;
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) {
ofs = follow_link(tuple);
if (ofs < 0)
return -ENODEV;
attr = tuple->Flags.space;
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:
tuple->Flags.has_link = 1;
tuple->Flags.link_space = attr | IS_ATTR;
read_cis(attr, ofs+2, 4, &tuple->LinkOffset);
break;
case CISTPL_LONGLINK_C:
tuple->Flags.has_link = 1;
tuple->Flags.link_space = attr & ~IS_ATTR;
read_cis(attr, ofs+2, 4, &tuple->LinkOffset);
break;
case CISTPL_INDIRECT:
tuple->Flags.has_link = 1;
tuple->Flags.link_space = IS_ATTR | IS_INDIRECT;
tuple->LinkOffset = 0;
break;
case CISTPL_LONGLINK_MFC:
tuple->LinkOffset = ofs + 3;
tuple->Flags.link_space = attr;
if (function == BIND_FN_ALL) {
/* Follow all the MFC links */
read_cis(attr, ofs+2, 1, &tmp);
tuple->Flags.mfc_fn = tmp;
} else {
/* Follow exactly one of the links */
tuple->Flags.mfc_fn = 1;
tuple->LinkOffset += function * 5;
}
break;
case CISTPL_NO_LINK:
tuple->Flags.has_link = 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;
}
int pcmcia_get_first_tuple(unsigned int function, tuple_t *tuple)
{
tuple->TupleLink = 0;
tuple->Flags.link_space = tuple->Flags.mfc_fn = 0;
/* Assume presence of a LONGLINK_C to address 0 */
tuple->CISOffset = tuple->LinkOffset = 0;
tuple->Flags.space = tuple->Flags.has_link = 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);
}
#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(tuple->Flags.space,
tuple->CISOffset + tuple->TupleOffset,
_MIN(len, tuple->TupleDataMax),
tuple->TupleData);
return 0;
}
int read_out_cis(unsigned int socket_no, FILE *fd)
{
char file[SYSFS_PATH_MAX];
int ret, i;
tuple_t tuple;
unsigned char buf[256];
snprintf(file, SYSFS_PATH_MAX, 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;
}