bulkmem.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:19k
- /*======================================================================
- PCMCIA Bulk Memory Services
- bulkmem.c 1.38 2000/09/25 19:29:51
- The contents of this file are subject to the Mozilla Public
- License Version 1.1 (the "License"); you may not use this file
- except in compliance with the License. You may obtain a copy of
- the License at http://www.mozilla.org/MPL/
- Software distributed under the License is distributed on an "AS
- IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- implied. See the License for the specific language governing
- rights and limitations under the License.
- 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.
- Alternatively, the contents of this file may be used under the
- terms of the GNU General Public License version 2 (the "GPL"), in which
- case the provisions of the GPL are applicable instead of the
- above. If you wish to allow the use of your version of this file
- only under the terms of the GPL and not to allow others to use
- your version of this file under the MPL, indicate your decision
- by deleting the provisions above and replace them with the notice
- and other provisions required by the GPL. If you do not delete
- the provisions above, a recipient may use your version of this
- file under either the MPL or the GPL.
-
- ======================================================================*/
- #define __NO_VERSION__
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/string.h>
- #include <linux/errno.h>
- #include <linux/slab.h>
- #include <linux/mm.h>
- #include <linux/sched.h>
- #include <linux/timer.h>
- #include <linux/proc_fs.h>
- #define IN_CARD_SERVICES
- #include <pcmcia/cs_types.h>
- #include <pcmcia/ss.h>
- #include <pcmcia/cs.h>
- #include <pcmcia/bulkmem.h>
- #include <pcmcia/cistpl.h>
- #include "cs_internal.h"
- /*======================================================================
- This function handles submitting an MTD request, and retrying
- requests when an MTD is busy.
- An MTD request should never block.
-
- ======================================================================*/
- static int do_mtd_request(memory_handle_t handle, mtd_request_t *req,
- caddr_t buf)
- {
- int ret, tries;
- client_t *mtd;
- socket_info_t *s;
-
- mtd = handle->mtd;
- if (mtd == NULL)
- return CS_GENERAL_FAILURE;
- s = SOCKET(mtd);
- for (ret = tries = 0; tries < 100; tries++) {
- mtd->event_callback_args.mtdrequest = req;
- mtd->event_callback_args.buffer = buf;
- ret = EVENT(mtd, CS_EVENT_MTD_REQUEST, CS_EVENT_PRI_LOW);
- if (ret != CS_BUSY)
- break;
- switch (req->Status) {
- case MTD_WAITREQ:
- /* Not that we should ever need this... */
- interruptible_sleep_on_timeout(&mtd->mtd_req, HZ);
- break;
- case MTD_WAITTIMER:
- case MTD_WAITRDY:
- interruptible_sleep_on_timeout(&mtd->mtd_req, req->Timeout*HZ/1000);
- req->Function |= MTD_REQ_TIMEOUT;
- break;
- case MTD_WAITPOWER:
- interruptible_sleep_on(&mtd->mtd_req);
- break;
- }
- if (signal_pending(current))
- printk(KERN_NOTICE "cs: do_mtd_request interrupted!n");
- }
- if (tries == 20) {
- printk(KERN_NOTICE "cs: MTD request timed out!n");
- ret = CS_GENERAL_FAILURE;
- }
- wake_up_interruptible(&mtd->mtd_req);
- retry_erase_list(&mtd->erase_busy, 0);
- return ret;
- } /* do_mtd_request */
- /*======================================================================
- This stuff is all for handling asynchronous erase requests. It
- is complicated because all the retry stuff has to be dealt with
- in timer interrupts or in the card status event handler.
- ======================================================================*/
- static void insert_queue(erase_busy_t *head, erase_busy_t *entry)
- {
- DEBUG(2, "cs: adding 0x%p to queue 0x%pn", entry, head);
- entry->next = head;
- entry->prev = head->prev;
- head->prev->next = entry;
- head->prev = entry;
- }
- static void remove_queue(erase_busy_t *entry)
- {
- DEBUG(2, "cs: unqueueing 0x%pn", entry);
- entry->next->prev = entry->prev;
- entry->prev->next = entry->next;
- }
- static void retry_erase(erase_busy_t *busy, u_int cause)
- {
- eraseq_entry_t *erase = busy->erase;
- mtd_request_t req;
- client_t *mtd;
- socket_info_t *s;
- int ret;
- DEBUG(2, "cs: trying erase request 0x%p...n", busy);
- if (busy->next)
- remove_queue(busy);
- req.Function = MTD_REQ_ERASE | cause;
- req.TransferLength = erase->Size;
- req.DestCardOffset = erase->Offset + erase->Handle->info.CardOffset;
- req.MediaID = erase->Handle->MediaID;
- mtd = erase->Handle->mtd;
- s = SOCKET(mtd);
- mtd->event_callback_args.mtdrequest = &req;
- ret = EVENT(mtd, CS_EVENT_MTD_REQUEST, CS_EVENT_PRI_LOW);
- if (ret == CS_BUSY) {
- DEBUG(2, " Status = %d, requeueing.n", req.Status);
- switch (req.Status) {
- case MTD_WAITREQ:
- case MTD_WAITPOWER:
- insert_queue(&mtd->erase_busy, busy);
- break;
- case MTD_WAITTIMER:
- case MTD_WAITRDY:
- if (req.Status == MTD_WAITRDY)
- insert_queue(&s->erase_busy, busy);
- mod_timer(&busy->timeout, jiffies + req.Timeout*HZ/1000);
- break;
- }
- } else {
- /* update erase queue status */
- DEBUG(2, " Ret = %dn", ret);
- switch (ret) {
- case CS_SUCCESS:
- erase->State = ERASE_PASSED; break;
- case CS_WRITE_PROTECTED:
- erase->State = ERASE_MEDIA_WRPROT; break;
- case CS_BAD_OFFSET:
- erase->State = ERASE_BAD_OFFSET; break;
- case CS_BAD_SIZE:
- erase->State = ERASE_BAD_SIZE; break;
- case CS_NO_CARD:
- erase->State = ERASE_BAD_SOCKET; break;
- default:
- erase->State = ERASE_FAILED; break;
- }
- busy->client->event_callback_args.info = erase;
- EVENT(busy->client, CS_EVENT_ERASE_COMPLETE, CS_EVENT_PRI_LOW);
- kfree(busy);
- /* Resubmit anything waiting for a request to finish */
- wake_up_interruptible(&mtd->mtd_req);
- retry_erase_list(&mtd->erase_busy, 0);
- }
- } /* retry_erase */
- void retry_erase_list(erase_busy_t *list, u_int cause)
- {
- erase_busy_t tmp = *list;
- DEBUG(2, "cs: rescanning erase queue list 0x%pn", list);
- if (list->next == list)
- return;
- /* First, truncate the original list */
- list->prev->next = &tmp;
- list->next->prev = &tmp;
- list->prev = list->next = list;
- tmp.prev->next = &tmp;
- tmp.next->prev = &tmp;
- /* Now, retry each request, in order. */
- while (tmp.next != &tmp)
- retry_erase(tmp.next, cause);
- } /* retry_erase_list */
- static void handle_erase_timeout(u_long arg)
- {
- DEBUG(0, "cs: erase timeout for entry 0x%lxn", arg);
- retry_erase((erase_busy_t *)arg, MTD_REQ_TIMEOUT);
- }
- static int setup_erase_request(client_handle_t handle, eraseq_entry_t *erase)
- {
- erase_busy_t *busy;
- region_info_t *info;
-
- if (CHECK_REGION(erase->Handle))
- erase->State = ERASE_BAD_SOCKET;
- else {
- info = &erase->Handle->info;
- if ((erase->Offset >= info->RegionSize) ||
- (erase->Offset & (info->BlockSize-1)))
- erase->State = ERASE_BAD_OFFSET;
- else if ((erase->Offset+erase->Size > info->RegionSize) ||
- (erase->Size & (info->BlockSize-1)))
- erase->State = ERASE_BAD_SIZE;
- else {
- erase->State = 1;
- busy = kmalloc(sizeof(erase_busy_t), GFP_KERNEL);
- if (!busy)
- return CS_GENERAL_FAILURE;
- busy->erase = erase;
- busy->client = handle;
- init_timer(&busy->timeout);
- busy->timeout.data = (u_long)busy;
- busy->timeout.function = &handle_erase_timeout;
- busy->prev = busy->next = NULL;
- retry_erase(busy, 0);
- }
- }
- return CS_SUCCESS;
- } /* setup_erase_request */
- /*======================================================================
- MTD helper functions
- ======================================================================*/
- static int mtd_modify_window(window_handle_t win, mtd_mod_win_t *req)
- {
- if ((win == NULL) || (win->magic != WINDOW_MAGIC))
- return CS_BAD_HANDLE;
- win->ctl.flags = MAP_16BIT | MAP_ACTIVE;
- if (req->Attributes & WIN_USE_WAIT)
- win->ctl.flags |= MAP_USE_WAIT;
- if (req->Attributes & WIN_MEMORY_TYPE)
- win->ctl.flags |= MAP_ATTRIB;
- win->ctl.speed = req->AccessSpeed;
- win->ctl.card_start = req->CardOffset;
- win->sock->ss_entry->set_mem_map(win->sock->sock, &win->ctl);
- return CS_SUCCESS;
- }
- static int mtd_set_vpp(client_handle_t handle, mtd_vpp_req_t *req)
- {
- socket_info_t *s;
- if (CHECK_HANDLE(handle))
- return CS_BAD_HANDLE;
- if (req->Vpp1 != req->Vpp2)
- return CS_BAD_VPP;
- s = SOCKET(handle);
- s->socket.Vpp = req->Vpp1;
- if (s->ss_entry->set_socket(s->sock, &s->socket))
- return CS_BAD_VPP;
- return CS_SUCCESS;
- }
- static int mtd_rdy_mask(client_handle_t handle, mtd_rdy_req_t *req)
- {
- socket_info_t *s;
- if (CHECK_HANDLE(handle))
- return CS_BAD_HANDLE;
- s = SOCKET(handle);
- if (req->Mask & CS_EVENT_READY_CHANGE)
- s->socket.csc_mask |= SS_READY;
- else
- s->socket.csc_mask &= ~SS_READY;
- if (s->ss_entry->set_socket(s->sock, &s->socket))
- return CS_GENERAL_FAILURE;
- return CS_SUCCESS;
- }
- int MTDHelperEntry(int func, void *a1, void *a2)
- {
- switch (func) {
- case MTDRequestWindow:
- {
- window_handle_t w;
- int ret = pcmcia_request_window(a1, a2, &w);
- (window_handle_t *)a1 = w;
- return ret;
- }
- break;
- case MTDReleaseWindow:
- return pcmcia_release_window(a1);
- case MTDModifyWindow:
- return mtd_modify_window(a1, a2); break;
- case MTDSetVpp:
- return mtd_set_vpp(a1, a2); break;
- case MTDRDYMask:
- return mtd_rdy_mask(a1, a2); break;
- default:
- return CS_UNSUPPORTED_FUNCTION; break;
- }
- } /* MTDHelperEntry */
- /*======================================================================
- This stuff is used by Card Services to initialize the table of
- region info used for subsequent calls to GetFirstRegion and
- GetNextRegion.
-
- ======================================================================*/
- static int setup_regions(client_handle_t handle, int attr,
- memory_handle_t *list)
- {
- int i, code, has_jedec, has_geo;
- u_int offset;
- cistpl_device_t device;
- cistpl_jedec_t jedec;
- cistpl_device_geo_t geo;
- memory_handle_t r;
- DEBUG(1, "cs: setup_regions(0x%p, %d, 0x%p)n",
- handle, attr, list);
- code = (attr) ? CISTPL_DEVICE_A : CISTPL_DEVICE;
- if (read_tuple(handle, code, &device) != CS_SUCCESS)
- return CS_GENERAL_FAILURE;
- code = (attr) ? CISTPL_JEDEC_A : CISTPL_JEDEC_C;
- has_jedec = (read_tuple(handle, code, &jedec) == CS_SUCCESS);
- if (has_jedec && (device.ndev != jedec.nid)) {
- #ifdef PCMCIA_DEBUG
- printk(KERN_DEBUG "cs: Device info does not match JEDEC info.n");
- #endif
- has_jedec = 0;
- }
- code = (attr) ? CISTPL_DEVICE_GEO_A : CISTPL_DEVICE_GEO;
- has_geo = (read_tuple(handle, code, &geo) == CS_SUCCESS);
- if (has_geo && (device.ndev != geo.ngeo)) {
- #ifdef PCMCIA_DEBUG
- printk(KERN_DEBUG "cs: Device info does not match geometry tuple.n");
- #endif
- has_geo = 0;
- }
-
- offset = 0;
- for (i = 0; i < device.ndev; i++) {
- if ((device.dev[i].type != CISTPL_DTYPE_NULL) &&
- (device.dev[i].size != 0)) {
- r = kmalloc(sizeof(*r), GFP_KERNEL);
- if (!r)
- return CS_GENERAL_FAILURE;
- r->region_magic = REGION_MAGIC;
- r->state = 0;
- r->dev_info[0] = ' ';
- r->mtd = NULL;
- r->info.Attributes = (attr) ? REGION_TYPE_AM : 0;
- r->info.CardOffset = offset;
- r->info.RegionSize = device.dev[i].size;
- r->info.AccessSpeed = device.dev[i].speed;
- if (has_jedec) {
- r->info.JedecMfr = jedec.id[i].mfr;
- r->info.JedecInfo = jedec.id[i].info;
- } else
- r->info.JedecMfr = r->info.JedecInfo = 0;
- if (has_geo) {
- r->info.BlockSize = geo.geo[i].buswidth *
- geo.geo[i].erase_block * geo.geo[i].interleave;
- r->info.PartMultiple =
- r->info.BlockSize * geo.geo[i].partition;
- } else
- r->info.BlockSize = r->info.PartMultiple = 1;
- r->info.next = *list; *list = r;
- }
- offset += device.dev[i].size;
- }
- return CS_SUCCESS;
- } /* setup_regions */
- /*======================================================================
- This is tricky. When get_first_region() is called by Driver
- Services, we initialize the region info table in the socket
- structure. When it is called by an MTD, we can just scan the
- table for matching entries.
-
- ======================================================================*/
- static int match_region(client_handle_t handle, memory_handle_t list,
- region_info_t *match)
- {
- while (list != NULL) {
- if (!(handle->Attributes & INFO_MTD_CLIENT) ||
- (strcmp(handle->dev_info, list->dev_info) == 0)) {
- *match = list->info;
- return CS_SUCCESS;
- }
- list = list->info.next;
- }
- return CS_NO_MORE_ITEMS;
- } /* match_region */
- int pcmcia_get_first_region(client_handle_t handle, region_info_t *rgn)
- {
- socket_info_t *s = SOCKET(handle);
- if (CHECK_HANDLE(handle))
- return CS_BAD_HANDLE;
-
- if ((handle->Attributes & INFO_MASTER_CLIENT) &&
- (!(s->state & SOCKET_REGION_INFO))) {
- if (setup_regions(handle, 0, &s->c_region) != CS_SUCCESS)
- return CS_GENERAL_FAILURE;
- if (setup_regions(handle, 1, &s->a_region) != CS_SUCCESS)
- return CS_GENERAL_FAILURE;
- s->state |= SOCKET_REGION_INFO;
- }
- if (rgn->Attributes & REGION_TYPE_AM)
- return match_region(handle, s->a_region, rgn);
- else
- return match_region(handle, s->c_region, rgn);
- } /* get_first_region */
- int pcmcia_get_next_region(client_handle_t handle, region_info_t *rgn)
- {
- if (CHECK_HANDLE(handle))
- return CS_BAD_HANDLE;
- return match_region(handle, rgn->next, rgn);
- } /* get_next_region */
- /*======================================================================
- Connect an MTD with a memory region.
-
- ======================================================================*/
- int pcmcia_register_mtd(client_handle_t handle, mtd_reg_t *reg)
- {
- memory_handle_t list;
- socket_info_t *s;
-
- if (CHECK_HANDLE(handle))
- return CS_BAD_HANDLE;
- s = SOCKET(handle);
- if (reg->Attributes & REGION_TYPE_AM)
- list = s->a_region;
- else
- list = s->c_region;
- DEBUG(1, "cs: register_mtd(0x%p, '%s', 0x%x)n",
- handle, handle->dev_info, reg->Offset);
- while (list) {
- if (list->info.CardOffset == reg->Offset) break;
- list = list->info.next;
- }
- if (list && (list->mtd == NULL) &&
- (strcmp(handle->dev_info, list->dev_info) == 0)) {
- list->info.Attributes = reg->Attributes;
- list->MediaID = reg->MediaID;
- list->mtd = handle;
- handle->mtd_count++;
- return CS_SUCCESS;
- } else
- return CS_BAD_OFFSET;
- } /* register_mtd */
- /*======================================================================
- Erase queue management functions
-
- ======================================================================*/
- int pcmcia_register_erase_queue(client_handle_t *handle, eraseq_hdr_t *header,
- eraseq_handle_t *e)
- {
- eraseq_t *queue;
- if ((handle == NULL) || CHECK_HANDLE(*handle))
- return CS_BAD_HANDLE;
- queue = kmalloc(sizeof(*queue), GFP_KERNEL);
- if (!queue) return CS_OUT_OF_RESOURCE;
- queue->eraseq_magic = ERASEQ_MAGIC;
- queue->handle = *handle;
- queue->count = header->QueueEntryCnt;
- queue->entry = header->QueueEntryArray;
- *e = queue;
- return CS_SUCCESS;
- } /* register_erase_queue */
- int pcmcia_deregister_erase_queue(eraseq_handle_t eraseq)
- {
- int i;
- if (CHECK_ERASEQ(eraseq))
- return CS_BAD_HANDLE;
- for (i = 0; i < eraseq->count; i++)
- if (ERASE_IN_PROGRESS(eraseq->entry[i].State)) break;
- if (i < eraseq->count)
- return CS_BUSY;
- eraseq->eraseq_magic = 0;
- kfree(eraseq);
- return CS_SUCCESS;
- } /* deregister_erase_queue */
- int pcmcia_check_erase_queue(eraseq_handle_t eraseq)
- {
- int i;
- if (CHECK_ERASEQ(eraseq))
- return CS_BAD_HANDLE;
- for (i = 0; i < eraseq->count; i++)
- if (eraseq->entry[i].State == ERASE_QUEUED)
- setup_erase_request(eraseq->handle, &eraseq->entry[i]);
- return CS_SUCCESS;
- } /* check_erase_queue */
- /*======================================================================
- Look up the memory region matching the request, and return a
- memory handle.
-
- ======================================================================*/
- int pcmcia_open_memory(client_handle_t *handle, open_mem_t *open, memory_handle_t *mh)
- {
- socket_info_t *s;
- memory_handle_t region;
-
- if ((handle == NULL) || CHECK_HANDLE(*handle))
- return CS_BAD_HANDLE;
- s = SOCKET(*handle);
- if (open->Attributes & MEMORY_TYPE_AM)
- region = s->a_region;
- else
- region = s->c_region;
- while (region) {
- if (region->info.CardOffset == open->Offset) break;
- region = region->info.next;
- }
- if (region && region->mtd) {
- *mh = region;
- DEBUG(1, "cs: open_memory(0x%p, 0x%x) = 0x%pn",
- handle, open->Offset, region);
- return CS_SUCCESS;
- } else
- return CS_BAD_OFFSET;
- } /* open_memory */
- /*======================================================================
- Close a memory handle from an earlier call to OpenMemory.
-
- For the moment, I don't think this needs to do anything.
-
- ======================================================================*/
- int pcmcia_close_memory(memory_handle_t handle)
- {
- DEBUG(1, "cs: close_memory(0x%p)n", handle);
- if (CHECK_REGION(handle))
- return CS_BAD_HANDLE;
- return CS_SUCCESS;
- } /* close_memory */
- /*======================================================================
- Read from a memory device, using a handle previously returned
- by a call to OpenMemory.
-
- ======================================================================*/
- int pcmcia_read_memory(memory_handle_t handle, mem_op_t *req, caddr_t buf)
- {
- mtd_request_t mtd;
- if (CHECK_REGION(handle))
- return CS_BAD_HANDLE;
- if (req->Offset >= handle->info.RegionSize)
- return CS_BAD_OFFSET;
- if (req->Offset+req->Count > handle->info.RegionSize)
- return CS_BAD_SIZE;
-
- mtd.SrcCardOffset = req->Offset + handle->info.CardOffset;
- mtd.TransferLength = req->Count;
- mtd.MediaID = handle->MediaID;
- mtd.Function = MTD_REQ_READ;
- if (req->Attributes & MEM_OP_BUFFER_KERNEL)
- mtd.Function |= MTD_REQ_KERNEL;
- return do_mtd_request(handle, &mtd, buf);
- } /* read_memory */
- /*======================================================================
- Write to a memory device, using a handle previously returned by
- a call to OpenMemory.
-
- ======================================================================*/
- int pcmcia_write_memory(memory_handle_t handle, mem_op_t *req, caddr_t buf)
- {
- mtd_request_t mtd;
- if (CHECK_REGION(handle))
- return CS_BAD_HANDLE;
- if (req->Offset >= handle->info.RegionSize)
- return CS_BAD_OFFSET;
- if (req->Offset+req->Count > handle->info.RegionSize)
- return CS_BAD_SIZE;
-
- mtd.DestCardOffset = req->Offset + handle->info.CardOffset;
- mtd.TransferLength = req->Count;
- mtd.MediaID = handle->MediaID;
- mtd.Function = MTD_REQ_WRITE;
- if (req->Attributes & MEM_OP_BUFFER_KERNEL)
- mtd.Function |= MTD_REQ_KERNEL;
- return do_mtd_request(handle, &mtd, buf);
- } /* write_memory */
- /*======================================================================
- This isn't needed for anything I could think of.
-
- ======================================================================*/
- int pcmcia_copy_memory(memory_handle_t handle, copy_op_t *req)
- {
- if (CHECK_REGION(handle))
- return CS_BAD_HANDLE;
- return CS_UNSUPPORTED_FUNCTION;
- }