- Kai Germaschewski: ymfpci cleanups and resource leak fixes
[davej-history.git] / drivers / pcmcia / bulkmem.c
blob232ab5ea551c40c2822fb0c44913556015616516
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 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/malloc.h>
41 #include <linux/mm.h>
42 #include <linux/sched.h>
43 #include <linux/timer.h>
45 #define IN_CARD_SERVICES
46 #include <pcmcia/cs_types.h>
47 #include <pcmcia/ss.h>
48 #include <pcmcia/cs.h>
49 #include <pcmcia/bulkmem.h>
50 #include <pcmcia/cistpl.h>
51 #include "cs_internal.h"
53 /*======================================================================
55 This function handles submitting an MTD request, and retrying
56 requests when an MTD is busy.
58 An MTD request should never block.
60 ======================================================================*/
62 static int do_mtd_request(memory_handle_t handle, mtd_request_t *req,
63 caddr_t buf)
65 int ret, tries;
66 client_t *mtd;
67 socket_info_t *s;
69 mtd = handle->mtd;
70 if (mtd == NULL)
71 return CS_GENERAL_FAILURE;
72 s = SOCKET(mtd);
73 for (ret = tries = 0; tries < 100; tries++) {
74 mtd->event_callback_args.mtdrequest = req;
75 mtd->event_callback_args.buffer = buf;
76 ret = EVENT(mtd, CS_EVENT_MTD_REQUEST, CS_EVENT_PRI_LOW);
77 if (ret != CS_BUSY)
78 break;
79 switch (req->Status) {
80 case MTD_WAITREQ:
81 /* Not that we should ever need this... */
82 interruptible_sleep_on_timeout(&mtd->mtd_req, HZ);
83 break;
84 case MTD_WAITTIMER:
85 case MTD_WAITRDY:
86 interruptible_sleep_on_timeout(&mtd->mtd_req, req->Timeout*HZ/1000);
87 req->Function |= MTD_REQ_TIMEOUT;
88 break;
89 case MTD_WAITPOWER:
90 interruptible_sleep_on(&mtd->mtd_req);
91 break;
93 if (signal_pending(current))
94 printk(KERN_NOTICE "cs: do_mtd_request interrupted!\n");
96 if (tries == 20) {
97 printk(KERN_NOTICE "cs: MTD request timed out!\n");
98 ret = CS_GENERAL_FAILURE;
100 wake_up_interruptible(&mtd->mtd_req);
101 retry_erase_list(&mtd->erase_busy, 0);
102 return ret;
103 } /* do_mtd_request */
105 /*======================================================================
107 This stuff is all for handling asynchronous erase requests. It
108 is complicated because all the retry stuff has to be dealt with
109 in timer interrupts or in the card status event handler.
111 ======================================================================*/
113 static void insert_queue(erase_busy_t *head, erase_busy_t *entry)
115 DEBUG(2, "cs: adding 0x%p to queue 0x%p\n", entry, head);
116 entry->next = head;
117 entry->prev = head->prev;
118 head->prev->next = entry;
119 head->prev = entry;
122 static void remove_queue(erase_busy_t *entry)
124 DEBUG(2, "cs: unqueueing 0x%p\n", entry);
125 entry->next->prev = entry->prev;
126 entry->prev->next = entry->next;
129 static void retry_erase(erase_busy_t *busy, u_int cause)
131 eraseq_entry_t *erase = busy->erase;
132 mtd_request_t req;
133 client_t *mtd;
134 socket_info_t *s;
135 int ret;
137 DEBUG(2, "cs: trying erase request 0x%p...\n", busy);
138 if (busy->next)
139 remove_queue(busy);
140 req.Function = MTD_REQ_ERASE | cause;
141 req.TransferLength = erase->Size;
142 req.DestCardOffset = erase->Offset + erase->Handle->info.CardOffset;
143 req.MediaID = erase->Handle->MediaID;
144 mtd = erase->Handle->mtd;
145 s = SOCKET(mtd);
146 mtd->event_callback_args.mtdrequest = &req;
147 ret = EVENT(mtd, CS_EVENT_MTD_REQUEST, CS_EVENT_PRI_LOW);
148 if (ret == CS_BUSY) {
149 DEBUG(2, " Status = %d, requeueing.\n", req.Status);
150 switch (req.Status) {
151 case MTD_WAITREQ:
152 case MTD_WAITPOWER:
153 insert_queue(&mtd->erase_busy, busy);
154 break;
155 case MTD_WAITTIMER:
156 case MTD_WAITRDY:
157 if (req.Status == MTD_WAITRDY)
158 insert_queue(&s->erase_busy, busy);
159 mod_timer(&busy->timeout, jiffies + req.Timeout*HZ/1000);
160 break;
162 } else {
163 /* update erase queue status */
164 DEBUG(2, " Ret = %d\n", ret);
165 switch (ret) {
166 case CS_SUCCESS:
167 erase->State = ERASE_PASSED; break;
168 case CS_WRITE_PROTECTED:
169 erase->State = ERASE_MEDIA_WRPROT; break;
170 case CS_BAD_OFFSET:
171 erase->State = ERASE_BAD_OFFSET; break;
172 case CS_BAD_SIZE:
173 erase->State = ERASE_BAD_SIZE; break;
174 case CS_NO_CARD:
175 erase->State = ERASE_BAD_SOCKET; break;
176 default:
177 erase->State = ERASE_FAILED; break;
179 busy->client->event_callback_args.info = erase;
180 EVENT(busy->client, CS_EVENT_ERASE_COMPLETE, CS_EVENT_PRI_LOW);
181 kfree(busy);
182 /* Resubmit anything waiting for a request to finish */
183 wake_up_interruptible(&mtd->mtd_req);
184 retry_erase_list(&mtd->erase_busy, 0);
186 } /* retry_erase */
188 void retry_erase_list(erase_busy_t *list, u_int cause)
190 erase_busy_t tmp = *list;
192 DEBUG(2, "cs: rescanning erase queue list 0x%p\n", list);
193 if (list->next == list)
194 return;
195 /* First, truncate the original list */
196 list->prev->next = &tmp;
197 list->next->prev = &tmp;
198 list->prev = list->next = list;
199 tmp.prev->next = &tmp;
200 tmp.next->prev = &tmp;
202 /* Now, retry each request, in order. */
203 while (tmp.next != &tmp)
204 retry_erase(tmp.next, cause);
205 } /* retry_erase_list */
207 static void handle_erase_timeout(u_long arg)
209 DEBUG(0, "cs: erase timeout for entry 0x%lx\n", arg);
210 retry_erase((erase_busy_t *)arg, MTD_REQ_TIMEOUT);
213 static void setup_erase_request(client_handle_t handle, eraseq_entry_t *erase)
215 erase_busy_t *busy;
216 region_info_t *info;
218 if (CHECK_REGION(erase->Handle))
219 erase->State = ERASE_BAD_SOCKET;
220 else {
221 info = &erase->Handle->info;
222 if ((erase->Offset >= info->RegionSize) ||
223 (erase->Offset & (info->BlockSize-1)))
224 erase->State = ERASE_BAD_OFFSET;
225 else if ((erase->Offset+erase->Size > info->RegionSize) ||
226 (erase->Size & (info->BlockSize-1)))
227 erase->State = ERASE_BAD_SIZE;
228 else {
229 erase->State = 1;
230 busy = kmalloc(sizeof(erase_busy_t), GFP_KERNEL);
231 busy->erase = erase;
232 busy->client = handle;
233 init_timer(&busy->timeout);
234 busy->timeout.data = (u_long)busy;
235 busy->timeout.function = &handle_erase_timeout;
236 busy->prev = busy->next = NULL;
237 retry_erase(busy, 0);
240 } /* setup_erase_request */
242 /*======================================================================
244 MTD helper functions
246 ======================================================================*/
248 static int mtd_modify_window(window_handle_t win, mtd_mod_win_t *req)
250 if ((win == NULL) || (win->magic != WINDOW_MAGIC))
251 return CS_BAD_HANDLE;
252 win->ctl.flags = MAP_16BIT | MAP_ACTIVE;
253 if (req->Attributes & WIN_USE_WAIT)
254 win->ctl.flags |= MAP_USE_WAIT;
255 if (req->Attributes & WIN_MEMORY_TYPE)
256 win->ctl.flags |= MAP_ATTRIB;
257 win->ctl.speed = req->AccessSpeed;
258 win->ctl.card_start = req->CardOffset;
259 win->sock->ss_entry->set_mem_map(win->sock->sock, &win->ctl);
260 return CS_SUCCESS;
263 static int mtd_set_vpp(client_handle_t handle, mtd_vpp_req_t *req)
265 socket_info_t *s;
266 if (CHECK_HANDLE(handle))
267 return CS_BAD_HANDLE;
268 if (req->Vpp1 != req->Vpp2)
269 return CS_BAD_VPP;
270 s = SOCKET(handle);
271 s->socket.Vpp = req->Vpp1;
272 if (s->ss_entry->set_socket(s->sock, &s->socket))
273 return CS_BAD_VPP;
274 return CS_SUCCESS;
277 static int mtd_rdy_mask(client_handle_t handle, mtd_rdy_req_t *req)
279 socket_info_t *s;
280 if (CHECK_HANDLE(handle))
281 return CS_BAD_HANDLE;
282 s = SOCKET(handle);
283 if (req->Mask & CS_EVENT_READY_CHANGE)
284 s->socket.csc_mask |= SS_READY;
285 else
286 s->socket.csc_mask &= ~SS_READY;
287 if (s->ss_entry->set_socket(s->sock, &s->socket))
288 return CS_GENERAL_FAILURE;
289 return CS_SUCCESS;
292 int MTDHelperEntry(int func, void *a1, void *a2)
294 switch (func) {
295 case MTDRequestWindow:
297 window_handle_t w;
298 int ret = pcmcia_request_window(a1, a2, &w);
299 (window_handle_t *)a1 = w;
300 return ret;
302 break;
303 case MTDReleaseWindow:
304 return pcmcia_release_window(a1);
305 case MTDModifyWindow:
306 return mtd_modify_window(a1, a2); break;
307 case MTDSetVpp:
308 return mtd_set_vpp(a1, a2); break;
309 case MTDRDYMask:
310 return mtd_rdy_mask(a1, a2); break;
311 default:
312 return CS_UNSUPPORTED_FUNCTION; break;
314 } /* MTDHelperEntry */
316 /*======================================================================
318 This stuff is used by Card Services to initialize the table of
319 region info used for subsequent calls to GetFirstRegion and
320 GetNextRegion.
322 ======================================================================*/
324 static void setup_regions(client_handle_t handle, int attr,
325 memory_handle_t *list)
327 int i, code, has_jedec, has_geo;
328 u_int offset;
329 cistpl_device_t device;
330 cistpl_jedec_t jedec;
331 cistpl_device_geo_t geo;
332 memory_handle_t r;
334 DEBUG(1, "cs: setup_regions(0x%p, %d, 0x%p)\n",
335 handle, attr, list);
337 code = (attr) ? CISTPL_DEVICE_A : CISTPL_DEVICE;
338 if (read_tuple(handle, code, &device) != CS_SUCCESS)
339 return;
340 code = (attr) ? CISTPL_JEDEC_A : CISTPL_JEDEC_C;
341 has_jedec = (read_tuple(handle, code, &jedec) == CS_SUCCESS);
342 if (has_jedec && (device.ndev != jedec.nid)) {
343 #ifdef PCMCIA_DEBUG
344 printk(KERN_DEBUG "cs: Device info does not match JEDEC info.\n");
345 #endif
346 has_jedec = 0;
348 code = (attr) ? CISTPL_DEVICE_GEO_A : CISTPL_DEVICE_GEO;
349 has_geo = (read_tuple(handle, code, &geo) == CS_SUCCESS);
350 if (has_geo && (device.ndev != geo.ngeo)) {
351 #ifdef PCMCIA_DEBUG
352 printk(KERN_DEBUG "cs: Device info does not match geometry tuple.\n");
353 #endif
354 has_geo = 0;
357 offset = 0;
358 for (i = 0; i < device.ndev; i++) {
359 if ((device.dev[i].type != CISTPL_DTYPE_NULL) &&
360 (device.dev[i].size != 0)) {
361 r = kmalloc(sizeof(*r), GFP_KERNEL);
362 r->region_magic = REGION_MAGIC;
363 r->state = 0;
364 r->dev_info[0] = '\0';
365 r->mtd = NULL;
366 r->info.Attributes = (attr) ? REGION_TYPE_AM : 0;
367 r->info.CardOffset = offset;
368 r->info.RegionSize = device.dev[i].size;
369 r->info.AccessSpeed = device.dev[i].speed;
370 if (has_jedec) {
371 r->info.JedecMfr = jedec.id[i].mfr;
372 r->info.JedecInfo = jedec.id[i].info;
373 } else
374 r->info.JedecMfr = r->info.JedecInfo = 0;
375 if (has_geo) {
376 r->info.BlockSize = geo.geo[i].buswidth *
377 geo.geo[i].erase_block * geo.geo[i].interleave;
378 r->info.PartMultiple =
379 r->info.BlockSize * geo.geo[i].partition;
380 } else
381 r->info.BlockSize = r->info.PartMultiple = 1;
382 r->info.next = *list; *list = r;
384 offset += device.dev[i].size;
386 } /* setup_regions */
388 /*======================================================================
390 This is tricky. When get_first_region() is called by Driver
391 Services, we initialize the region info table in the socket
392 structure. When it is called by an MTD, we can just scan the
393 table for matching entries.
395 ======================================================================*/
397 static int match_region(client_handle_t handle, memory_handle_t list,
398 region_info_t *match)
400 while (list != NULL) {
401 if (!(handle->Attributes & INFO_MTD_CLIENT) ||
402 (strcmp(handle->dev_info, list->dev_info) == 0)) {
403 *match = list->info;
404 return CS_SUCCESS;
406 list = list->info.next;
408 return CS_NO_MORE_ITEMS;
409 } /* match_region */
411 int pcmcia_get_first_region(client_handle_t handle, region_info_t *rgn)
413 socket_info_t *s = SOCKET(handle);
414 if (CHECK_HANDLE(handle))
415 return CS_BAD_HANDLE;
417 if ((handle->Attributes & INFO_MASTER_CLIENT) &&
418 (!(s->state & SOCKET_REGION_INFO))) {
419 setup_regions(handle, 0, &s->c_region);
420 setup_regions(handle, 1, &s->a_region);
421 s->state |= SOCKET_REGION_INFO;
424 if (rgn->Attributes & REGION_TYPE_AM)
425 return match_region(handle, s->a_region, rgn);
426 else
427 return match_region(handle, s->c_region, rgn);
428 } /* get_first_region */
430 int pcmcia_get_next_region(client_handle_t handle, region_info_t *rgn)
432 if (CHECK_HANDLE(handle))
433 return CS_BAD_HANDLE;
434 return match_region(handle, rgn->next, rgn);
435 } /* get_next_region */
437 /*======================================================================
439 Connect an MTD with a memory region.
441 ======================================================================*/
443 int pcmcia_register_mtd(client_handle_t handle, mtd_reg_t *reg)
445 memory_handle_t list;
446 socket_info_t *s;
448 if (CHECK_HANDLE(handle))
449 return CS_BAD_HANDLE;
450 s = SOCKET(handle);
451 if (reg->Attributes & REGION_TYPE_AM)
452 list = s->a_region;
453 else
454 list = s->c_region;
455 DEBUG(1, "cs: register_mtd(0x%p, '%s', 0x%x)\n",
456 handle, handle->dev_info, reg->Offset);
457 while (list) {
458 if (list->info.CardOffset == reg->Offset) break;
459 list = list->info.next;
461 if (list && (list->mtd == NULL) &&
462 (strcmp(handle->dev_info, list->dev_info) == 0)) {
463 list->info.Attributes = reg->Attributes;
464 list->MediaID = reg->MediaID;
465 list->mtd = handle;
466 handle->mtd_count++;
467 return CS_SUCCESS;
468 } else
469 return CS_BAD_OFFSET;
470 } /* register_mtd */
472 /*======================================================================
474 Erase queue management functions
476 ======================================================================*/
478 int pcmcia_register_erase_queue(client_handle_t *handle, eraseq_hdr_t *header,
479 eraseq_handle_t *e)
481 eraseq_t *queue;
483 if ((handle == NULL) || CHECK_HANDLE(*handle))
484 return CS_BAD_HANDLE;
485 queue = kmalloc(sizeof(*queue), GFP_KERNEL);
486 if (!queue) return CS_OUT_OF_RESOURCE;
487 queue->eraseq_magic = ERASEQ_MAGIC;
488 queue->handle = *handle;
489 queue->count = header->QueueEntryCnt;
490 queue->entry = header->QueueEntryArray;
491 *e = queue;
492 return CS_SUCCESS;
493 } /* register_erase_queue */
495 int pcmcia_deregister_erase_queue(eraseq_handle_t eraseq)
497 int i;
498 if (CHECK_ERASEQ(eraseq))
499 return CS_BAD_HANDLE;
500 for (i = 0; i < eraseq->count; i++)
501 if (ERASE_IN_PROGRESS(eraseq->entry[i].State)) break;
502 if (i < eraseq->count)
503 return CS_BUSY;
504 eraseq->eraseq_magic = 0;
505 kfree(eraseq);
506 return CS_SUCCESS;
507 } /* deregister_erase_queue */
509 int pcmcia_check_erase_queue(eraseq_handle_t eraseq)
511 int i;
512 if (CHECK_ERASEQ(eraseq))
513 return CS_BAD_HANDLE;
514 for (i = 0; i < eraseq->count; i++)
515 if (eraseq->entry[i].State == ERASE_QUEUED)
516 setup_erase_request(eraseq->handle, &eraseq->entry[i]);
517 return CS_SUCCESS;
518 } /* check_erase_queue */
520 /*======================================================================
522 Look up the memory region matching the request, and return a
523 memory handle.
525 ======================================================================*/
527 int pcmcia_open_memory(client_handle_t *handle, open_mem_t *open, memory_handle_t *mh)
529 socket_info_t *s;
530 memory_handle_t region;
532 if ((handle == NULL) || CHECK_HANDLE(*handle))
533 return CS_BAD_HANDLE;
534 s = SOCKET(*handle);
535 if (open->Attributes & MEMORY_TYPE_AM)
536 region = s->a_region;
537 else
538 region = s->c_region;
539 while (region) {
540 if (region->info.CardOffset == open->Offset) break;
541 region = region->info.next;
543 if (region && region->mtd) {
544 *mh = region;
545 DEBUG(1, "cs: open_memory(0x%p, 0x%x) = 0x%p\n",
546 handle, open->Offset, region);
547 return CS_SUCCESS;
548 } else
549 return CS_BAD_OFFSET;
550 } /* open_memory */
552 /*======================================================================
554 Close a memory handle from an earlier call to OpenMemory.
556 For the moment, I don't think this needs to do anything.
558 ======================================================================*/
560 int pcmcia_close_memory(memory_handle_t handle)
562 DEBUG(1, "cs: close_memory(0x%p)\n", handle);
563 if (CHECK_REGION(handle))
564 return CS_BAD_HANDLE;
565 return CS_SUCCESS;
566 } /* close_memory */
568 /*======================================================================
570 Read from a memory device, using a handle previously returned
571 by a call to OpenMemory.
573 ======================================================================*/
575 int pcmcia_read_memory(memory_handle_t handle, mem_op_t *req, caddr_t buf)
577 mtd_request_t mtd;
578 if (CHECK_REGION(handle))
579 return CS_BAD_HANDLE;
580 if (req->Offset >= handle->info.RegionSize)
581 return CS_BAD_OFFSET;
582 if (req->Offset+req->Count > handle->info.RegionSize)
583 return CS_BAD_SIZE;
585 mtd.SrcCardOffset = req->Offset + handle->info.CardOffset;
586 mtd.TransferLength = req->Count;
587 mtd.MediaID = handle->MediaID;
588 mtd.Function = MTD_REQ_READ;
589 if (req->Attributes & MEM_OP_BUFFER_KERNEL)
590 mtd.Function |= MTD_REQ_KERNEL;
591 return do_mtd_request(handle, &mtd, buf);
592 } /* read_memory */
594 /*======================================================================
596 Write to a memory device, using a handle previously returned by
597 a call to OpenMemory.
599 ======================================================================*/
601 int pcmcia_write_memory(memory_handle_t handle, mem_op_t *req, caddr_t buf)
603 mtd_request_t mtd;
604 if (CHECK_REGION(handle))
605 return CS_BAD_HANDLE;
606 if (req->Offset >= handle->info.RegionSize)
607 return CS_BAD_OFFSET;
608 if (req->Offset+req->Count > handle->info.RegionSize)
609 return CS_BAD_SIZE;
611 mtd.DestCardOffset = req->Offset + handle->info.CardOffset;
612 mtd.TransferLength = req->Count;
613 mtd.MediaID = handle->MediaID;
614 mtd.Function = MTD_REQ_WRITE;
615 if (req->Attributes & MEM_OP_BUFFER_KERNEL)
616 mtd.Function |= MTD_REQ_KERNEL;
617 return do_mtd_request(handle, &mtd, buf);
618 } /* write_memory */
620 /*======================================================================
622 This isn't needed for anything I could think of.
624 ======================================================================*/
626 int pcmcia_copy_memory(memory_handle_t handle, copy_op_t *req)
628 if (CHECK_REGION(handle))
629 return CS_BAD_HANDLE;
630 return CS_UNSUPPORTED_FUNCTION;