|
@@ -64,6 +64,7 @@ module_param(cis_width, int, 0444);
|
|
|
|
|
|
void release_cis_mem(struct pcmcia_socket *s)
|
|
void release_cis_mem(struct pcmcia_socket *s)
|
|
{
|
|
{
|
|
|
|
+ mutex_lock(&s->ops_mutex);
|
|
if (s->cis_mem.flags & MAP_ACTIVE) {
|
|
if (s->cis_mem.flags & MAP_ACTIVE) {
|
|
s->cis_mem.flags &= ~MAP_ACTIVE;
|
|
s->cis_mem.flags &= ~MAP_ACTIVE;
|
|
s->ops->set_mem_map(s, &s->cis_mem);
|
|
s->ops->set_mem_map(s, &s->cis_mem);
|
|
@@ -75,13 +76,15 @@ void release_cis_mem(struct pcmcia_socket *s)
|
|
iounmap(s->cis_virt);
|
|
iounmap(s->cis_virt);
|
|
s->cis_virt = NULL;
|
|
s->cis_virt = NULL;
|
|
}
|
|
}
|
|
|
|
+ mutex_unlock(&s->ops_mutex);
|
|
}
|
|
}
|
|
-EXPORT_SYMBOL(release_cis_mem);
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
* Map the card memory at "card_offset" into virtual space.
|
|
* Map the card memory at "card_offset" into virtual space.
|
|
* If flags & MAP_ATTRIB, map the attribute space, otherwise
|
|
* If flags & MAP_ATTRIB, map the attribute space, otherwise
|
|
* map the memory space.
|
|
* map the memory space.
|
|
|
|
+ *
|
|
|
|
+ * Must be called with ops_mutex held.
|
|
*/
|
|
*/
|
|
static void __iomem *
|
|
static void __iomem *
|
|
set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flags)
|
|
set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flags)
|
|
@@ -140,6 +143,7 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
|
|
|
|
|
|
dev_dbg(&s->dev, "pcmcia_read_cis_mem(%d, %#x, %u)\n", attr, addr, len);
|
|
dev_dbg(&s->dev, "pcmcia_read_cis_mem(%d, %#x, %u)\n", attr, addr, len);
|
|
|
|
|
|
|
|
+ mutex_lock(&s->ops_mutex);
|
|
if (attr & IS_INDIRECT) {
|
|
if (attr & IS_INDIRECT) {
|
|
/* Indirect accesses use a bunch of special registers at fixed
|
|
/* Indirect accesses use a bunch of special registers at fixed
|
|
locations in common memory */
|
|
locations in common memory */
|
|
@@ -151,7 +155,9 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
|
|
|
|
|
|
sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0));
|
|
sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0));
|
|
if (!sys) {
|
|
if (!sys) {
|
|
|
|
+ dev_dbg(&s->dev, "could not map memory\n");
|
|
memset(ptr, 0xff, len);
|
|
memset(ptr, 0xff, len);
|
|
|
|
+ mutex_unlock(&s->ops_mutex);
|
|
return -1;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -165,6 +171,9 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
|
|
} else {
|
|
} else {
|
|
u_int inc = 1, card_offset, flags;
|
|
u_int inc = 1, card_offset, flags;
|
|
|
|
|
|
|
|
+ if (addr > CISTPL_MAX_CIS_SIZE)
|
|
|
|
+ dev_dbg(&s->dev, "attempt to read CIS mem at addr %#x", addr);
|
|
|
|
+
|
|
flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0);
|
|
flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0);
|
|
if (attr) {
|
|
if (attr) {
|
|
flags |= MAP_ATTRIB;
|
|
flags |= MAP_ATTRIB;
|
|
@@ -176,7 +185,9 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
|
|
while (len) {
|
|
while (len) {
|
|
sys = set_cis_map(s, card_offset, flags);
|
|
sys = set_cis_map(s, card_offset, flags);
|
|
if (!sys) {
|
|
if (!sys) {
|
|
|
|
+ dev_dbg(&s->dev, "could not map memory\n");
|
|
memset(ptr, 0xff, len);
|
|
memset(ptr, 0xff, len);
|
|
|
|
+ mutex_unlock(&s->ops_mutex);
|
|
return -1;
|
|
return -1;
|
|
}
|
|
}
|
|
end = sys + s->map_size;
|
|
end = sys + s->map_size;
|
|
@@ -190,12 +201,12 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
|
|
addr = 0;
|
|
addr = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ mutex_unlock(&s->ops_mutex);
|
|
dev_dbg(&s->dev, " %#2.2x %#2.2x %#2.2x %#2.2x ...\n",
|
|
dev_dbg(&s->dev, " %#2.2x %#2.2x %#2.2x %#2.2x ...\n",
|
|
*(u_char *)(ptr+0), *(u_char *)(ptr+1),
|
|
*(u_char *)(ptr+0), *(u_char *)(ptr+1),
|
|
*(u_char *)(ptr+2), *(u_char *)(ptr+3));
|
|
*(u_char *)(ptr+2), *(u_char *)(ptr+3));
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
-EXPORT_SYMBOL(pcmcia_read_cis_mem);
|
|
|
|
|
|
|
|
|
|
|
|
void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
|
|
void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
|
|
@@ -206,6 +217,7 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
|
|
|
|
|
|
dev_dbg(&s->dev, "pcmcia_write_cis_mem(%d, %#x, %u)\n", attr, addr, len);
|
|
dev_dbg(&s->dev, "pcmcia_write_cis_mem(%d, %#x, %u)\n", attr, addr, len);
|
|
|
|
|
|
|
|
+ mutex_lock(&s->ops_mutex);
|
|
if (attr & IS_INDIRECT) {
|
|
if (attr & IS_INDIRECT) {
|
|
/* Indirect accesses use a bunch of special registers at fixed
|
|
/* Indirect accesses use a bunch of special registers at fixed
|
|
locations in common memory */
|
|
locations in common memory */
|
|
@@ -216,8 +228,11 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
|
|
}
|
|
}
|
|
|
|
|
|
sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0));
|
|
sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0));
|
|
- if (!sys)
|
|
|
|
|
|
+ if (!sys) {
|
|
|
|
+ dev_dbg(&s->dev, "could not map memory\n");
|
|
|
|
+ mutex_unlock(&s->ops_mutex);
|
|
return; /* FIXME: Error */
|
|
return; /* FIXME: Error */
|
|
|
|
+ }
|
|
|
|
|
|
writeb(flags, sys+CISREG_ICTRL0);
|
|
writeb(flags, sys+CISREG_ICTRL0);
|
|
writeb(addr & 0xff, sys+CISREG_IADDR0);
|
|
writeb(addr & 0xff, sys+CISREG_IADDR0);
|
|
@@ -239,8 +254,11 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
|
|
card_offset = addr & ~(s->map_size-1);
|
|
card_offset = addr & ~(s->map_size-1);
|
|
while (len) {
|
|
while (len) {
|
|
sys = set_cis_map(s, card_offset, flags);
|
|
sys = set_cis_map(s, card_offset, flags);
|
|
- if (!sys)
|
|
|
|
|
|
+ if (!sys) {
|
|
|
|
+ dev_dbg(&s->dev, "could not map memory\n");
|
|
|
|
+ mutex_unlock(&s->ops_mutex);
|
|
return; /* FIXME: error */
|
|
return; /* FIXME: error */
|
|
|
|
+ }
|
|
|
|
|
|
end = sys + s->map_size;
|
|
end = sys + s->map_size;
|
|
sys = sys + (addr & (s->map_size-1));
|
|
sys = sys + (addr & (s->map_size-1));
|
|
@@ -253,8 +271,8 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
|
|
addr = 0;
|
|
addr = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ mutex_unlock(&s->ops_mutex);
|
|
}
|
|
}
|
|
-EXPORT_SYMBOL(pcmcia_write_cis_mem);
|
|
|
|
|
|
|
|
|
|
|
|
/*======================================================================
|
|
/*======================================================================
|
|
@@ -265,32 +283,36 @@ EXPORT_SYMBOL(pcmcia_write_cis_mem);
|
|
|
|
|
|
======================================================================*/
|
|
======================================================================*/
|
|
|
|
|
|
-static void read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr,
|
|
|
|
- size_t len, void *ptr)
|
|
|
|
|
|
+static int read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr,
|
|
|
|
+ size_t len, void *ptr)
|
|
{
|
|
{
|
|
- struct cis_cache_entry *cis;
|
|
|
|
- int ret;
|
|
|
|
|
|
+ struct cis_cache_entry *cis;
|
|
|
|
+ int ret = 0;
|
|
|
|
|
|
- if (s->fake_cis) {
|
|
|
|
- if (s->fake_cis_len >= addr+len)
|
|
|
|
- memcpy(ptr, s->fake_cis+addr, len);
|
|
|
|
- else
|
|
|
|
- memset(ptr, 0xff, len);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
|
|
+ if (s->state & SOCKET_CARDBUS)
|
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- list_for_each_entry(cis, &s->cis_cache, node) {
|
|
|
|
- if (cis->addr == addr && cis->len == len && cis->attr == attr) {
|
|
|
|
- memcpy(ptr, cis->cache, len);
|
|
|
|
- return;
|
|
|
|
|
|
+ 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;
|
|
}
|
|
}
|
|
- }
|
|
|
|
|
|
|
|
-#ifdef CONFIG_CARDBUS
|
|
|
|
- if (s->state & SOCKET_CARDBUS)
|
|
|
|
- ret = read_cb_mem(s, attr, addr, len, ptr);
|
|
|
|
- else
|
|
|
|
-#endif
|
|
|
|
|
|
+ list_for_each_entry(cis, &s->cis_cache, node) {
|
|
|
|
+ if (cis->addr == addr && cis->len == len && cis->attr == attr) {
|
|
|
|
+ memcpy(ptr, cis->cache, len);
|
|
|
|
+ mutex_unlock(&s->ops_mutex);
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ mutex_unlock(&s->ops_mutex);
|
|
|
|
+
|
|
ret = pcmcia_read_cis_mem(s, attr, addr, len, ptr);
|
|
ret = pcmcia_read_cis_mem(s, attr, addr, len, ptr);
|
|
|
|
|
|
if (ret == 0) {
|
|
if (ret == 0) {
|
|
@@ -301,9 +323,12 @@ static void read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr,
|
|
cis->len = len;
|
|
cis->len = len;
|
|
cis->attr = attr;
|
|
cis->attr = attr;
|
|
memcpy(cis->cache, ptr, len);
|
|
memcpy(cis->cache, ptr, len);
|
|
|
|
+ mutex_lock(&s->ops_mutex);
|
|
list_add(&cis->node, &s->cis_cache);
|
|
list_add(&cis->node, &s->cis_cache);
|
|
|
|
+ mutex_unlock(&s->ops_mutex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
static void
|
|
@@ -311,32 +336,35 @@ remove_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, u_int len)
|
|
{
|
|
{
|
|
struct cis_cache_entry *cis;
|
|
struct cis_cache_entry *cis;
|
|
|
|
|
|
|
|
+ mutex_lock(&s->ops_mutex);
|
|
list_for_each_entry(cis, &s->cis_cache, node)
|
|
list_for_each_entry(cis, &s->cis_cache, node)
|
|
if (cis->addr == addr && cis->len == len && cis->attr == attr) {
|
|
if (cis->addr == addr && cis->len == len && cis->attr == attr) {
|
|
list_del(&cis->node);
|
|
list_del(&cis->node);
|
|
kfree(cis);
|
|
kfree(cis);
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
+ mutex_unlock(&s->ops_mutex);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * 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)
|
|
void destroy_cis_cache(struct pcmcia_socket *s)
|
|
{
|
|
{
|
|
struct list_head *l, *n;
|
|
struct list_head *l, *n;
|
|
|
|
+ struct cis_cache_entry *cis;
|
|
|
|
|
|
list_for_each_safe(l, n, &s->cis_cache) {
|
|
list_for_each_safe(l, n, &s->cis_cache) {
|
|
- struct cis_cache_entry *cis = list_entry(l, struct cis_cache_entry, node);
|
|
|
|
-
|
|
|
|
|
|
+ cis = list_entry(l, struct cis_cache_entry, node);
|
|
list_del(&cis->node);
|
|
list_del(&cis->node);
|
|
kfree(cis);
|
|
kfree(cis);
|
|
}
|
|
}
|
|
-
|
|
|
|
- /*
|
|
|
|
- * If there was a fake CIS, destroy that as well.
|
|
|
|
- */
|
|
|
|
- kfree(s->fake_cis);
|
|
|
|
- s->fake_cis = NULL;
|
|
|
|
}
|
|
}
|
|
-EXPORT_SYMBOL(destroy_cis_cache);
|
|
|
|
|
|
|
|
/*======================================================================
|
|
/*======================================================================
|
|
|
|
|
|
@@ -349,6 +377,10 @@ int verify_cis_cache(struct pcmcia_socket *s)
|
|
{
|
|
{
|
|
struct cis_cache_entry *cis;
|
|
struct cis_cache_entry *cis;
|
|
char *buf;
|
|
char *buf;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ if (s->state & SOCKET_CARDBUS)
|
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
buf = kmalloc(256, GFP_KERNEL);
|
|
buf = kmalloc(256, GFP_KERNEL);
|
|
if (buf == NULL) {
|
|
if (buf == NULL) {
|
|
@@ -361,14 +393,9 @@ int verify_cis_cache(struct pcmcia_socket *s)
|
|
|
|
|
|
if (len > 256)
|
|
if (len > 256)
|
|
len = 256;
|
|
len = 256;
|
|
-#ifdef CONFIG_CARDBUS
|
|
|
|
- if (s->state & SOCKET_CARDBUS)
|
|
|
|
- read_cb_mem(s, cis->attr, cis->addr, len, buf);
|
|
|
|
- else
|
|
|
|
-#endif
|
|
|
|
- pcmcia_read_cis_mem(s, cis->attr, cis->addr, len, buf);
|
|
|
|
|
|
|
|
- if (memcmp(buf, cis->cache, len) != 0) {
|
|
|
|
|
|
+ ret = pcmcia_read_cis_mem(s, cis->attr, cis->addr, len, buf);
|
|
|
|
+ if (ret || memcmp(buf, cis->cache, len) != 0) {
|
|
kfree(buf);
|
|
kfree(buf);
|
|
return -1;
|
|
return -1;
|
|
}
|
|
}
|
|
@@ -391,17 +418,20 @@ int pcmcia_replace_cis(struct pcmcia_socket *s,
|
|
dev_printk(KERN_WARNING, &s->dev, "replacement CIS too big\n");
|
|
dev_printk(KERN_WARNING, &s->dev, "replacement CIS too big\n");
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
+ mutex_lock(&s->ops_mutex);
|
|
kfree(s->fake_cis);
|
|
kfree(s->fake_cis);
|
|
s->fake_cis = kmalloc(len, GFP_KERNEL);
|
|
s->fake_cis = kmalloc(len, GFP_KERNEL);
|
|
if (s->fake_cis == NULL) {
|
|
if (s->fake_cis == NULL) {
|
|
dev_printk(KERN_WARNING, &s->dev, "no memory to replace CIS\n");
|
|
dev_printk(KERN_WARNING, &s->dev, "no memory to replace CIS\n");
|
|
|
|
+ mutex_unlock(&s->ops_mutex);
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
s->fake_cis_len = len;
|
|
s->fake_cis_len = len;
|
|
memcpy(s->fake_cis, data, len);
|
|
memcpy(s->fake_cis, data, len);
|
|
|
|
+ dev_info(&s->dev, "Using replacement CIS\n");
|
|
|
|
+ mutex_unlock(&s->ops_mutex);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
-EXPORT_SYMBOL(pcmcia_replace_cis);
|
|
|
|
|
|
|
|
/*======================================================================
|
|
/*======================================================================
|
|
|
|
|
|
@@ -425,25 +455,16 @@ int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function, tuple
|
|
{
|
|
{
|
|
if (!s)
|
|
if (!s)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
- if (!(s->state & SOCKET_PRESENT))
|
|
|
|
|
|
+
|
|
|
|
+ if (!(s->state & SOCKET_PRESENT) || (s->state & SOCKET_CARDBUS))
|
|
return -ENODEV;
|
|
return -ENODEV;
|
|
tuple->TupleLink = tuple->Flags = 0;
|
|
tuple->TupleLink = tuple->Flags = 0;
|
|
-#ifdef CONFIG_CARDBUS
|
|
|
|
- if (s->state & SOCKET_CARDBUS) {
|
|
|
|
- struct pci_dev *dev = s->cb_dev;
|
|
|
|
- u_int ptr;
|
|
|
|
- pci_bus_read_config_dword(dev->subordinate, 0, PCI_CARDBUS_CIS, &ptr);
|
|
|
|
- tuple->CISOffset = ptr & ~7;
|
|
|
|
- SPACE(tuple->Flags) = (ptr & 7);
|
|
|
|
- } else
|
|
|
|
-#endif
|
|
|
|
- {
|
|
|
|
- /* Assume presence of a LONGLINK_C to address 0 */
|
|
|
|
- tuple->CISOffset = tuple->LinkOffset = 0;
|
|
|
|
- SPACE(tuple->Flags) = HAS_LINK(tuple->Flags) = 1;
|
|
|
|
- }
|
|
|
|
- if (!(s->state & SOCKET_CARDBUS) && (s->functions > 1) &&
|
|
|
|
- !(tuple->Attributes & TUPLE_RETURN_COMMON)) {
|
|
|
|
|
|
+
|
|
|
|
+ /* Assume presence of a LONGLINK_C to address 0 */
|
|
|
|
+ tuple->CISOffset = tuple->LinkOffset = 0;
|
|
|
|
+ SPACE(tuple->Flags) = HAS_LINK(tuple->Flags) = 1;
|
|
|
|
+
|
|
|
|
+ if ((s->functions > 1) && !(tuple->Attributes & TUPLE_RETURN_COMMON)) {
|
|
cisdata_t req = tuple->DesiredTuple;
|
|
cisdata_t req = tuple->DesiredTuple;
|
|
tuple->DesiredTuple = CISTPL_LONGLINK_MFC;
|
|
tuple->DesiredTuple = CISTPL_LONGLINK_MFC;
|
|
if (pccard_get_next_tuple(s, function, tuple) == 0) {
|
|
if (pccard_get_next_tuple(s, function, tuple) == 0) {
|
|
@@ -456,17 +477,19 @@ int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function, tuple
|
|
}
|
|
}
|
|
return pccard_get_next_tuple(s, function, tuple);
|
|
return pccard_get_next_tuple(s, function, tuple);
|
|
}
|
|
}
|
|
-EXPORT_SYMBOL(pccard_get_first_tuple);
|
|
|
|
|
|
|
|
static int follow_link(struct pcmcia_socket *s, tuple_t *tuple)
|
|
static int follow_link(struct pcmcia_socket *s, tuple_t *tuple)
|
|
{
|
|
{
|
|
u_char link[5];
|
|
u_char link[5];
|
|
u_int ofs;
|
|
u_int ofs;
|
|
|
|
+ int ret;
|
|
|
|
|
|
if (MFC_FN(tuple->Flags)) {
|
|
if (MFC_FN(tuple->Flags)) {
|
|
/* Get indirect link from the MFC tuple */
|
|
/* Get indirect link from the MFC tuple */
|
|
- read_cis_cache(s, LINK_SPACE(tuple->Flags),
|
|
|
|
|
|
+ ret = read_cis_cache(s, LINK_SPACE(tuple->Flags),
|
|
tuple->LinkOffset, 5, link);
|
|
tuple->LinkOffset, 5, link);
|
|
|
|
+ if (ret)
|
|
|
|
+ return -1;
|
|
ofs = get_unaligned_le32(link + 1);
|
|
ofs = get_unaligned_le32(link + 1);
|
|
SPACE(tuple->Flags) = (link[0] == CISTPL_MFC_ATTR);
|
|
SPACE(tuple->Flags) = (link[0] == CISTPL_MFC_ATTR);
|
|
/* Move to the next indirect link */
|
|
/* Move to the next indirect link */
|
|
@@ -479,10 +502,12 @@ static int follow_link(struct pcmcia_socket *s, tuple_t *tuple)
|
|
} else {
|
|
} else {
|
|
return -1;
|
|
return -1;
|
|
}
|
|
}
|
|
- if (!(s->state & SOCKET_CARDBUS) && SPACE(tuple->Flags)) {
|
|
|
|
|
|
+ if (SPACE(tuple->Flags)) {
|
|
/* This is ugly, but a common CIS error is to code the long
|
|
/* This is ugly, but a common CIS error is to code the long
|
|
link offset incorrectly, so we check the right spot... */
|
|
link offset incorrectly, so we check the right spot... */
|
|
- read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
|
|
|
|
|
|
+ ret = read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
|
|
|
|
+ if (ret)
|
|
|
|
+ return -1;
|
|
if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
|
|
if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
|
|
(strncmp(link+2, "CIS", 3) == 0))
|
|
(strncmp(link+2, "CIS", 3) == 0))
|
|
return ofs;
|
|
return ofs;
|
|
@@ -490,7 +515,9 @@ static int follow_link(struct pcmcia_socket *s, tuple_t *tuple)
|
|
/* Then, we try the wrong spot... */
|
|
/* Then, we try the wrong spot... */
|
|
ofs = ofs >> 1;
|
|
ofs = ofs >> 1;
|
|
}
|
|
}
|
|
- read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
|
|
|
|
|
|
+ ret = read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
|
|
|
|
+ if (ret)
|
|
|
|
+ return -1;
|
|
if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
|
|
if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
|
|
(strncmp(link+2, "CIS", 3) == 0))
|
|
(strncmp(link+2, "CIS", 3) == 0))
|
|
return ofs;
|
|
return ofs;
|
|
@@ -502,10 +529,11 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_
|
|
{
|
|
{
|
|
u_char link[2], tmp;
|
|
u_char link[2], tmp;
|
|
int ofs, i, attr;
|
|
int ofs, i, attr;
|
|
|
|
+ int ret;
|
|
|
|
|
|
if (!s)
|
|
if (!s)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
- if (!(s->state & SOCKET_PRESENT))
|
|
|
|
|
|
+ if (!(s->state & SOCKET_PRESENT) || (s->state & SOCKET_CARDBUS))
|
|
return -ENODEV;
|
|
return -ENODEV;
|
|
|
|
|
|
link[1] = tuple->TupleLink;
|
|
link[1] = tuple->TupleLink;
|
|
@@ -516,7 +544,9 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_
|
|
if (link[1] == 0xff) {
|
|
if (link[1] == 0xff) {
|
|
link[0] = CISTPL_END;
|
|
link[0] = CISTPL_END;
|
|
} else {
|
|
} else {
|
|
- read_cis_cache(s, attr, ofs, 2, link);
|
|
|
|
|
|
+ ret = read_cis_cache(s, attr, ofs, 2, link);
|
|
|
|
+ if (ret)
|
|
|
|
+ return -1;
|
|
if (link[0] == CISTPL_NULL) {
|
|
if (link[0] == CISTPL_NULL) {
|
|
ofs++; continue;
|
|
ofs++; continue;
|
|
}
|
|
}
|
|
@@ -528,7 +558,9 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_
|
|
if (ofs < 0)
|
|
if (ofs < 0)
|
|
return -ENOSPC;
|
|
return -ENOSPC;
|
|
attr = SPACE(tuple->Flags);
|
|
attr = SPACE(tuple->Flags);
|
|
- read_cis_cache(s, attr, ofs, 2, link);
|
|
|
|
|
|
+ ret = read_cis_cache(s, attr, ofs, 2, link);
|
|
|
|
+ if (ret)
|
|
|
|
+ return -1;
|
|
}
|
|
}
|
|
|
|
|
|
/* Is this a link tuple? Make a note of it */
|
|
/* Is this a link tuple? Make a note of it */
|
|
@@ -542,12 +574,16 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_
|
|
case CISTPL_LONGLINK_A:
|
|
case CISTPL_LONGLINK_A:
|
|
HAS_LINK(tuple->Flags) = 1;
|
|
HAS_LINK(tuple->Flags) = 1;
|
|
LINK_SPACE(tuple->Flags) = attr | IS_ATTR;
|
|
LINK_SPACE(tuple->Flags) = attr | IS_ATTR;
|
|
- read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset);
|
|
|
|
|
|
+ ret = read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset);
|
|
|
|
+ if (ret)
|
|
|
|
+ return -1;
|
|
break;
|
|
break;
|
|
case CISTPL_LONGLINK_C:
|
|
case CISTPL_LONGLINK_C:
|
|
HAS_LINK(tuple->Flags) = 1;
|
|
HAS_LINK(tuple->Flags) = 1;
|
|
LINK_SPACE(tuple->Flags) = attr & ~IS_ATTR;
|
|
LINK_SPACE(tuple->Flags) = attr & ~IS_ATTR;
|
|
- read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset);
|
|
|
|
|
|
+ ret = read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset);
|
|
|
|
+ if (ret)
|
|
|
|
+ return -1;
|
|
break;
|
|
break;
|
|
case CISTPL_INDIRECT:
|
|
case CISTPL_INDIRECT:
|
|
HAS_LINK(tuple->Flags) = 1;
|
|
HAS_LINK(tuple->Flags) = 1;
|
|
@@ -559,7 +595,9 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_
|
|
LINK_SPACE(tuple->Flags) = attr;
|
|
LINK_SPACE(tuple->Flags) = attr;
|
|
if (function == BIND_FN_ALL) {
|
|
if (function == BIND_FN_ALL) {
|
|
/* Follow all the MFC links */
|
|
/* Follow all the MFC links */
|
|
- read_cis_cache(s, attr, ofs+2, 1, &tmp);
|
|
|
|
|
|
+ ret = read_cis_cache(s, attr, ofs+2, 1, &tmp);
|
|
|
|
+ if (ret)
|
|
|
|
+ return -1;
|
|
MFC_FN(tuple->Flags) = tmp;
|
|
MFC_FN(tuple->Flags) = tmp;
|
|
} else {
|
|
} else {
|
|
/* Follow exactly one of the links */
|
|
/* Follow exactly one of the links */
|
|
@@ -592,7 +630,6 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_
|
|
tuple->CISOffset = ofs + 2;
|
|
tuple->CISOffset = ofs + 2;
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
-EXPORT_SYMBOL(pccard_get_next_tuple);
|
|
|
|
|
|
|
|
/*====================================================================*/
|
|
/*====================================================================*/
|
|
|
|
|
|
@@ -601,6 +638,7 @@ EXPORT_SYMBOL(pccard_get_next_tuple);
|
|
int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple)
|
|
int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple)
|
|
{
|
|
{
|
|
u_int len;
|
|
u_int len;
|
|
|
|
+ int ret;
|
|
|
|
|
|
if (!s)
|
|
if (!s)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
@@ -611,12 +649,13 @@ int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple)
|
|
tuple->TupleDataLen = tuple->TupleLink;
|
|
tuple->TupleDataLen = tuple->TupleLink;
|
|
if (len == 0)
|
|
if (len == 0)
|
|
return 0;
|
|
return 0;
|
|
- read_cis_cache(s, SPACE(tuple->Flags),
|
|
|
|
|
|
+ ret = read_cis_cache(s, SPACE(tuple->Flags),
|
|
tuple->CISOffset + tuple->TupleOffset,
|
|
tuple->CISOffset + tuple->TupleOffset,
|
|
_MIN(len, tuple->TupleDataMax), tuple->TupleData);
|
|
_MIN(len, tuple->TupleDataMax), tuple->TupleData);
|
|
|
|
+ if (ret)
|
|
|
|
+ return -1;
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
-EXPORT_SYMBOL(pccard_get_tuple_data);
|
|
|
|
|
|
|
|
|
|
|
|
/*======================================================================
|
|
/*======================================================================
|
|
@@ -1190,119 +1229,6 @@ static int parse_cftable_entry(tuple_t *tuple,
|
|
|
|
|
|
/*====================================================================*/
|
|
/*====================================================================*/
|
|
|
|
|
|
-#ifdef CONFIG_CARDBUS
|
|
|
|
-
|
|
|
|
-static int parse_bar(tuple_t *tuple, cistpl_bar_t *bar)
|
|
|
|
-{
|
|
|
|
- u_char *p;
|
|
|
|
- if (tuple->TupleDataLen < 6)
|
|
|
|
- return -EINVAL;
|
|
|
|
- p = (u_char *)tuple->TupleData;
|
|
|
|
- bar->attr = *p;
|
|
|
|
- p += 2;
|
|
|
|
- bar->size = get_unaligned_le32(p);
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static int parse_config_cb(tuple_t *tuple, cistpl_config_t *config)
|
|
|
|
-{
|
|
|
|
- u_char *p;
|
|
|
|
-
|
|
|
|
- p = (u_char *)tuple->TupleData;
|
|
|
|
- if ((*p != 3) || (tuple->TupleDataLen < 6))
|
|
|
|
- return -EINVAL;
|
|
|
|
- config->last_idx = *(++p);
|
|
|
|
- p++;
|
|
|
|
- config->base = get_unaligned_le32(p);
|
|
|
|
- config->subtuples = tuple->TupleDataLen - 6;
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static int parse_cftable_entry_cb(tuple_t *tuple,
|
|
|
|
- cistpl_cftable_entry_cb_t *entry)
|
|
|
|
-{
|
|
|
|
- u_char *p, *q, features;
|
|
|
|
-
|
|
|
|
- p = tuple->TupleData;
|
|
|
|
- q = p + tuple->TupleDataLen;
|
|
|
|
- entry->index = *p & 0x3f;
|
|
|
|
- entry->flags = 0;
|
|
|
|
- if (*p & 0x40)
|
|
|
|
- entry->flags |= CISTPL_CFTABLE_DEFAULT;
|
|
|
|
-
|
|
|
|
- /* Process optional features */
|
|
|
|
- if (++p == q)
|
|
|
|
- return -EINVAL;
|
|
|
|
- features = *p; p++;
|
|
|
|
-
|
|
|
|
- /* Power options */
|
|
|
|
- if ((features & 3) > 0) {
|
|
|
|
- p = parse_power(p, q, &entry->vcc);
|
|
|
|
- if (p == NULL)
|
|
|
|
- return -EINVAL;
|
|
|
|
- } else
|
|
|
|
- entry->vcc.present = 0;
|
|
|
|
- if ((features & 3) > 1) {
|
|
|
|
- p = parse_power(p, q, &entry->vpp1);
|
|
|
|
- if (p == NULL)
|
|
|
|
- return -EINVAL;
|
|
|
|
- } else
|
|
|
|
- entry->vpp1.present = 0;
|
|
|
|
- if ((features & 3) > 2) {
|
|
|
|
- p = parse_power(p, q, &entry->vpp2);
|
|
|
|
- if (p == NULL)
|
|
|
|
- return -EINVAL;
|
|
|
|
- } else
|
|
|
|
- entry->vpp2.present = 0;
|
|
|
|
-
|
|
|
|
- /* I/O window options */
|
|
|
|
- if (features & 0x08) {
|
|
|
|
- if (p == q)
|
|
|
|
- return -EINVAL;
|
|
|
|
- entry->io = *p; p++;
|
|
|
|
- } else
|
|
|
|
- entry->io = 0;
|
|
|
|
-
|
|
|
|
- /* Interrupt options */
|
|
|
|
- if (features & 0x10) {
|
|
|
|
- p = parse_irq(p, q, &entry->irq);
|
|
|
|
- if (p == NULL)
|
|
|
|
- return -EINVAL;
|
|
|
|
- } else
|
|
|
|
- entry->irq.IRQInfo1 = 0;
|
|
|
|
-
|
|
|
|
- if (features & 0x20) {
|
|
|
|
- if (p == q)
|
|
|
|
- return -EINVAL;
|
|
|
|
- entry->mem = *p; p++;
|
|
|
|
- } else
|
|
|
|
- entry->mem = 0;
|
|
|
|
-
|
|
|
|
- /* Misc features */
|
|
|
|
- if (features & 0x80) {
|
|
|
|
- if (p == q)
|
|
|
|
- return -EINVAL;
|
|
|
|
- entry->flags |= (*p << 8);
|
|
|
|
- if (*p & 0x80) {
|
|
|
|
- if (++p == q)
|
|
|
|
- return -EINVAL;
|
|
|
|
- entry->flags |= (*p << 16);
|
|
|
|
- }
|
|
|
|
- while (*p & 0x80)
|
|
|
|
- if (++p == q)
|
|
|
|
- return -EINVAL;
|
|
|
|
- p++;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- entry->subtuples = q-p;
|
|
|
|
-
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-#endif
|
|
|
|
-
|
|
|
|
-/*====================================================================*/
|
|
|
|
-
|
|
|
|
static int parse_device_geo(tuple_t *tuple, cistpl_device_geo_t *geo)
|
|
static int parse_device_geo(tuple_t *tuple, cistpl_device_geo_t *geo)
|
|
{
|
|
{
|
|
u_char *p, *q;
|
|
u_char *p, *q;
|
|
@@ -1404,17 +1330,6 @@ int pcmcia_parse_tuple(tuple_t *tuple, cisparse_t *parse)
|
|
case CISTPL_DEVICE_A:
|
|
case CISTPL_DEVICE_A:
|
|
ret = parse_device(tuple, &parse->device);
|
|
ret = parse_device(tuple, &parse->device);
|
|
break;
|
|
break;
|
|
-#ifdef CONFIG_CARDBUS
|
|
|
|
- case CISTPL_BAR:
|
|
|
|
- ret = parse_bar(tuple, &parse->bar);
|
|
|
|
- break;
|
|
|
|
- case CISTPL_CONFIG_CB:
|
|
|
|
- ret = parse_config_cb(tuple, &parse->config);
|
|
|
|
- break;
|
|
|
|
- case CISTPL_CFTABLE_ENTRY_CB:
|
|
|
|
- ret = parse_cftable_entry_cb(tuple, &parse->cftable_entry_cb);
|
|
|
|
- break;
|
|
|
|
-#endif
|
|
|
|
case CISTPL_CHECKSUM:
|
|
case CISTPL_CHECKSUM:
|
|
ret = parse_checksum(tuple, &parse->checksum);
|
|
ret = parse_checksum(tuple, &parse->checksum);
|
|
break;
|
|
break;
|
|
@@ -1513,7 +1428,6 @@ done:
|
|
kfree(buf);
|
|
kfree(buf);
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
-EXPORT_SYMBOL(pccard_read_tuple);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -1573,84 +1487,238 @@ next_entry:
|
|
kfree(buf);
|
|
kfree(buf);
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
-EXPORT_SYMBOL(pccard_loop_tuple);
|
|
|
|
|
|
|
|
|
|
|
|
-/*======================================================================
|
|
|
|
-
|
|
|
|
- This tries to determine if a card has a sensible CIS. 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.
|
|
|
|
-
|
|
|
|
-======================================================================*/
|
|
|
|
-
|
|
|
|
|
|
+/**
|
|
|
|
+ * 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, unsigned int *info)
|
|
int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *info)
|
|
{
|
|
{
|
|
- tuple_t *tuple;
|
|
|
|
- cisparse_t *p;
|
|
|
|
- unsigned int count = 0;
|
|
|
|
- int ret, reserved, dev_ok = 0, ident_ok = 0;
|
|
|
|
|
|
+ tuple_t *tuple;
|
|
|
|
+ cisparse_t *p;
|
|
|
|
+ unsigned int count = 0;
|
|
|
|
+ int ret, reserved, dev_ok = 0, ident_ok = 0;
|
|
|
|
|
|
- if (!s)
|
|
|
|
- return -EINVAL;
|
|
|
|
|
|
+ if (!s)
|
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- tuple = kmalloc(sizeof(*tuple), GFP_KERNEL);
|
|
|
|
- if (tuple == NULL) {
|
|
|
|
- dev_printk(KERN_WARNING, &s->dev, "no memory to validate CIS\n");
|
|
|
|
- return -ENOMEM;
|
|
|
|
- }
|
|
|
|
- p = kmalloc(sizeof(*p), GFP_KERNEL);
|
|
|
|
- if (p == NULL) {
|
|
|
|
- kfree(tuple);
|
|
|
|
- dev_printk(KERN_WARNING, &s->dev, "no memory to validate CIS\n");
|
|
|
|
- return -ENOMEM;
|
|
|
|
- }
|
|
|
|
|
|
+ /* We do not want to validate the CIS cache... */
|
|
|
|
+ mutex_lock(&s->ops_mutex);
|
|
|
|
+ destroy_cis_cache(s);
|
|
|
|
+ mutex_unlock(&s->ops_mutex);
|
|
|
|
|
|
- count = reserved = 0;
|
|
|
|
- tuple->DesiredTuple = RETURN_FIRST_TUPLE;
|
|
|
|
- tuple->Attributes = TUPLE_RETURN_COMMON;
|
|
|
|
- ret = pccard_get_first_tuple(s, BIND_FN_ALL, tuple);
|
|
|
|
- if (ret != 0)
|
|
|
|
- goto done;
|
|
|
|
-
|
|
|
|
- /* 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) == 0) ||
|
|
|
|
- (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY_CB, p) == 0))
|
|
|
|
- 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++;
|
|
|
|
-
|
|
|
|
- if (!dev_ok && !ident_ok)
|
|
|
|
- goto done;
|
|
|
|
-
|
|
|
|
- for (count = 1; count < MAX_TUPLES; count++) {
|
|
|
|
- ret = pccard_get_next_tuple(s, BIND_FN_ALL, tuple);
|
|
|
|
|
|
+ tuple = kmalloc(sizeof(*tuple), GFP_KERNEL);
|
|
|
|
+ if (tuple == NULL) {
|
|
|
|
+ dev_warn(&s->dev, "no memory to validate CIS\n");
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+ }
|
|
|
|
+ p = kmalloc(sizeof(*p), GFP_KERNEL);
|
|
|
|
+ if (p == NULL) {
|
|
|
|
+ kfree(tuple);
|
|
|
|
+ dev_warn(&s->dev, "no memory to validate CIS\n");
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ count = reserved = 0;
|
|
|
|
+ tuple->DesiredTuple = RETURN_FIRST_TUPLE;
|
|
|
|
+ tuple->Attributes = TUPLE_RETURN_COMMON;
|
|
|
|
+ ret = pccard_get_first_tuple(s, BIND_FN_ALL, tuple);
|
|
if (ret != 0)
|
|
if (ret != 0)
|
|
- break;
|
|
|
|
- if (((tuple->TupleCode > 0x23) && (tuple->TupleCode < 0x40)) ||
|
|
|
|
- ((tuple->TupleCode > 0x47) && (tuple->TupleCode < 0x80)) ||
|
|
|
|
- ((tuple->TupleCode > 0x90) && (tuple->TupleCode < 0xff)))
|
|
|
|
- reserved++;
|
|
|
|
- }
|
|
|
|
- if ((count == MAX_TUPLES) || (reserved > 5) ||
|
|
|
|
- ((!dev_ok || !ident_ok) && (count > 10)))
|
|
|
|
- count = 0;
|
|
|
|
|
|
+ goto done;
|
|
|
|
+
|
|
|
|
+ /* 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++;
|
|
|
|
+
|
|
|
|
+ if (!dev_ok && !ident_ok)
|
|
|
|
+ goto done;
|
|
|
|
+
|
|
|
|
+ for (count = 1; count < MAX_TUPLES; count++) {
|
|
|
|
+ ret = pccard_get_next_tuple(s, BIND_FN_ALL, tuple);
|
|
|
|
+ if (ret != 0)
|
|
|
|
+ break;
|
|
|
|
+ if (((tuple->TupleCode > 0x23) && (tuple->TupleCode < 0x40)) ||
|
|
|
|
+ ((tuple->TupleCode > 0x47) && (tuple->TupleCode < 0x80)) ||
|
|
|
|
+ ((tuple->TupleCode > 0x90) && (tuple->TupleCode < 0xff)))
|
|
|
|
+ reserved++;
|
|
|
|
+ }
|
|
|
|
+ if ((count == MAX_TUPLES) || (reserved > 5) ||
|
|
|
|
+ ((!dev_ok || !ident_ok) && (count > 10)))
|
|
|
|
+ count = 0;
|
|
|
|
+
|
|
|
|
+ ret = 0;
|
|
|
|
|
|
done:
|
|
done:
|
|
- if (info)
|
|
|
|
- *info = count;
|
|
|
|
- kfree(tuple);
|
|
|
|
- kfree(p);
|
|
|
|
- return 0;
|
|
|
|
|
|
+ /* 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);
|
|
|
|
+ ret = -EIO;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (info)
|
|
|
|
+ *info = count;
|
|
|
|
+ kfree(tuple);
|
|
|
|
+ kfree(p);
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
-EXPORT_SYMBOL(pccard_validate_cis);
|
|
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+#define to_socket(_dev) container_of(_dev, struct pcmcia_socket, dev)
|
|
|
|
+
|
|
|
|
+static ssize_t pccard_extract_cis(struct pcmcia_socket *s, char *buf,
|
|
|
|
+ loff_t off, size_t count)
|
|
|
|
+{
|
|
|
|
+ tuple_t tuple;
|
|
|
|
+ int status, i;
|
|
|
|
+ loff_t pointer = 0;
|
|
|
|
+ ssize_t ret = 0;
|
|
|
|
+ u_char *tuplebuffer;
|
|
|
|
+ u_char *tempbuffer;
|
|
|
|
+
|
|
|
|
+ tuplebuffer = kmalloc(sizeof(u_char) * 256, GFP_KERNEL);
|
|
|
|
+ if (!tuplebuffer)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ tempbuffer = kmalloc(sizeof(u_char) * 258, GFP_KERNEL);
|
|
|
|
+ if (!tempbuffer) {
|
|
|
|
+ ret = -ENOMEM;
|
|
|
|
+ goto free_tuple;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ memset(&tuple, 0, sizeof(tuple_t));
|
|
|
|
+
|
|
|
|
+ tuple.Attributes = TUPLE_RETURN_LINK | TUPLE_RETURN_COMMON;
|
|
|
|
+ tuple.DesiredTuple = RETURN_FIRST_TUPLE;
|
|
|
|
+ tuple.TupleOffset = 0;
|
|
|
|
+
|
|
|
|
+ status = pccard_get_first_tuple(s, BIND_FN_ALL, &tuple);
|
|
|
|
+ while (!status) {
|
|
|
|
+ tuple.TupleData = tuplebuffer;
|
|
|
|
+ tuple.TupleDataMax = 255;
|
|
|
|
+ memset(tuplebuffer, 0, sizeof(u_char) * 255);
|
|
|
|
+
|
|
|
|
+ status = pccard_get_tuple_data(s, &tuple);
|
|
|
|
+ if (status)
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ if (off < (pointer + 2 + tuple.TupleDataLen)) {
|
|
|
|
+ tempbuffer[0] = tuple.TupleCode & 0xff;
|
|
|
|
+ tempbuffer[1] = tuple.TupleLink & 0xff;
|
|
|
|
+ for (i = 0; i < tuple.TupleDataLen; i++)
|
|
|
|
+ tempbuffer[i + 2] = tuplebuffer[i] & 0xff;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < (2 + tuple.TupleDataLen); i++) {
|
|
|
|
+ if (((i + pointer) >= off) &&
|
|
|
|
+ (i + pointer) < (off + count)) {
|
|
|
|
+ buf[ret] = tempbuffer[i];
|
|
|
|
+ ret++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pointer += 2 + tuple.TupleDataLen;
|
|
|
|
+
|
|
|
|
+ if (pointer >= (off + count))
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ if (tuple.TupleCode == CISTPL_END)
|
|
|
|
+ break;
|
|
|
|
+ status = pccard_get_next_tuple(s, BIND_FN_ALL, &tuple);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ kfree(tempbuffer);
|
|
|
|
+ free_tuple:
|
|
|
|
+ kfree(tuplebuffer);
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static ssize_t pccard_show_cis(struct kobject *kobj,
|
|
|
|
+ struct bin_attribute *bin_attr,
|
|
|
|
+ char *buf, loff_t off, size_t count)
|
|
|
|
+{
|
|
|
|
+ unsigned int size = 0x200;
|
|
|
|
+
|
|
|
|
+ if (off >= size)
|
|
|
|
+ count = 0;
|
|
|
|
+ else {
|
|
|
|
+ struct pcmcia_socket *s;
|
|
|
|
+ unsigned int chains;
|
|
|
|
+
|
|
|
|
+ if (off + count > size)
|
|
|
|
+ count = size - off;
|
|
|
|
+
|
|
|
|
+ s = to_socket(container_of(kobj, struct device, kobj));
|
|
|
|
+
|
|
|
|
+ if (!(s->state & SOCKET_PRESENT))
|
|
|
|
+ return -ENODEV;
|
|
|
|
+ if (pccard_validate_cis(s, &chains))
|
|
|
|
+ return -EIO;
|
|
|
|
+ if (!chains)
|
|
|
|
+ return -ENODATA;
|
|
|
|
+
|
|
|
|
+ count = pccard_extract_cis(s, buf, off, count);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return count;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static ssize_t pccard_store_cis(struct kobject *kobj,
|
|
|
|
+ struct bin_attribute *bin_attr,
|
|
|
|
+ char *buf, loff_t off, size_t count)
|
|
|
|
+{
|
|
|
|
+ struct pcmcia_socket *s;
|
|
|
|
+ int error;
|
|
|
|
+
|
|
|
|
+ s = to_socket(container_of(kobj, struct device, kobj));
|
|
|
|
+
|
|
|
|
+ if (off)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ if (count >= CISTPL_MAX_CIS_SIZE)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ if (!(s->state & SOCKET_PRESENT))
|
|
|
|
+ return -ENODEV;
|
|
|
|
+
|
|
|
|
+ error = pcmcia_replace_cis(s, buf, count);
|
|
|
|
+ if (error)
|
|
|
|
+ return -EIO;
|
|
|
|
+
|
|
|
|
+ pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY);
|
|
|
|
+
|
|
|
|
+ return count;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+struct bin_attribute pccard_cis_attr = {
|
|
|
|
+ .attr = { .name = "cis", .mode = S_IRUGO | S_IWUSR },
|
|
|
|
+ .size = 0x200,
|
|
|
|
+ .read = pccard_show_cis,
|
|
|
|
+ .write = pccard_store_cis,
|
|
|
|
+};
|