// SPDX-License-Identifier: GPL-2.0-only /* * cistpl.c -- 16-bit PCMCIA Card Information Structure parser * * 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
*/
/* Convert an extended speed byte to a time in nanoseconds */ #define SPEED_CVT(v) \
(mantissa[(((v)>>3)&15)-1] * exponent[(v)&7] / 10) /* Convert a power byte to a current in 0.1 microamps */ #define POWER_CVT(v) \
(mantissa[((v)>>3)&15] * exponent[(v)&7] / 10) #define POWER_SCALE(v) (exponent[(v)&7])
/* Upper limit on reasonable # of tuples */ #define MAX_TUPLES 200
/* Bits in IRQInfo1 field */ #define IRQ_INFO2_VALID 0x10
/* * set_cis_map() - map the card memory at "card_offset" into virtual space. * * If flags & MAP_ATTRIB, map the attribute space, otherwise * map the memory space. * * Must be called with ops_mutex held.
*/ staticvoid __iomem *set_cis_map(struct pcmcia_socket *s, unsignedint card_offset, unsignedint flags)
{
pccard_mem_map *mem = &s->cis_mem; int ret;
ret = s->ops->set_mem_map(s, mem); if (ret) {
iounmap(s->cis_virt);
s->cis_virt = NULL; return NULL;
}
if (s->features & SS_CAP_STATIC_MAP) { if (s->cis_virt)
iounmap(s->cis_virt);
s->cis_virt = ioremap(mem->static_start, s->map_size);
}
return s->cis_virt;
}
/* Bits in attr field */ #define IS_ATTR 1 #define IS_INDIRECT 8
/* * pcmcia_read_cis_mem() - low-level function to read CIS memory * * must be called with ops_mutex held
*/ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
u_int len, void *ptr)
{ void __iomem *sys, *end; unsignedchar *buf = ptr;
if (attr & IS_INDIRECT) { /* Indirect accesses use a bunch of special registers at fixed
locations in common memory */
u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN; if (attr & IS_ATTR) {
addr *= 2;
flags = ICTRL0_AUTOINC;
}
/* * pcmcia_write_cis_mem() - low-level function to write CIS memory * * Probably only useful for writing one-byte registers. Must be called * with ops_mutex held.
*/ int pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
u_int len, void *ptr)
{ void __iomem *sys, *end; unsignedchar *buf = ptr;
if (attr & IS_INDIRECT) { /* Indirect accesses use a bunch of special registers at fixed
locations in common memory */
u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN; if (attr & IS_ATTR) {
addr *= 2;
flags = ICTRL0_AUTOINC;
}
/* * read_cis_cache() - read CIS memory or its associated cache * * This is a wrapper around read_cis_mem, with the same interface, * but which caches information, for cards whose CIS may not be * readable all the time.
*/ staticint read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr,
size_t len, void *ptr)
{ struct cis_cache_entry *cis; int ret = 0;
if (s->state & SOCKET_CARDBUS) return -EINVAL;
mutex_lock(&s->ops_mutex); if (s->fake_cis) { if (s->fake_cis_len >= addr+len)
memcpy(ptr, s->fake_cis+addr, len); else {
memset(ptr, 0xff, len);
ret = -EINVAL;
}
mutex_unlock(&s->ops_mutex); return ret;
}
/** * destroy_cis_cache() - destroy the CIS cache * @s: pcmcia_socket for which CIS cache shall be destroyed * * This destroys the CIS cache but keeps any fake CIS alive. Must be * called with ops_mutex held.
*/ void destroy_cis_cache(struct pcmcia_socket *s)
{ struct list_head *l, *n; struct cis_cache_entry *cis;
/* * verify_cis_cache() - does the CIS match what is in the CIS cache?
*/ int verify_cis_cache(struct pcmcia_socket *s)
{ struct cis_cache_entry *cis; char *buf; int ret;
if (s->state & SOCKET_CARDBUS) return -EINVAL;
buf = kmalloc(256, GFP_KERNEL); if (buf == NULL) {
dev_warn(&s->dev, "no memory for verifying CIS\n"); return -ENOMEM;
}
mutex_lock(&s->ops_mutex);
list_for_each_entry(cis, &s->cis_cache, node) { int len = cis->len;
if (MFC_FN(tuple->Flags)) { /* Get indirect link from the MFC tuple */
ret = read_cis_cache(s, LINK_SPACE(tuple->Flags),
tuple->LinkOffset, 5, link); if (ret) return -1;
ofs = get_unaligned_le32(link + 1);
SPACE(tuple->Flags) = (link[0] == CISTPL_MFC_ATTR); /* Move to the next indirect link */
tuple->LinkOffset += 5;
MFC_FN(tuple->Flags)--;
} elseif (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... */
ret = read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link); if (ret) return -1; if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
(strncmp(link+2, "CIS", 3) == 0)) return ofs;
remove_cis_cache(s, SPACE(tuple->Flags), ofs, 5); /* Then, we try the wrong spot... */
ofs = ofs >> 1;
}
ret = read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link); if (ret) return -1; if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
(strncmp(link+2, "CIS", 3) == 0)) return ofs;
remove_cis_cache(s, SPACE(tuple->Flags), ofs, 5); return -1;
}
int pccard_get_next_tuple(struct pcmcia_socket *s, unsignedint function,
tuple_t *tuple)
{
u_char link[2], tmp; int ofs, i, attr; int ret;
if (!s) return -EINVAL; if (!(s->state & SOCKET_PRESENT) || (s->state & SOCKET_CARDBUS)) return -ENODEV;
for (i = 0; i < MAX_TUPLES; i++) { if (link[1] == 0xff)
link[0] = CISTPL_END; else {
ret = read_cis_cache(s, attr, ofs, 2, link); if (ret) return -1; if (link[0] == CISTPL_NULL) {
ofs++; continue;
}
}
/* End of chain? Follow long link if possible */ if (link[0] == CISTPL_END) {
ofs = follow_link(s, tuple); if (ofs < 0) return -ENOSPC;
attr = SPACE(tuple->Flags);
ret = read_cis_cache(s, attr, ofs, 2, link); if (ret) return -1;
}
/* 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;
ret = read_cis_cache(s, attr, ofs+2, 4,
&tuple->LinkOffset); if (ret) return -1; break; case CISTPL_LONGLINK_C:
HAS_LINK(tuple->Flags) = 1;
LINK_SPACE(tuple->Flags) = attr & ~IS_ATTR;
ret = read_cis_cache(s, attr, ofs+2, 4,
&tuple->LinkOffset); if (ret) return -1; 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 */
ret = read_cis_cache(s, attr, ofs+2,
1, &tmp); if (ret) return -1;
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) {
dev_dbg(&s->dev, "cs: overrun in pcmcia_get_next_tuple\n"); return -ENOSPC;
}
int pcmcia_parse_tuple(tuple_t *tuple, cisparse_t *parse)
{ int ret = 0;
if (tuple->TupleDataLen > tuple->TupleDataMax) return -EINVAL; switch (tuple->TupleCode) { case CISTPL_DEVICE: case CISTPL_DEVICE_A:
ret = parse_device(tuple, &parse->device); break; case CISTPL_CHECKSUM:
ret = parse_checksum(tuple, &parse->checksum); break; case CISTPL_LONGLINK_A: case CISTPL_LONGLINK_C:
ret = parse_longlink(tuple, &parse->longlink); break; case CISTPL_LONGLINK_MFC:
ret = parse_longlink_mfc(tuple, &parse->longlink_mfc); break; case CISTPL_VERS_1:
ret = parse_vers_1(tuple, &parse->version_1); break; case CISTPL_ALTSTR:
ret = parse_altstr(tuple, &parse->altstr); break; case CISTPL_JEDEC_A: case CISTPL_JEDEC_C:
ret = parse_jedec(tuple, &parse->jedec); break; case CISTPL_MANFID:
ret = parse_manfid(tuple, &parse->manfid); break; case CISTPL_FUNCID:
ret = parse_funcid(tuple, &parse->funcid); break; case CISTPL_FUNCE:
ret = parse_funce(tuple, &parse->funce); break; case CISTPL_CONFIG:
ret = parse_config(tuple, &parse->config); break; case CISTPL_CFTABLE_ENTRY:
ret = parse_cftable_entry(tuple, &parse->cftable_entry); break; case CISTPL_DEVICE_GEO: case CISTPL_DEVICE_GEO_A:
ret = parse_device_geo(tuple, &parse->device_geo); break; case CISTPL_VERS_2:
ret = parse_vers_2(tuple, &parse->vers_2); break; case CISTPL_ORG:
ret = parse_org(tuple, &parse->org); break; case CISTPL_FORMAT: case CISTPL_FORMAT_A:
ret = parse_format(tuple, &parse->format); break; case CISTPL_NO_LINK: case CISTPL_LINKTARGET:
ret = 0; break; default:
ret = -EINVAL; break;
} if (ret)
pr_debug("parse_tuple failed %d\n", ret); return ret;
}
EXPORT_SYMBOL(pcmcia_parse_tuple);
/** * pccard_validate_cis() - check whether card has a sensible CIS * @s: the struct pcmcia_socket we are to check * @info: returns the number of tuples in the (valid) CIS, or 0 * * This tries to determine if a card has a sensible CIS. In @info, it * returns the number of tuples in the CIS, or 0 if the CIS looks bad. The * checks include making sure several critical tuples are present and * valid; seeing if the total number of tuples is reasonable; and * looking for tuples that use reserved codes. * * The function returns 0 on success.
*/ int pccard_validate_cis(struct pcmcia_socket *s, unsignedint *info)
{
tuple_t *tuple;
cisparse_t *p; unsignedint count = 0; int ret, reserved, dev_ok = 0, ident_ok = 0;
/* First tuple should be DEVICE; we should really have either that
or a CFTABLE_ENTRY of some sort */ if ((tuple->TupleCode == CISTPL_DEVICE) ||
(!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY, p)) ||
(!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY_CB, p)))
dev_ok++;
/* All cards should have a MANFID tuple, and/or a VERS_1 or VERS_2 tuple, for card identification. Certain old D-Link and Linksys
cards have only a broken VERS_2 tuple; hence the bogus test. */ if ((pccard_read_tuple(s, BIND_FN_ALL, CISTPL_MANFID, p) == 0) ||
(pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_1, p) == 0) ||
(pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_2, p) != -ENOSPC))
ident_ok++;
done: /* invalidate CIS cache on failure */ if (!dev_ok || !ident_ok || !count) {
mutex_lock(&s->ops_mutex);
destroy_cis_cache(s);
mutex_unlock(&s->ops_mutex); /* We differentiate between dev_ok, ident_ok and count failures to allow for an override for anonymous cards
in ds.c */ if (!dev_ok || !ident_ok)
ret = -EIO; else
ret = -EFAULT;
}
if (info)
*info = count;
kfree(tuple);
kfree(p); return ret;
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.17Bemerkung:
(vorverarbeitet)
¤