MOXA linux-2.6.x / linux-2.6.9-uc0 from sdlinux-moxaart.tgz
[linux-2.6.9-moxart.git] / drivers / pcmcia / bulkmem.c
blob3298828e28905bed2e16861b3cdd81173ab2856a
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 static void retry_erase_list(erase_busy_t *list, u_int cause);
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 struct pcmcia_socket *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(struct pcmcia_socket *s, erase_busy_t *head, erase_busy_t *entry)
115 cs_dbg(s, 2, "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(struct pcmcia_socket *s, erase_busy_t *entry)
124 cs_dbg(s, 2, "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 struct pcmcia_socket *s;
135 int ret;
137 mtd = erase->Handle->mtd;
138 s = SOCKET(mtd);
140 cs_dbg(s, 2, "trying erase request 0x%p...\n", busy);
141 if (busy->next)
142 remove_queue(s, busy);
143 req.Function = MTD_REQ_ERASE | cause;
144 req.TransferLength = erase->Size;
145 req.DestCardOffset = erase->Offset + erase->Handle->info.CardOffset;
146 req.MediaID = erase->Handle->MediaID;
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 cs_dbg(s, 2, " Status = %d, requeueing.\n", req.Status);
151 switch (req.Status) {
152 case MTD_WAITREQ:
153 case MTD_WAITPOWER:
154 insert_queue(s, &mtd->erase_busy, busy);
155 break;
156 case MTD_WAITTIMER:
157 case MTD_WAITRDY:
158 if (req.Status == MTD_WAITRDY)
159 insert_queue(s, &s->erase_busy, busy);
160 mod_timer(&busy->timeout, jiffies + req.Timeout*HZ/1000);
161 break;
163 } else {
164 /* update erase queue status */
165 cs_dbg(s, 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 static void retry_erase_list(erase_busy_t *list, u_int cause)
191 erase_busy_t tmp = *list;
193 cs_dbg(SOCKET(list->client), 2, "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 erase_busy_t *busy = (erase_busy_t *)arg;
211 cs_dbg(SOCKET(busy->client), 0, "erase timeout for entry 0x%lx\n", arg);
212 retry_erase(busy, MTD_REQ_TIMEOUT);
215 static void setup_erase_request(client_handle_t handle, eraseq_entry_t *erase)
217 erase_busy_t *busy;
218 region_info_t *info;
220 if (CHECK_REGION(erase->Handle))
221 erase->State = ERASE_BAD_SOCKET;
222 else {
223 info = &erase->Handle->info;
224 if ((erase->Offset >= info->RegionSize) ||
225 (erase->Offset & (info->BlockSize-1)))
226 erase->State = ERASE_BAD_OFFSET;
227 else if ((erase->Offset+erase->Size > info->RegionSize) ||
228 (erase->Size & (info->BlockSize-1)))
229 erase->State = ERASE_BAD_SIZE;
230 else {
231 erase->State = 1;
232 busy = kmalloc(sizeof(erase_busy_t), GFP_KERNEL);
233 if (!busy) {
234 erase->State = ERASE_FAILED;
235 return;
237 busy->erase = erase;
238 busy->client = handle;
239 init_timer(&busy->timeout);
240 busy->timeout.data = (u_long)busy;
241 busy->timeout.function = &handle_erase_timeout;
242 busy->prev = busy->next = NULL;
243 retry_erase(busy, 0);
246 } /* setup_erase_request */
248 /*======================================================================
250 MTD helper functions
252 ======================================================================*/
254 static int mtd_modify_window(window_handle_t win, mtd_mod_win_t *req)
256 if ((win == NULL) || (win->magic != WINDOW_MAGIC))
257 return CS_BAD_HANDLE;
258 win->ctl.flags = MAP_16BIT | MAP_ACTIVE;
259 if (req->Attributes & WIN_USE_WAIT)
260 win->ctl.flags |= MAP_USE_WAIT;
261 if (req->Attributes & WIN_MEMORY_TYPE)
262 win->ctl.flags |= MAP_ATTRIB;
263 win->ctl.speed = req->AccessSpeed;
264 win->ctl.card_start = req->CardOffset;
265 win->sock->ops->set_mem_map(win->sock, &win->ctl);
266 return CS_SUCCESS;
269 static int mtd_set_vpp(client_handle_t handle, mtd_vpp_req_t *req)
271 struct pcmcia_socket *s;
272 if (CHECK_HANDLE(handle))
273 return CS_BAD_HANDLE;
274 if (req->Vpp1 != req->Vpp2)
275 return CS_BAD_VPP;
276 s = SOCKET(handle);
277 s->socket.Vpp = req->Vpp1;
278 if (s->ops->set_socket(s, &s->socket))
279 return CS_BAD_VPP;
280 return CS_SUCCESS;
283 static int mtd_rdy_mask(client_handle_t handle, mtd_rdy_req_t *req)
285 struct pcmcia_socket *s;
286 if (CHECK_HANDLE(handle))
287 return CS_BAD_HANDLE;
288 s = SOCKET(handle);
289 if (req->Mask & CS_EVENT_READY_CHANGE)
290 s->socket.csc_mask |= SS_READY;
291 else
292 s->socket.csc_mask &= ~SS_READY;
293 if (s->ops->set_socket(s, &s->socket))
294 return CS_GENERAL_FAILURE;
295 return CS_SUCCESS;
298 int MTDHelperEntry(int func, void *a1, void *a2)
300 switch (func) {
301 case MTDRequestWindow:
303 window_handle_t w;
304 int ret = pcmcia_request_window(a1, a2, &w);
305 a1 = w;
306 return ret;
308 break;
309 case MTDReleaseWindow:
310 return pcmcia_release_window(a1);
311 case MTDModifyWindow:
312 return mtd_modify_window(a1, a2); break;
313 case MTDSetVpp:
314 return mtd_set_vpp(a1, a2); break;
315 case MTDRDYMask:
316 return mtd_rdy_mask(a1, a2); break;
317 default:
318 return CS_UNSUPPORTED_FUNCTION; break;
320 } /* MTDHelperEntry */
322 /*======================================================================
324 This stuff is used by Card Services to initialize the table of
325 region info used for subsequent calls to GetFirstRegion and
326 GetNextRegion.
328 ======================================================================*/
330 static void setup_regions(client_handle_t handle, int attr,
331 memory_handle_t *list)
333 int i, code, has_jedec, has_geo;
334 u_int offset;
335 cistpl_device_t device;
336 cistpl_jedec_t jedec;
337 cistpl_device_geo_t geo;
338 memory_handle_t r;
340 cs_dbg(SOCKET(handle), 1, "setup_regions(0x%p, %d, 0x%p)\n",
341 handle, attr, list);
343 code = (attr) ? CISTPL_DEVICE_A : CISTPL_DEVICE;
344 if (read_tuple(handle, code, &device) != CS_SUCCESS)
345 return;
346 code = (attr) ? CISTPL_JEDEC_A : CISTPL_JEDEC_C;
347 has_jedec = (read_tuple(handle, code, &jedec) == CS_SUCCESS);
348 if (has_jedec && (device.ndev != jedec.nid)) {
349 cs_dbg(SOCKET(handle), 0, "Device info does not match JEDEC info.\n");
350 has_jedec = 0;
352 code = (attr) ? CISTPL_DEVICE_GEO_A : CISTPL_DEVICE_GEO;
353 has_geo = (read_tuple(handle, code, &geo) == CS_SUCCESS);
354 if (has_geo && (device.ndev != geo.ngeo)) {
355 cs_dbg(SOCKET(handle), 0, "Device info does not match geometry tuple.\n");
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 cs_dbg(s, 1, "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 cs_dbg(s, 1, "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 cs_dbg(SOCKET(handle->mtd), 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;