Merge with Linux 2.6.0-test1.
[linux-2.6/linux-mips.git] / drivers / pcmcia / bulkmem.c
blob4759c521694b93112f88c0ee8e8275e580a19e82
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 #include <linux/module.h>
35 #include <linux/kernel.h>
36 #include <linux/string.h>
37 #include <linux/errno.h>
38 #include <linux/slab.h>
39 #include <linux/mm.h>
40 #include <linux/sched.h>
41 #include <linux/timer.h>
43 #define IN_CARD_SERVICES
44 #include <pcmcia/cs_types.h>
45 #include <pcmcia/ss.h>
46 #include <pcmcia/cs.h>
47 #include <pcmcia/bulkmem.h>
48 #include <pcmcia/cistpl.h>
49 #include "cs_internal.h"
51 /*======================================================================
53 This function handles submitting an MTD request, and retrying
54 requests when an MTD is busy.
56 An MTD request should never block.
58 ======================================================================*/
60 static int do_mtd_request(memory_handle_t handle, mtd_request_t *req,
61 caddr_t buf)
63 int ret, tries;
64 client_t *mtd;
65 struct pcmcia_socket *s;
67 mtd = handle->mtd;
68 if (mtd == NULL)
69 return CS_GENERAL_FAILURE;
70 s = SOCKET(mtd);
71 for (ret = tries = 0; tries < 100; tries++) {
72 mtd->event_callback_args.mtdrequest = req;
73 mtd->event_callback_args.buffer = buf;
74 ret = EVENT(mtd, CS_EVENT_MTD_REQUEST, CS_EVENT_PRI_LOW);
75 if (ret != CS_BUSY)
76 break;
77 switch (req->Status) {
78 case MTD_WAITREQ:
79 /* Not that we should ever need this... */
80 interruptible_sleep_on_timeout(&mtd->mtd_req, HZ);
81 break;
82 case MTD_WAITTIMER:
83 case MTD_WAITRDY:
84 interruptible_sleep_on_timeout(&mtd->mtd_req, req->Timeout*HZ/1000);
85 req->Function |= MTD_REQ_TIMEOUT;
86 break;
87 case MTD_WAITPOWER:
88 interruptible_sleep_on(&mtd->mtd_req);
89 break;
91 if (signal_pending(current))
92 printk(KERN_NOTICE "cs: do_mtd_request interrupted!\n");
94 if (tries == 20) {
95 printk(KERN_NOTICE "cs: MTD request timed out!\n");
96 ret = CS_GENERAL_FAILURE;
98 wake_up_interruptible(&mtd->mtd_req);
99 retry_erase_list(&mtd->erase_busy, 0);
100 return ret;
101 } /* do_mtd_request */
103 /*======================================================================
105 This stuff is all for handling asynchronous erase requests. It
106 is complicated because all the retry stuff has to be dealt with
107 in timer interrupts or in the card status event handler.
109 ======================================================================*/
111 static void insert_queue(erase_busy_t *head, erase_busy_t *entry)
113 DEBUG(2, "cs: adding 0x%p to queue 0x%p\n", entry, head);
114 entry->next = head;
115 entry->prev = head->prev;
116 head->prev->next = entry;
117 head->prev = entry;
120 static void remove_queue(erase_busy_t *entry)
122 DEBUG(2, "cs: unqueueing 0x%p\n", entry);
123 entry->next->prev = entry->prev;
124 entry->prev->next = entry->next;
127 static void retry_erase(erase_busy_t *busy, u_int cause)
129 eraseq_entry_t *erase = busy->erase;
130 mtd_request_t req;
131 client_t *mtd;
132 struct pcmcia_socket *s;
133 int ret;
135 DEBUG(2, "cs: trying erase request 0x%p...\n", busy);
136 if (busy->next)
137 remove_queue(busy);
138 req.Function = MTD_REQ_ERASE | cause;
139 req.TransferLength = erase->Size;
140 req.DestCardOffset = erase->Offset + erase->Handle->info.CardOffset;
141 req.MediaID = erase->Handle->MediaID;
142 mtd = erase->Handle->mtd;
143 s = SOCKET(mtd);
144 mtd->event_callback_args.mtdrequest = &req;
145 ret = EVENT(mtd, CS_EVENT_MTD_REQUEST, CS_EVENT_PRI_LOW);
146 if (ret == CS_BUSY) {
147 DEBUG(2, " Status = %d, requeueing.\n", req.Status);
148 switch (req.Status) {
149 case MTD_WAITREQ:
150 case MTD_WAITPOWER:
151 insert_queue(&mtd->erase_busy, busy);
152 break;
153 case MTD_WAITTIMER:
154 case MTD_WAITRDY:
155 if (req.Status == MTD_WAITRDY)
156 insert_queue(&s->erase_busy, busy);
157 mod_timer(&busy->timeout, jiffies + req.Timeout*HZ/1000);
158 break;
160 } else {
161 /* update erase queue status */
162 DEBUG(2, " Ret = %d\n", ret);
163 switch (ret) {
164 case CS_SUCCESS:
165 erase->State = ERASE_PASSED; break;
166 case CS_WRITE_PROTECTED:
167 erase->State = ERASE_MEDIA_WRPROT; break;
168 case CS_BAD_OFFSET:
169 erase->State = ERASE_BAD_OFFSET; break;
170 case CS_BAD_SIZE:
171 erase->State = ERASE_BAD_SIZE; break;
172 case CS_NO_CARD:
173 erase->State = ERASE_BAD_SOCKET; break;
174 default:
175 erase->State = ERASE_FAILED; break;
177 busy->client->event_callback_args.info = erase;
178 EVENT(busy->client, CS_EVENT_ERASE_COMPLETE, CS_EVENT_PRI_LOW);
179 kfree(busy);
180 /* Resubmit anything waiting for a request to finish */
181 wake_up_interruptible(&mtd->mtd_req);
182 retry_erase_list(&mtd->erase_busy, 0);
184 } /* retry_erase */
186 void retry_erase_list(erase_busy_t *list, u_int cause)
188 erase_busy_t tmp = *list;
190 DEBUG(2, "cs: rescanning erase queue list 0x%p\n", list);
191 if (list->next == list)
192 return;
193 /* First, truncate the original list */
194 list->prev->next = &tmp;
195 list->next->prev = &tmp;
196 list->prev = list->next = list;
197 tmp.prev->next = &tmp;
198 tmp.next->prev = &tmp;
200 /* Now, retry each request, in order. */
201 while (tmp.next != &tmp)
202 retry_erase(tmp.next, cause);
203 } /* retry_erase_list */
205 static void handle_erase_timeout(u_long arg)
207 DEBUG(0, "cs: erase timeout for entry 0x%lx\n", arg);
208 retry_erase((erase_busy_t *)arg, MTD_REQ_TIMEOUT);
211 static void setup_erase_request(client_handle_t handle, eraseq_entry_t *erase)
213 erase_busy_t *busy;
214 region_info_t *info;
216 if (CHECK_REGION(erase->Handle))
217 erase->State = ERASE_BAD_SOCKET;
218 else {
219 info = &erase->Handle->info;
220 if ((erase->Offset >= info->RegionSize) ||
221 (erase->Offset & (info->BlockSize-1)))
222 erase->State = ERASE_BAD_OFFSET;
223 else if ((erase->Offset+erase->Size > info->RegionSize) ||
224 (erase->Size & (info->BlockSize-1)))
225 erase->State = ERASE_BAD_SIZE;
226 else {
227 erase->State = 1;
228 busy = kmalloc(sizeof(erase_busy_t), GFP_KERNEL);
229 if (!busy) {
230 erase->State = ERASE_FAILED;
231 return;
233 busy->erase = erase;
234 busy->client = handle;
235 init_timer(&busy->timeout);
236 busy->timeout.data = (u_long)busy;
237 busy->timeout.function = &handle_erase_timeout;
238 busy->prev = busy->next = NULL;
239 retry_erase(busy, 0);
242 } /* setup_erase_request */
244 /*======================================================================
246 MTD helper functions
248 ======================================================================*/
250 static int mtd_modify_window(window_handle_t win, mtd_mod_win_t *req)
252 if ((win == NULL) || (win->magic != WINDOW_MAGIC))
253 return CS_BAD_HANDLE;
254 win->ctl.flags = MAP_16BIT | MAP_ACTIVE;
255 if (req->Attributes & WIN_USE_WAIT)
256 win->ctl.flags |= MAP_USE_WAIT;
257 if (req->Attributes & WIN_MEMORY_TYPE)
258 win->ctl.flags |= MAP_ATTRIB;
259 win->ctl.speed = req->AccessSpeed;
260 win->ctl.card_start = req->CardOffset;
261 win->sock->ops->set_mem_map(win->sock, &win->ctl);
262 return CS_SUCCESS;
265 static int mtd_set_vpp(client_handle_t handle, mtd_vpp_req_t *req)
267 struct pcmcia_socket *s;
268 if (CHECK_HANDLE(handle))
269 return CS_BAD_HANDLE;
270 if (req->Vpp1 != req->Vpp2)
271 return CS_BAD_VPP;
272 s = SOCKET(handle);
273 s->socket.Vpp = req->Vpp1;
274 if (s->ops->set_socket(s, &s->socket))
275 return CS_BAD_VPP;
276 return CS_SUCCESS;
279 static int mtd_rdy_mask(client_handle_t handle, mtd_rdy_req_t *req)
281 struct pcmcia_socket *s;
282 if (CHECK_HANDLE(handle))
283 return CS_BAD_HANDLE;
284 s = SOCKET(handle);
285 if (req->Mask & CS_EVENT_READY_CHANGE)
286 s->socket.csc_mask |= SS_READY;
287 else
288 s->socket.csc_mask &= ~SS_READY;
289 if (s->ops->set_socket(s, &s->socket))
290 return CS_GENERAL_FAILURE;
291 return CS_SUCCESS;
294 int MTDHelperEntry(int func, void *a1, void *a2)
296 switch (func) {
297 case MTDRequestWindow:
299 window_handle_t w;
300 int ret = pcmcia_request_window(a1, a2, &w);
301 (window_handle_t *)a1 = w;
302 return ret;
304 break;
305 case MTDReleaseWindow:
306 return pcmcia_release_window(a1);
307 case MTDModifyWindow:
308 return mtd_modify_window(a1, a2); break;
309 case MTDSetVpp:
310 return mtd_set_vpp(a1, a2); break;
311 case MTDRDYMask:
312 return mtd_rdy_mask(a1, a2); break;
313 default:
314 return CS_UNSUPPORTED_FUNCTION; break;
316 } /* MTDHelperEntry */
318 /*======================================================================
320 This stuff is used by Card Services to initialize the table of
321 region info used for subsequent calls to GetFirstRegion and
322 GetNextRegion.
324 ======================================================================*/
326 static void setup_regions(client_handle_t handle, int attr,
327 memory_handle_t *list)
329 int i, code, has_jedec, has_geo;
330 u_int offset;
331 cistpl_device_t device;
332 cistpl_jedec_t jedec;
333 cistpl_device_geo_t geo;
334 memory_handle_t r;
336 DEBUG(1, "cs: setup_regions(0x%p, %d, 0x%p)\n",
337 handle, attr, list);
339 code = (attr) ? CISTPL_DEVICE_A : CISTPL_DEVICE;
340 if (read_tuple(handle, code, &device) != CS_SUCCESS)
341 return;
342 code = (attr) ? CISTPL_JEDEC_A : CISTPL_JEDEC_C;
343 has_jedec = (read_tuple(handle, code, &jedec) == CS_SUCCESS);
344 if (has_jedec && (device.ndev != jedec.nid)) {
345 #ifdef PCMCIA_DEBUG
346 printk(KERN_DEBUG "cs: Device info does not match JEDEC info.\n");
347 #endif
348 has_jedec = 0;
350 code = (attr) ? CISTPL_DEVICE_GEO_A : CISTPL_DEVICE_GEO;
351 has_geo = (read_tuple(handle, code, &geo) == CS_SUCCESS);
352 if (has_geo && (device.ndev != geo.ngeo)) {
353 #ifdef PCMCIA_DEBUG
354 printk(KERN_DEBUG "cs: Device info does not match geometry tuple.\n");
355 #endif
356 has_geo = 0;
359 offset = 0;
360 for (i = 0; i < device.ndev; i++) {
361 if ((device.dev[i].type != CISTPL_DTYPE_NULL) &&
362 (device.dev[i].size != 0)) {
363 r = kmalloc(sizeof(*r), GFP_KERNEL);
364 if (!r) {
365 printk(KERN_NOTICE "cs: setup_regions: kmalloc failed!\n");
366 return;
368 r->region_magic = REGION_MAGIC;
369 r->state = 0;
370 r->dev_info[0] = '\0';
371 r->mtd = NULL;
372 r->info.Attributes = (attr) ? REGION_TYPE_AM : 0;
373 r->info.CardOffset = offset;
374 r->info.RegionSize = device.dev[i].size;
375 r->info.AccessSpeed = device.dev[i].speed;
376 if (has_jedec) {
377 r->info.JedecMfr = jedec.id[i].mfr;
378 r->info.JedecInfo = jedec.id[i].info;
379 } else
380 r->info.JedecMfr = r->info.JedecInfo = 0;
381 if (has_geo) {
382 r->info.BlockSize = geo.geo[i].buswidth *
383 geo.geo[i].erase_block * geo.geo[i].interleave;
384 r->info.PartMultiple =
385 r->info.BlockSize * geo.geo[i].partition;
386 } else
387 r->info.BlockSize = r->info.PartMultiple = 1;
388 r->info.next = *list; *list = r;
390 offset += device.dev[i].size;
392 } /* setup_regions */
394 /*======================================================================
396 This is tricky. When get_first_region() is called by Driver
397 Services, we initialize the region info table in the socket
398 structure. When it is called by an MTD, we can just scan the
399 table for matching entries.
401 ======================================================================*/
403 static int match_region(client_handle_t handle, memory_handle_t list,
404 region_info_t *match)
406 while (list != NULL) {
407 if (!(handle->Attributes & INFO_MTD_CLIENT) ||
408 (strcmp(handle->dev_info, list->dev_info) == 0)) {
409 *match = list->info;
410 return CS_SUCCESS;
412 list = list->info.next;
414 return CS_NO_MORE_ITEMS;
415 } /* match_region */
417 int pcmcia_get_first_region(client_handle_t handle, region_info_t *rgn)
419 struct pcmcia_socket *s = SOCKET(handle);
420 if (CHECK_HANDLE(handle))
421 return CS_BAD_HANDLE;
423 if ((handle->Attributes & INFO_MASTER_CLIENT) &&
424 (!(s->state & SOCKET_REGION_INFO))) {
425 setup_regions(handle, 0, &s->c_region);
426 setup_regions(handle, 1, &s->a_region);
427 s->state |= SOCKET_REGION_INFO;
430 if (rgn->Attributes & REGION_TYPE_AM)
431 return match_region(handle, s->a_region, rgn);
432 else
433 return match_region(handle, s->c_region, rgn);
434 } /* get_first_region */
436 int pcmcia_get_next_region(client_handle_t handle, region_info_t *rgn)
438 if (CHECK_HANDLE(handle))
439 return CS_BAD_HANDLE;
440 return match_region(handle, rgn->next, rgn);
441 } /* get_next_region */
443 /*======================================================================
445 Connect an MTD with a memory region.
447 ======================================================================*/
449 int pcmcia_register_mtd(client_handle_t handle, mtd_reg_t *reg)
451 memory_handle_t list;
452 struct pcmcia_socket *s;
454 if (CHECK_HANDLE(handle))
455 return CS_BAD_HANDLE;
456 s = SOCKET(handle);
457 if (reg->Attributes & REGION_TYPE_AM)
458 list = s->a_region;
459 else
460 list = s->c_region;
461 DEBUG(1, "cs: register_mtd(0x%p, '%s', 0x%x)\n",
462 handle, handle->dev_info, reg->Offset);
463 while (list) {
464 if (list->info.CardOffset == reg->Offset) break;
465 list = list->info.next;
467 if (list && (list->mtd == NULL) &&
468 (strcmp(handle->dev_info, list->dev_info) == 0)) {
469 list->info.Attributes = reg->Attributes;
470 list->MediaID = reg->MediaID;
471 list->mtd = handle;
472 handle->mtd_count++;
473 return CS_SUCCESS;
474 } else
475 return CS_BAD_OFFSET;
476 } /* register_mtd */
478 /*======================================================================
480 Erase queue management functions
482 ======================================================================*/
484 int pcmcia_register_erase_queue(client_handle_t *handle, eraseq_hdr_t *header,
485 eraseq_handle_t *e)
487 eraseq_t *queue;
489 if ((handle == NULL) || CHECK_HANDLE(*handle))
490 return CS_BAD_HANDLE;
491 queue = kmalloc(sizeof(*queue), GFP_KERNEL);
492 if (!queue) return CS_OUT_OF_RESOURCE;
493 queue->eraseq_magic = ERASEQ_MAGIC;
494 queue->handle = *handle;
495 queue->count = header->QueueEntryCnt;
496 queue->entry = header->QueueEntryArray;
497 *e = queue;
498 return CS_SUCCESS;
499 } /* register_erase_queue */
501 int pcmcia_deregister_erase_queue(eraseq_handle_t eraseq)
503 int i;
504 if (CHECK_ERASEQ(eraseq))
505 return CS_BAD_HANDLE;
506 for (i = 0; i < eraseq->count; i++)
507 if (ERASE_IN_PROGRESS(eraseq->entry[i].State)) break;
508 if (i < eraseq->count)
509 return CS_BUSY;
510 eraseq->eraseq_magic = 0;
511 kfree(eraseq);
512 return CS_SUCCESS;
513 } /* deregister_erase_queue */
515 int pcmcia_check_erase_queue(eraseq_handle_t eraseq)
517 int i;
518 if (CHECK_ERASEQ(eraseq))
519 return CS_BAD_HANDLE;
520 for (i = 0; i < eraseq->count; i++)
521 if (eraseq->entry[i].State == ERASE_QUEUED)
522 setup_erase_request(eraseq->handle, &eraseq->entry[i]);
523 return CS_SUCCESS;
524 } /* check_erase_queue */
526 /*======================================================================
528 Look up the memory region matching the request, and return a
529 memory handle.
531 ======================================================================*/
533 int pcmcia_open_memory(client_handle_t *handle, open_mem_t *open, memory_handle_t *mh)
535 struct pcmcia_socket *s;
536 memory_handle_t region;
538 if ((handle == NULL) || CHECK_HANDLE(*handle))
539 return CS_BAD_HANDLE;
540 s = (*handle)->Socket;
541 if (open->Attributes & MEMORY_TYPE_AM)
542 region = s->a_region;
543 else
544 region = s->c_region;
545 while (region) {
546 if (region->info.CardOffset == open->Offset) break;
547 region = region->info.next;
549 if (region && region->mtd) {
550 *mh = region;
551 DEBUG(1, "cs: open_memory(0x%p, 0x%x) = 0x%p\n",
552 handle, open->Offset, region);
553 return CS_SUCCESS;
554 } else
555 return CS_BAD_OFFSET;
556 } /* open_memory */
558 /*======================================================================
560 Close a memory handle from an earlier call to OpenMemory.
562 For the moment, I don't think this needs to do anything.
564 ======================================================================*/
566 int pcmcia_close_memory(memory_handle_t handle)
568 DEBUG(1, "cs: close_memory(0x%p)\n", handle);
569 if (CHECK_REGION(handle))
570 return CS_BAD_HANDLE;
571 return CS_SUCCESS;
572 } /* close_memory */
574 /*======================================================================
576 Read from a memory device, using a handle previously returned
577 by a call to OpenMemory.
579 ======================================================================*/
581 int pcmcia_read_memory(memory_handle_t handle, mem_op_t *req, caddr_t buf)
583 mtd_request_t mtd;
584 if (CHECK_REGION(handle))
585 return CS_BAD_HANDLE;
586 if (req->Offset >= handle->info.RegionSize)
587 return CS_BAD_OFFSET;
588 if (req->Offset+req->Count > handle->info.RegionSize)
589 return CS_BAD_SIZE;
591 mtd.SrcCardOffset = req->Offset + handle->info.CardOffset;
592 mtd.TransferLength = req->Count;
593 mtd.MediaID = handle->MediaID;
594 mtd.Function = MTD_REQ_READ;
595 if (req->Attributes & MEM_OP_BUFFER_KERNEL)
596 mtd.Function |= MTD_REQ_KERNEL;
597 return do_mtd_request(handle, &mtd, buf);
598 } /* read_memory */
600 /*======================================================================
602 Write to a memory device, using a handle previously returned by
603 a call to OpenMemory.
605 ======================================================================*/
607 int pcmcia_write_memory(memory_handle_t handle, mem_op_t *req, caddr_t buf)
609 mtd_request_t mtd;
610 if (CHECK_REGION(handle))
611 return CS_BAD_HANDLE;
612 if (req->Offset >= handle->info.RegionSize)
613 return CS_BAD_OFFSET;
614 if (req->Offset+req->Count > handle->info.RegionSize)
615 return CS_BAD_SIZE;
617 mtd.DestCardOffset = req->Offset + handle->info.CardOffset;
618 mtd.TransferLength = req->Count;
619 mtd.MediaID = handle->MediaID;
620 mtd.Function = MTD_REQ_WRITE;
621 if (req->Attributes & MEM_OP_BUFFER_KERNEL)
622 mtd.Function |= MTD_REQ_KERNEL;
623 return do_mtd_request(handle, &mtd, buf);
624 } /* write_memory */
626 /*======================================================================
628 This isn't needed for anything I could think of.
630 ======================================================================*/
632 int pcmcia_copy_memory(memory_handle_t handle, copy_op_t *req)
634 if (CHECK_REGION(handle))
635 return CS_BAD_HANDLE;
636 return CS_UNSUPPORTED_FUNCTION;