From 4de24fcf8ccd1a5d71951240116a0e7775967951 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 23 Apr 2003 23:18:16 +0100 Subject: [PATCH] [PCMCIA] Don't cache CIS bytes found to be invalid. Several PCMCIA cards I have here do not work correctly over a suspend/resume cycle; the PCMCIA code believes that the card has been changed in the slot, and therefore performs a remove/insert cycle. This seems to be because the card returns more or less random data when reading memory space, leading to the CIS cache mismatching the card data. This in turn is caused because we try to read CIS data from both the attribute and memory spaces, and we add the result to the CIS cache whether or not the returned data was valid. We therefore convert the CIS cache to use a linked list, and provide a way to remove cached data from that list. We also replace the "s->cis_used=0;" construct with a function "destroy_cis_cache(s)" which clearly describes what we're doing. --- drivers/pcmcia/cistpl.c | 108 ++++++++++++++++++++++------------- drivers/pcmcia/cs.c | 3 +- drivers/pcmcia/cs_internal.h | 19 +++--- drivers/pcmcia/rsrc_mgr.c | 4 +- 4 files changed, 80 insertions(+), 54 deletions(-) diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index c34acc53ebe34..303ee17d6825e 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -269,8 +269,8 @@ void write_cis_mem(socket_info_t *s, int attr, u_int addr, static void read_cis_cache(socket_info_t *s, int attr, u_int addr, u_int len, void *ptr) { - int i, ret; - char *caddr; + struct cis_cache_entry *cis; + int ret; if (s->fake_cis) { if (s->fake_cis_len > addr+len) @@ -279,32 +279,55 @@ static void read_cis_cache(socket_info_t *s, int attr, u_int addr, memset(ptr, 0xff, len); return; } - caddr = s->cis_cache; - for (i = 0; i < s->cis_used; i++) { - if ((s->cis_table[i].addr == addr) && - (s->cis_table[i].len == len) && - (s->cis_table[i].attr == attr)) break; - caddr += s->cis_table[i].len; - } - if (i < s->cis_used) { - memcpy(ptr, caddr, len); - return; + + 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; + } } + #ifdef CONFIG_CARDBUS if (s->state & SOCKET_CARDBUS) ret = read_cb_mem(s, attr, addr, len, ptr); else #endif ret = read_cis_mem(s, attr, addr, len, ptr); - /* Copy data into the cache, if there is room */ - if ((ret == 0) && (i < MAX_CIS_TABLE) && - (caddr+len < s->cis_cache+MAX_CIS_DATA)) { - s->cis_table[i].addr = addr; - s->cis_table[i].len = len; - s->cis_table[i].attr = attr; - s->cis_used++; - memcpy(caddr, ptr, len); - } + + /* Copy data into the cache */ + cis = kmalloc(sizeof(struct cis_cache_entry) + len, GFP_KERNEL); + if (cis) { + cis->addr = addr; + cis->len = len; + cis->attr = attr; + memcpy(cis->cache, ptr, len); + list_add(&cis->node, &s->cis_cache); + } +} + +static void +remove_cis_cache(socket_info_t *s, int attr, u_int addr, u_int len) +{ + struct cis_cache_entry *cis; + + list_for_each_entry(cis, &s->cis_cache, node) + if (cis->addr == addr && cis->len == len && cis->attr == attr) { + list_del(&cis->node); + kfree(cis); + break; + } +} + +void destroy_cis_cache(socket_info_t *s) +{ + struct list_head *l, *n; + + list_for_each_safe(l, n, &s->cis_cache) { + struct cis_cache_entry *cis = list_entry(l, struct cis_cache_entry, node); + + list_del(&cis->node); + kfree(cis); + } } /*====================================================================== @@ -316,24 +339,25 @@ static void read_cis_cache(socket_info_t *s, int attr, u_int addr, int verify_cis_cache(socket_info_t *s) { - char buf[256], *caddr; - int i; - - caddr = s->cis_cache; - for (i = 0; i < s->cis_used; i++) { + struct cis_cache_entry *cis; + char buf[256]; + + list_for_each_entry(cis, &s->cis_cache, node) { + int len = cis->len; + + if (len > 256) + len = 256; #ifdef CONFIG_CARDBUS - if (s->state & SOCKET_CARDBUS) - read_cb_mem(s, s->cis_table[i].attr, s->cis_table[i].addr, - s->cis_table[i].len, buf); - else + if (s->state & SOCKET_CARDBUS) + read_cb_mem(s, cis->attr, cis->addr, len, buf); + else #endif - read_cis_mem(s, s->cis_table[i].attr, s->cis_table[i].addr, - s->cis_table[i].len, buf); - if (memcmp(buf, caddr, s->cis_table[i].len) != 0) - break; - caddr += s->cis_table[i].len; - } - return (i < s->cis_used); + read_cis_mem(s, cis->attr, cis->addr, len, buf); + + if (memcmp(buf, cis->cache, len) != 0) + return -1; + } + return 0; } /*====================================================================== @@ -449,14 +473,16 @@ static int follow_link(socket_info_t *s, tuple_t *tuple) 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; } read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link); - if ((link[0] != CISTPL_LINKTARGET) || (link[1] < 3) || - (strncmp(link+2, "CIS", 3) != 0)) - return -1; - return ofs; + 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 pcmcia_get_next_tuple(client_handle_t handle, tuple_t *tuple) diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 0359563637f43..aa4fd90589103 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -342,6 +342,7 @@ int pcmcia_register_socket(struct device *dev) s->cis_mem.flags = 0; s->cis_mem.speed = cis_speed; s->erase_busy.next = s->erase_busy.prev = &s->erase_busy; + INIT_LIST_HEAD(&s->cis_cache); spin_lock_init(&s->lock); /* TBD: remove usage of socket_table, use class_for_each_dev instead */ @@ -469,7 +470,7 @@ static void shutdown_socket(socket_info_t *s) init_socket(s); s->irq.AssignedIRQ = s->irq.Config = 0; s->lock_count = 0; - s->cis_used = 0; + destroy_cis_cache(s); if (s->fake_cis) { kfree(s->fake_cis); s->fake_cis = NULL; diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index f95482e621c86..d4dfca9a925a3 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -114,9 +114,13 @@ typedef struct config_t { /* Maximum number of memory windows per socket */ #define MAX_WIN 4 -/* The size of the CIS cache */ -#define MAX_CIS_TABLE 64 -#define MAX_CIS_DATA 512 +struct cis_cache_entry { + struct list_head node; + unsigned int addr; + unsigned int len; + unsigned int attr; + unsigned char cache[0]; +}; typedef struct socket_info_t { spinlock_t lock; @@ -145,13 +149,7 @@ typedef struct socket_info_t { window_t win[MAX_WIN]; region_t *c_region, *a_region; erase_busy_t erase_busy; - int cis_used; - struct { - u_int addr; - u_short len; - u_short attr; - } cis_table[MAX_CIS_TABLE]; - char cis_cache[MAX_CIS_DATA]; + struct list_head cis_cache; u_int fake_cis_len; char *fake_cis; #ifdef CONFIG_PROC_FS @@ -206,6 +204,7 @@ int read_cis_mem(socket_info_t *s, int attr, void write_cis_mem(socket_info_t *s, int attr, u_int addr, u_int len, void *ptr); void release_cis_mem(socket_info_t *s); +void destroy_cis_cache(socket_info_t *s); int verify_cis_cache(socket_info_t *s); void preload_cis_cache(socket_info_t *s); int get_first_tuple(client_handle_t handle, tuple_t *tuple); diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c index 0d1f58225149f..e4a4f4b41ed78 100644 --- a/drivers/pcmcia/rsrc_mgr.c +++ b/drivers/pcmcia/rsrc_mgr.c @@ -351,7 +351,7 @@ static int cis_readable(socket_info_t *s, u_long base) ret = pcmcia_validate_cis(s->clients, &info1); /* invalidate mapping and CIS cache */ iounmap(s->cis_virt); - s->cis_used = 0; + destroy_cis_cache(s); if ((ret != 0) || (info1.Chains == 0)) return 0; s->cis_mem.sys_start = base+s->cap.map_size; @@ -359,7 +359,7 @@ static int cis_readable(socket_info_t *s, u_long base) s->cis_virt = ioremap(base+s->cap.map_size, s->cap.map_size); ret = pcmcia_validate_cis(s->clients, &info2); iounmap(s->cis_virt); - s->cis_used = 0; + destroy_cis_cache(s); return ((ret == 0) && (info1.Chains == info2.Chains)); } -- 2.53.0