- pre2
[davej-history.git] / drivers / pcmcia / bulkmem.c
blobe20918a25002a4a196f659d644d762fdf830dec1
1 /*======================================================================
3 PCMCIA Bulk Memory Services
5 bulkmem.c 1.37 2000/06/12 21:29:35
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 busy->timeout.expires = jiffies + req.Timeout*HZ/1000;
160 add_timer(&busy->timeout);
161 break;
163 } else {
164 /* update erase queue status */
165 DEBUG(2, " Ret = %d\n", ret);
166 switch (ret) {
167 case CS_SUCCESS:
168 erase->State = ERASE_PASSED; break;
169 case CS_WRITE_PROTECTED:
170 erase->State = ERASE_MEDIA_WRPROT; break;
171 case CS_BAD_OFFSET:
172 erase->State = ERASE_BAD_OFFSET; break;
173 case CS_BAD_SIZE:
174 erase->State = ERASE_BAD_SIZE; break;
175 case CS_NO_CARD:
176 erase->State = ERASE_BAD_SOCKET; break;
177 default:
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);
182 kfree(busy);
183 /* Resubmit anything waiting for a request to finish */
184 wake_up_interruptible(&mtd->mtd_req);
185 retry_erase_list(&mtd->erase_busy, 0);
187 } /* retry_erase */
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)
195 return;
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)
216 erase_busy_t *busy;
217 region_info_t *info;
219 if (CHECK_REGION(erase->Handle))
220 erase->State = ERASE_BAD_SOCKET;
221 else {
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;
229 else {
230 erase->State = 1;
231 busy = kmalloc(sizeof(erase_busy_t), GFP_KERNEL);
232 busy->erase = erase;
233 busy->client = handle;
234 init_timer(&busy->timeout);
235 busy->timeout.data = (u_long)busy;
236 busy->timeout.function = &handle_erase_timeout;
237 busy->prev = busy->next = NULL;
238 retry_erase(busy, 0);
241 } /* setup_erase_request */
243 /*======================================================================
245 MTD helper functions
247 ======================================================================*/
249 static int mtd_modify_window(window_handle_t win, mtd_mod_win_t *req)
251 if ((win == NULL) || (win->magic != WINDOW_MAGIC))
252 return CS_BAD_HANDLE;
253 win->ctl.flags = MAP_16BIT | MAP_ACTIVE;
254 if (req->Attributes & WIN_USE_WAIT)
255 win->ctl.flags |= MAP_USE_WAIT;
256 if (req->Attributes & WIN_MEMORY_TYPE)
257 win->ctl.flags |= MAP_ATTRIB;
258 win->ctl.speed = req->AccessSpeed;
259 win->ctl.card_start = req->CardOffset;
260 win->sock->ss_entry->set_mem_map(win->sock->sock, &win->ctl);
261 return CS_SUCCESS;
264 static int mtd_set_vpp(client_handle_t handle, mtd_vpp_req_t *req)
266 socket_info_t *s;
267 if (CHECK_HANDLE(handle))
268 return CS_BAD_HANDLE;
269 if (req->Vpp1 != req->Vpp2)
270 return CS_BAD_VPP;
271 s = SOCKET(handle);
272 s->socket.Vpp = req->Vpp1;
273 if (s->ss_entry->set_socket(s->sock, &s->socket))
274 return CS_BAD_VPP;
275 return CS_SUCCESS;
278 static int mtd_rdy_mask(client_handle_t handle, mtd_rdy_req_t *req)
280 socket_info_t *s;
281 if (CHECK_HANDLE(handle))
282 return CS_BAD_HANDLE;
283 s = SOCKET(handle);
284 if (req->Mask & CS_EVENT_READY_CHANGE)
285 s->socket.csc_mask |= SS_READY;
286 else
287 s->socket.csc_mask &= ~SS_READY;
288 if (s->ss_entry->set_socket(s->sock, &s->socket))
289 return CS_GENERAL_FAILURE;
290 return CS_SUCCESS;
293 int MTDHelperEntry(int func, void *a1, void *a2)
295 switch (func) {
296 case MTDRequestWindow:
298 window_handle_t w;
299 int ret = pcmcia_request_window(a1, a2, &w);
300 (window_handle_t *)a1 = w;
301 return ret;
303 break;
304 case MTDReleaseWindow:
305 return pcmcia_release_window(a1);
306 case MTDModifyWindow:
307 return mtd_modify_window(a1, a2); break;
308 case MTDSetVpp:
309 return mtd_set_vpp(a1, a2); break;
310 case MTDRDYMask:
311 return mtd_rdy_mask(a1, a2); break;
312 default:
313 return CS_UNSUPPORTED_FUNCTION; break;
315 } /* MTDHelperEntry */
317 /*======================================================================
319 This stuff is used by Card Services to initialize the table of
320 region info used for subsequent calls to GetFirstRegion and
321 GetNextRegion.
323 ======================================================================*/
325 static void setup_regions(client_handle_t handle, int attr,
326 memory_handle_t *list)
328 int i, code, has_jedec, has_geo;
329 u_int offset;
330 cistpl_device_t device;
331 cistpl_jedec_t jedec;
332 cistpl_device_geo_t geo;
333 memory_handle_t r;
335 DEBUG(1, "cs: setup_regions(0x%p, %d, 0x%p)\n",
336 handle, attr, list);
338 code = (attr) ? CISTPL_DEVICE_A : CISTPL_DEVICE;
339 if (read_tuple(handle, code, &device) != CS_SUCCESS)
340 return;
341 code = (attr) ? CISTPL_JEDEC_A : CISTPL_JEDEC_C;
342 has_jedec = (read_tuple(handle, code, &jedec) == CS_SUCCESS);
343 if (has_jedec && (device.ndev != jedec.nid)) {
344 #ifdef PCMCIA_DEBUG
345 printk(KERN_DEBUG "cs: Device info does not match JEDEC info.\n");
346 #endif
347 has_jedec = 0;
349 code = (attr) ? CISTPL_DEVICE_GEO_A : CISTPL_DEVICE_GEO;
350 has_geo = (read_tuple(handle, code, &geo) == CS_SUCCESS);
351 if (has_geo && (device.ndev != geo.ngeo)) {
352 #ifdef PCMCIA_DEBUG
353 printk(KERN_DEBUG "cs: Device info does not match geometry tuple.\n");
354 #endif
355 has_geo = 0;
358 offset = 0;
359 for (i = 0; i < device.ndev; i++) {
360 if ((device.dev[i].type != CISTPL_DTYPE_NULL) &&
361 (device.dev[i].size != 0)) {
362 r = kmalloc(sizeof(*r), GFP_KERNEL);
363 r->region_magic = REGION_MAGIC;
364 r->state = 0;
365 r->dev_info[0] = '\0';
366 r->mtd = NULL;
367 r->info.Attributes = (attr) ? REGION_TYPE_AM : 0;
368 r->info.CardOffset = offset;
369 r->info.RegionSize = device.dev[i].size;
370 r->info.AccessSpeed = device.dev[i].speed;
371 if (has_jedec) {
372 r->info.JedecMfr = jedec.id[i].mfr;
373 r->info.JedecInfo = jedec.id[i].info;
374 } else
375 r->info.JedecMfr = r->info.JedecInfo = 0;
376 if (has_geo) {
377 r->info.BlockSize = geo.geo[i].buswidth *
378 geo.geo[i].erase_block * geo.geo[i].interleave;
379 r->info.PartMultiple =
380 r->info.BlockSize * geo.geo[i].partition;
381 } else
382 r->info.BlockSize = r->info.PartMultiple = 1;
383 r->info.next = *list; *list = r;
385 offset += device.dev[i].size;
387 } /* setup_regions */
389 /*======================================================================
391 This is tricky. When get_first_region() is called by Driver
392 Services, we initialize the region info table in the socket
393 structure. When it is called by an MTD, we can just scan the
394 table for matching entries.
396 ======================================================================*/
398 static int match_region(client_handle_t handle, memory_handle_t list,
399 region_info_t *match)
401 while (list != NULL) {
402 if (!(handle->Attributes & INFO_MTD_CLIENT) ||
403 (strcmp(handle->dev_info, list->dev_info) == 0)) {
404 *match = list->info;
405 return CS_SUCCESS;
407 list = list->info.next;
409 return CS_NO_MORE_ITEMS;
410 } /* match_region */
412 int pcmcia_get_first_region(client_handle_t handle, region_info_t *rgn)
414 socket_info_t *s = SOCKET(handle);
415 if (CHECK_HANDLE(handle))
416 return CS_BAD_HANDLE;
418 if ((handle->Attributes & INFO_MASTER_CLIENT) &&
419 (!(s->state & SOCKET_REGION_INFO))) {
420 setup_regions(handle, 0, &s->c_region);
421 setup_regions(handle, 1, &s->a_region);
422 s->state |= SOCKET_REGION_INFO;
425 if (rgn->Attributes & REGION_TYPE_AM)
426 return match_region(handle, s->a_region, rgn);
427 else
428 return match_region(handle, s->c_region, rgn);
429 } /* get_first_region */
431 int pcmcia_get_next_region(client_handle_t handle, region_info_t *rgn)
433 if (CHECK_HANDLE(handle))
434 return CS_BAD_HANDLE;
435 return match_region(handle, rgn->next, rgn);
436 } /* get_next_region */
438 /*======================================================================
440 Connect an MTD with a memory region.
442 ======================================================================*/
444 int pcmcia_register_mtd(client_handle_t handle, mtd_reg_t *reg)
446 memory_handle_t list;
447 socket_info_t *s;
449 if (CHECK_HANDLE(handle))
450 return CS_BAD_HANDLE;
451 s = SOCKET(handle);
452 if (reg->Attributes & REGION_TYPE_AM)
453 list = s->a_region;
454 else
455 list = s->c_region;
456 DEBUG(1, "cs: register_mtd(0x%p, '%s', 0x%x)\n",
457 handle, handle->dev_info, reg->Offset);
458 while (list) {
459 if (list->info.CardOffset == reg->Offset) break;
460 list = list->info.next;
462 if (list && (list->mtd == NULL) &&
463 (strcmp(handle->dev_info, list->dev_info) == 0)) {
464 list->info.Attributes = reg->Attributes;
465 list->MediaID = reg->MediaID;
466 list->mtd = handle;
467 handle->mtd_count++;
468 return CS_SUCCESS;
469 } else
470 return CS_BAD_OFFSET;
471 } /* register_mtd */
473 /*======================================================================
475 Erase queue management functions
477 ======================================================================*/
479 int pcmcia_register_erase_queue(client_handle_t *handle, eraseq_hdr_t *header,
480 eraseq_handle_t *e)
482 eraseq_t *queue;
484 if ((handle == NULL) || CHECK_HANDLE(*handle))
485 return CS_BAD_HANDLE;
486 queue = kmalloc(sizeof(*queue), GFP_KERNEL);
487 if (!queue) return CS_OUT_OF_RESOURCE;
488 queue->eraseq_magic = ERASEQ_MAGIC;
489 queue->handle = *handle;
490 queue->count = header->QueueEntryCnt;
491 queue->entry = header->QueueEntryArray;
492 *e = queue;
493 return CS_SUCCESS;
494 } /* register_erase_queue */
496 int pcmcia_deregister_erase_queue(eraseq_handle_t eraseq)
498 int i;
499 if (CHECK_ERASEQ(eraseq))
500 return CS_BAD_HANDLE;
501 for (i = 0; i < eraseq->count; i++)
502 if (ERASE_IN_PROGRESS(eraseq->entry[i].State)) break;
503 if (i < eraseq->count)
504 return CS_BUSY;
505 eraseq->eraseq_magic = 0;
506 kfree(eraseq);
507 return CS_SUCCESS;
508 } /* deregister_erase_queue */
510 int pcmcia_check_erase_queue(eraseq_handle_t eraseq)
512 int i;
513 if (CHECK_ERASEQ(eraseq))
514 return CS_BAD_HANDLE;
515 for (i = 0; i < eraseq->count; i++)
516 if (eraseq->entry[i].State == ERASE_QUEUED)
517 setup_erase_request(eraseq->handle, &eraseq->entry[i]);
518 return CS_SUCCESS;
519 } /* check_erase_queue */
521 /*======================================================================
523 Look up the memory region matching the request, and return a
524 memory handle.
526 ======================================================================*/
528 int pcmcia_open_memory(client_handle_t *handle, open_mem_t *open, memory_handle_t *mh)
530 socket_info_t *s;
531 memory_handle_t region;
533 if ((handle == NULL) || CHECK_HANDLE(*handle))
534 return CS_BAD_HANDLE;
535 s = SOCKET(*handle);
536 if (open->Attributes & MEMORY_TYPE_AM)
537 region = s->a_region;
538 else
539 region = s->c_region;
540 while (region) {
541 if (region->info.CardOffset == open->Offset) break;
542 region = region->info.next;
544 if (region && region->mtd) {
545 *mh = region;
546 DEBUG(1, "cs: open_memory(0x%p, 0x%x) = 0x%p\n",
547 handle, open->Offset, region);
548 return CS_SUCCESS;
549 } else
550 return CS_BAD_OFFSET;
551 } /* open_memory */
553 /*======================================================================
555 Close a memory handle from an earlier call to OpenMemory.
557 For the moment, I don't think this needs to do anything.
559 ======================================================================*/
561 int pcmcia_close_memory(memory_handle_t handle)
563 DEBUG(1, "cs: close_memory(0x%p)\n", handle);
564 if (CHECK_REGION(handle))
565 return CS_BAD_HANDLE;
566 return CS_SUCCESS;
567 } /* close_memory */
569 /*======================================================================
571 Read from a memory device, using a handle previously returned
572 by a call to OpenMemory.
574 ======================================================================*/
576 int pcmcia_read_memory(memory_handle_t handle, mem_op_t *req, caddr_t buf)
578 mtd_request_t mtd;
579 if (CHECK_REGION(handle))
580 return CS_BAD_HANDLE;
581 if (req->Offset >= handle->info.RegionSize)
582 return CS_BAD_OFFSET;
583 if (req->Offset+req->Count > handle->info.RegionSize)
584 return CS_BAD_SIZE;
586 mtd.SrcCardOffset = req->Offset + handle->info.CardOffset;
587 mtd.TransferLength = req->Count;
588 mtd.MediaID = handle->MediaID;
589 mtd.Function = MTD_REQ_READ;
590 if (req->Attributes & MEM_OP_BUFFER_KERNEL)
591 mtd.Function |= MTD_REQ_KERNEL;
592 return do_mtd_request(handle, &mtd, buf);
593 } /* read_memory */
595 /*======================================================================
597 Write to a memory device, using a handle previously returned by
598 a call to OpenMemory.
600 ======================================================================*/
602 int pcmcia_write_memory(memory_handle_t handle, mem_op_t *req, caddr_t buf)
604 mtd_request_t mtd;
605 if (CHECK_REGION(handle))
606 return CS_BAD_HANDLE;
607 if (req->Offset >= handle->info.RegionSize)
608 return CS_BAD_OFFSET;
609 if (req->Offset+req->Count > handle->info.RegionSize)
610 return CS_BAD_SIZE;
612 mtd.DestCardOffset = req->Offset + handle->info.CardOffset;
613 mtd.TransferLength = req->Count;
614 mtd.MediaID = handle->MediaID;
615 mtd.Function = MTD_REQ_WRITE;
616 if (req->Attributes & MEM_OP_BUFFER_KERNEL)
617 mtd.Function |= MTD_REQ_KERNEL;
618 return do_mtd_request(handle, &mtd, buf);
619 } /* write_memory */
621 /*======================================================================
623 This isn't needed for anything I could think of.
625 ======================================================================*/
627 int pcmcia_copy_memory(memory_handle_t handle, copy_op_t *req)
629 if (CHECK_REGION(handle))
630 return CS_BAD_HANDLE;
631 return CS_UNSUPPORTED_FUNCTION;