1 /*======================================================================
3 PCMCIA Bulk Memory Services
5 bulkmem.c 1.38 2000/09/25 19:29:51
7 The contents of this file are subject to the Mozilla Public
8 License Version 1.1 (the "License"); you may not use this file
9 except in compliance with the License. You may obtain a copy of
10 the License at http://www.mozilla.org/MPL/
12 Software distributed under the License is distributed on an "AS
13 IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14 implied. See the License for the specific language governing
15 rights and limitations under the License.
17 The initial developer of the original code is David A. Hinds
18 <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
19 are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
21 Alternatively, the contents of this file may be used under the
22 terms of the GNU General Public License version 2 (the "GPL"), in which
23 case the provisions of the GPL are applicable instead of the
24 above. If you wish to allow the use of your version of this file
25 only under the terms of the GPL and not to allow others to use
26 your version of this file under the MPL, indicate your decision
27 by deleting the provisions above and replace them with the notice
28 and other provisions required by the GPL. If you do not delete
29 the provisions above, a recipient may use your version of this
30 file under either the MPL or the GPL.
32 ======================================================================*/
34 #define __NO_VERSION__
36 #include <linux/module.h>
37 #include <linux/kernel.h>
38 #include <linux/string.h>
39 #include <linux/errno.h>
40 #include <linux/slab.h>
42 #include <linux/sched.h>
43 #include <linux/timer.h>
44 #include <linux/proc_fs.h>
46 #define IN_CARD_SERVICES
47 #include <pcmcia/cs_types.h>
48 #include <pcmcia/ss.h>
49 #include <pcmcia/cs.h>
50 #include <pcmcia/bulkmem.h>
51 #include <pcmcia/cistpl.h>
52 #include "cs_internal.h"
54 /*======================================================================
56 This function handles submitting an MTD request, and retrying
57 requests when an MTD is busy.
59 An MTD request should never block.
61 ======================================================================*/
63 static int do_mtd_request(memory_handle_t handle, mtd_request_t *req,
72 return CS_GENERAL_FAILURE;
74 for (ret = tries = 0; tries < 100; tries++) {
75 mtd->event_callback_args.mtdrequest = req;
76 mtd->event_callback_args.buffer = buf;
77 ret = EVENT(mtd, CS_EVENT_MTD_REQUEST, CS_EVENT_PRI_LOW);
80 switch (req->Status) {
82 /* Not that we should ever need this... */
83 interruptible_sleep_on_timeout(&mtd->mtd_req, HZ);
87 interruptible_sleep_on_timeout(&mtd->mtd_req, req->Timeout*HZ/1000);
88 req->Function |= MTD_REQ_TIMEOUT;
91 interruptible_sleep_on(&mtd->mtd_req);
94 if (signal_pending(current))
95 printk(KERN_NOTICE "cs: do_mtd_request interrupted!\n");
98 printk(KERN_NOTICE "cs: MTD request timed out!\n");
99 ret = CS_GENERAL_FAILURE;
101 wake_up_interruptible(&mtd->mtd_req);
102 retry_erase_list(&mtd->erase_busy, 0);
104 } /* do_mtd_request */
106 /*======================================================================
108 This stuff is all for handling asynchronous erase requests. It
109 is complicated because all the retry stuff has to be dealt with
110 in timer interrupts or in the card status event handler.
112 ======================================================================*/
114 static void insert_queue(erase_busy_t *head, erase_busy_t *entry)
116 DEBUG(2, "cs: adding 0x%p to queue 0x%p\n", entry, head);
118 entry->prev = head->prev;
119 head->prev->next = entry;
123 static void remove_queue(erase_busy_t *entry)
125 DEBUG(2, "cs: unqueueing 0x%p\n", entry);
126 entry->next->prev = entry->prev;
127 entry->prev->next = entry->next;
130 static void retry_erase(erase_busy_t *busy, u_int cause)
132 eraseq_entry_t *erase = busy->erase;
138 DEBUG(2, "cs: trying erase request 0x%p...\n", busy);
141 req.Function = MTD_REQ_ERASE | cause;
142 req.TransferLength = erase->Size;
143 req.DestCardOffset = erase->Offset + erase->Handle->info.CardOffset;
144 req.MediaID = erase->Handle->MediaID;
145 mtd = erase->Handle->mtd;
147 mtd->event_callback_args.mtdrequest = &req;
148 ret = EVENT(mtd, CS_EVENT_MTD_REQUEST, CS_EVENT_PRI_LOW);
149 if (ret == CS_BUSY) {
150 DEBUG(2, " Status = %d, requeueing.\n", req.Status);
151 switch (req.Status) {
154 insert_queue(&mtd->erase_busy, busy);
158 if (req.Status == MTD_WAITRDY)
159 insert_queue(&s->erase_busy, busy);
160 mod_timer(&busy->timeout, jiffies + req.Timeout*HZ/1000);
164 /* update erase queue status */
165 DEBUG(2, " Ret = %d\n", ret);
168 erase->State = ERASE_PASSED; break;
169 case CS_WRITE_PROTECTED:
170 erase->State = ERASE_MEDIA_WRPROT; break;
172 erase->State = ERASE_BAD_OFFSET; break;
174 erase->State = ERASE_BAD_SIZE; break;
176 erase->State = ERASE_BAD_SOCKET; break;
178 erase->State = ERASE_FAILED; break;
180 busy->client->event_callback_args.info = erase;
181 EVENT(busy->client, CS_EVENT_ERASE_COMPLETE, CS_EVENT_PRI_LOW);
183 /* Resubmit anything waiting for a request to finish */
184 wake_up_interruptible(&mtd->mtd_req);
185 retry_erase_list(&mtd->erase_busy, 0);
189 void retry_erase_list(erase_busy_t *list, u_int cause)
191 erase_busy_t tmp = *list;
193 DEBUG(2, "cs: rescanning erase queue list 0x%p\n", list);
194 if (list->next == list)
196 /* First, truncate the original list */
197 list->prev->next = &tmp;
198 list->next->prev = &tmp;
199 list->prev = list->next = list;
200 tmp.prev->next = &tmp;
201 tmp.next->prev = &tmp;
203 /* Now, retry each request, in order. */
204 while (tmp.next != &tmp)
205 retry_erase(tmp.next, cause);
206 } /* retry_erase_list */
208 static void handle_erase_timeout(u_long arg)
210 DEBUG(0, "cs: erase timeout for entry 0x%lx\n", arg);
211 retry_erase((erase_busy_t *)arg, MTD_REQ_TIMEOUT);
214 static void setup_erase_request(client_handle_t handle, eraseq_entry_t *erase)
219 if (CHECK_REGION(erase->Handle))
220 erase->State = ERASE_BAD_SOCKET;
222 info = &erase->Handle->info;
223 if ((erase->Offset >= info->RegionSize) ||
224 (erase->Offset & (info->BlockSize-1)))
225 erase->State = ERASE_BAD_OFFSET;
226 else if ((erase->Offset+erase->Size > info->RegionSize) ||
227 (erase->Size & (info->BlockSize-1)))
228 erase->State = ERASE_BAD_SIZE;
231 busy = kmalloc(sizeof(erase_busy_t), GFP_KERNEL);
233 erase->State = ERASE_FAILED;
237 busy->client = handle;
238 init_timer(&busy->timeout);
239 busy->timeout.data = (u_long)busy;
240 busy->timeout.function = &handle_erase_timeout;
241 busy->prev = busy->next = NULL;
242 retry_erase(busy, 0);
245 } /* setup_erase_request */
247 /*======================================================================
251 ======================================================================*/
253 static int mtd_modify_window(window_handle_t win, mtd_mod_win_t *req)
255 if ((win == NULL) || (win->magic != WINDOW_MAGIC))
256 return CS_BAD_HANDLE;
257 win->ctl.flags = MAP_16BIT | MAP_ACTIVE;
258 if (req->Attributes & WIN_USE_WAIT)
259 win->ctl.flags |= MAP_USE_WAIT;
260 if (req->Attributes & WIN_MEMORY_TYPE)
261 win->ctl.flags |= MAP_ATTRIB;
262 win->ctl.speed = req->AccessSpeed;
263 win->ctl.card_start = req->CardOffset;
264 win->sock->ss_entry->set_mem_map(win->sock->sock, &win->ctl);
268 static int mtd_set_vpp(client_handle_t handle, mtd_vpp_req_t *req)
271 if (CHECK_HANDLE(handle))
272 return CS_BAD_HANDLE;
273 if (req->Vpp1 != req->Vpp2)
276 s->socket.Vpp = req->Vpp1;
277 if (s->ss_entry->set_socket(s->sock, &s->socket))
282 static int mtd_rdy_mask(client_handle_t handle, mtd_rdy_req_t *req)
285 if (CHECK_HANDLE(handle))
286 return CS_BAD_HANDLE;
288 if (req->Mask & CS_EVENT_READY_CHANGE)
289 s->socket.csc_mask |= SS_READY;
291 s->socket.csc_mask &= ~SS_READY;
292 if (s->ss_entry->set_socket(s->sock, &s->socket))
293 return CS_GENERAL_FAILURE;
297 int MTDHelperEntry(int func, void *a1, void *a2)
300 case MTDRequestWindow:
303 int ret = pcmcia_request_window(a1, a2, &w);
304 (window_handle_t *)a1 = w;
308 case MTDReleaseWindow:
309 return pcmcia_release_window(a1);
310 case MTDModifyWindow:
311 return mtd_modify_window(a1, a2); break;
313 return mtd_set_vpp(a1, a2); break;
315 return mtd_rdy_mask(a1, a2); break;
317 return CS_UNSUPPORTED_FUNCTION; break;
319 } /* MTDHelperEntry */
321 /*======================================================================
323 This stuff is used by Card Services to initialize the table of
324 region info used for subsequent calls to GetFirstRegion and
327 ======================================================================*/
329 static void setup_regions(client_handle_t handle, int attr,
330 memory_handle_t *list)
332 int i, code, has_jedec, has_geo;
334 cistpl_device_t device;
335 cistpl_jedec_t jedec;
336 cistpl_device_geo_t geo;
339 DEBUG(1, "cs: setup_regions(0x%p, %d, 0x%p)\n",
342 code = (attr) ? CISTPL_DEVICE_A : CISTPL_DEVICE;
343 if (read_tuple(handle, code, &device) != CS_SUCCESS)
345 code = (attr) ? CISTPL_JEDEC_A : CISTPL_JEDEC_C;
346 has_jedec = (read_tuple(handle, code, &jedec) == CS_SUCCESS);
347 if (has_jedec && (device.ndev != jedec.nid)) {
349 printk(KERN_DEBUG "cs: Device info does not match JEDEC info.\n");
353 code = (attr) ? CISTPL_DEVICE_GEO_A : CISTPL_DEVICE_GEO;
354 has_geo = (read_tuple(handle, code, &geo) == CS_SUCCESS);
355 if (has_geo && (device.ndev != geo.ngeo)) {
357 printk(KERN_DEBUG "cs: Device info does not match geometry tuple.\n");
363 for (i = 0; i < device.ndev; i++) {
364 if ((device.dev[i].type != CISTPL_DTYPE_NULL) &&
365 (device.dev[i].size != 0)) {
366 r = kmalloc(sizeof(*r), GFP_KERNEL);
368 printk(KERN_NOTICE "cs: setup_regions: kmalloc failed!\n");
371 r->region_magic = REGION_MAGIC;
373 r->dev_info[0] = '\0';
375 r->info.Attributes = (attr) ? REGION_TYPE_AM : 0;
376 r->info.CardOffset = offset;
377 r->info.RegionSize = device.dev[i].size;
378 r->info.AccessSpeed = device.dev[i].speed;
380 r->info.JedecMfr = jedec.id[i].mfr;
381 r->info.JedecInfo = jedec.id[i].info;
383 r->info.JedecMfr = r->info.JedecInfo = 0;
385 r->info.BlockSize = geo.geo[i].buswidth *
386 geo.geo[i].erase_block * geo.geo[i].interleave;
387 r->info.PartMultiple =
388 r->info.BlockSize * geo.geo[i].partition;
390 r->info.BlockSize = r->info.PartMultiple = 1;
391 r->info.next = *list; *list = r;
393 offset += device.dev[i].size;
395 } /* setup_regions */
397 /*======================================================================
399 This is tricky. When get_first_region() is called by Driver
400 Services, we initialize the region info table in the socket
401 structure. When it is called by an MTD, we can just scan the
402 table for matching entries.
404 ======================================================================*/
406 static int match_region(client_handle_t handle, memory_handle_t list,
407 region_info_t *match)
409 while (list != NULL) {
410 if (!(handle->Attributes & INFO_MTD_CLIENT) ||
411 (strcmp(handle->dev_info, list->dev_info) == 0)) {
415 list = list->info.next;
417 return CS_NO_MORE_ITEMS;
420 int pcmcia_get_first_region(client_handle_t handle, region_info_t *rgn)
422 socket_info_t *s = SOCKET(handle);
423 if (CHECK_HANDLE(handle))
424 return CS_BAD_HANDLE;
426 if ((handle->Attributes & INFO_MASTER_CLIENT) &&
427 (!(s->state & SOCKET_REGION_INFO))) {
428 setup_regions(handle, 0, &s->c_region);
429 setup_regions(handle, 1, &s->a_region);
430 s->state |= SOCKET_REGION_INFO;
433 if (rgn->Attributes & REGION_TYPE_AM)
434 return match_region(handle, s->a_region, rgn);
436 return match_region(handle, s->c_region, rgn);
437 } /* get_first_region */
439 int pcmcia_get_next_region(client_handle_t handle, region_info_t *rgn)
441 if (CHECK_HANDLE(handle))
442 return CS_BAD_HANDLE;
443 return match_region(handle, rgn->next, rgn);
444 } /* get_next_region */
446 /*======================================================================
448 Connect an MTD with a memory region.
450 ======================================================================*/
452 int pcmcia_register_mtd(client_handle_t handle, mtd_reg_t *reg)
454 memory_handle_t list;
457 if (CHECK_HANDLE(handle))
458 return CS_BAD_HANDLE;
460 if (reg->Attributes & REGION_TYPE_AM)
464 DEBUG(1, "cs: register_mtd(0x%p, '%s', 0x%x)\n",
465 handle, handle->dev_info, reg->Offset);
467 if (list->info.CardOffset == reg->Offset) break;
468 list = list->info.next;
470 if (list && (list->mtd == NULL) &&
471 (strcmp(handle->dev_info, list->dev_info) == 0)) {
472 list->info.Attributes = reg->Attributes;
473 list->MediaID = reg->MediaID;
478 return CS_BAD_OFFSET;
481 /*======================================================================
483 Erase queue management functions
485 ======================================================================*/
487 int pcmcia_register_erase_queue(client_handle_t *handle, eraseq_hdr_t *header,
492 if ((handle == NULL) || CHECK_HANDLE(*handle))
493 return CS_BAD_HANDLE;
494 queue = kmalloc(sizeof(*queue), GFP_KERNEL);
495 if (!queue) return CS_OUT_OF_RESOURCE;
496 queue->eraseq_magic = ERASEQ_MAGIC;
497 queue->handle = *handle;
498 queue->count = header->QueueEntryCnt;
499 queue->entry = header->QueueEntryArray;
502 } /* register_erase_queue */
504 int pcmcia_deregister_erase_queue(eraseq_handle_t eraseq)
507 if (CHECK_ERASEQ(eraseq))
508 return CS_BAD_HANDLE;
509 for (i = 0; i < eraseq->count; i++)
510 if (ERASE_IN_PROGRESS(eraseq->entry[i].State)) break;
511 if (i < eraseq->count)
513 eraseq->eraseq_magic = 0;
516 } /* deregister_erase_queue */
518 int pcmcia_check_erase_queue(eraseq_handle_t eraseq)
521 if (CHECK_ERASEQ(eraseq))
522 return CS_BAD_HANDLE;
523 for (i = 0; i < eraseq->count; i++)
524 if (eraseq->entry[i].State == ERASE_QUEUED)
525 setup_erase_request(eraseq->handle, &eraseq->entry[i]);
527 } /* check_erase_queue */
529 /*======================================================================
531 Look up the memory region matching the request, and return a
534 ======================================================================*/
536 int pcmcia_open_memory(client_handle_t *handle, open_mem_t *open, memory_handle_t *mh)
539 memory_handle_t region;
541 if ((handle == NULL) || CHECK_HANDLE(*handle))
542 return CS_BAD_HANDLE;
544 if (open->Attributes & MEMORY_TYPE_AM)
545 region = s->a_region;
547 region = s->c_region;
549 if (region->info.CardOffset == open->Offset) break;
550 region = region->info.next;
552 if (region && region->mtd) {
554 DEBUG(1, "cs: open_memory(0x%p, 0x%x) = 0x%p\n",
555 handle, open->Offset, region);
558 return CS_BAD_OFFSET;
561 /*======================================================================
563 Close a memory handle from an earlier call to OpenMemory.
565 For the moment, I don't think this needs to do anything.
567 ======================================================================*/
569 int pcmcia_close_memory(memory_handle_t handle)
571 DEBUG(1, "cs: close_memory(0x%p)\n", handle);
572 if (CHECK_REGION(handle))
573 return CS_BAD_HANDLE;
577 /*======================================================================
579 Read from a memory device, using a handle previously returned
580 by a call to OpenMemory.
582 ======================================================================*/
584 int pcmcia_read_memory(memory_handle_t handle, mem_op_t *req, caddr_t buf)
587 if (CHECK_REGION(handle))
588 return CS_BAD_HANDLE;
589 if (req->Offset >= handle->info.RegionSize)
590 return CS_BAD_OFFSET;
591 if (req->Offset+req->Count > handle->info.RegionSize)
594 mtd.SrcCardOffset = req->Offset + handle->info.CardOffset;
595 mtd.TransferLength = req->Count;
596 mtd.MediaID = handle->MediaID;
597 mtd.Function = MTD_REQ_READ;
598 if (req->Attributes & MEM_OP_BUFFER_KERNEL)
599 mtd.Function |= MTD_REQ_KERNEL;
600 return do_mtd_request(handle, &mtd, buf);
603 /*======================================================================
605 Write to a memory device, using a handle previously returned by
606 a call to OpenMemory.
608 ======================================================================*/
610 int pcmcia_write_memory(memory_handle_t handle, mem_op_t *req, caddr_t buf)
613 if (CHECK_REGION(handle))
614 return CS_BAD_HANDLE;
615 if (req->Offset >= handle->info.RegionSize)
616 return CS_BAD_OFFSET;
617 if (req->Offset+req->Count > handle->info.RegionSize)
620 mtd.DestCardOffset = req->Offset + handle->info.CardOffset;
621 mtd.TransferLength = req->Count;
622 mtd.MediaID = handle->MediaID;
623 mtd.Function = MTD_REQ_WRITE;
624 if (req->Attributes & MEM_OP_BUFFER_KERNEL)
625 mtd.Function |= MTD_REQ_KERNEL;
626 return do_mtd_request(handle, &mtd, buf);
629 /*======================================================================
631 This isn't needed for anything I could think of.
633 ======================================================================*/
635 int pcmcia_copy_memory(memory_handle_t handle, copy_op_t *req)
637 if (CHECK_REGION(handle))
638 return CS_BAD_HANDLE;
639 return CS_UNSUPPORTED_FUNCTION;