ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / pcmcia / bulkmem.c
1 /*======================================================================
2
3     PCMCIA Bulk Memory Services
4
5     bulkmem.c 1.38 2000/09/25 19:29:51
6
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/
11
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.
16
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.
20
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.
31     
32 ======================================================================*/
33
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>
42
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"
50
51 static void retry_erase_list(erase_busy_t *list, u_int cause);
52
53 /*======================================================================
54
55     This function handles submitting an MTD request, and retrying
56     requests when an MTD is busy.
57
58     An MTD request should never block.
59     
60 ======================================================================*/
61
62 static int do_mtd_request(memory_handle_t handle, mtd_request_t *req,
63                           caddr_t buf)
64 {
65     int ret, tries;
66     client_t *mtd;
67     struct pcmcia_socket *s;
68     
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;
92         }
93         if (signal_pending(current))
94             printk(KERN_NOTICE "cs: do_mtd_request interrupted!\n");
95     }
96     if (tries == 20) {
97         printk(KERN_NOTICE "cs: MTD request timed out!\n");
98         ret = CS_GENERAL_FAILURE;
99     }
100     wake_up_interruptible(&mtd->mtd_req);
101     retry_erase_list(&mtd->erase_busy, 0);
102     return ret;
103 } /* do_mtd_request */
104
105 /*======================================================================
106
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.
110
111 ======================================================================*/
112
113 static void insert_queue(struct pcmcia_socket *s, erase_busy_t *head, erase_busy_t *entry)
114 {
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;
120 }
121
122 static void remove_queue(struct pcmcia_socket *s, erase_busy_t *entry)
123 {
124     cs_dbg(s, 2, "unqueueing 0x%p\n", entry);
125     entry->next->prev = entry->prev;
126     entry->prev->next = entry->next;
127 }
128
129 static void retry_erase(erase_busy_t *busy, u_int cause)
130 {
131     eraseq_entry_t *erase = busy->erase;
132     mtd_request_t req;
133     client_t *mtd;
134     struct pcmcia_socket *s;
135     int ret;
136
137     mtd = erase->Handle->mtd;
138     s = SOCKET(mtd);
139
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;
162         }
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;
179         }
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);
186     }
187 } /* retry_erase */
188
189 static void retry_erase_list(erase_busy_t *list, u_int cause)
190 {
191     erase_busy_t tmp = *list;
192
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;
202
203     /* Now, retry each request, in order. */
204     while (tmp.next != &tmp)
205         retry_erase(tmp.next, cause);
206 } /* retry_erase_list */
207
208 static void handle_erase_timeout(u_long arg)
209 {
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);
213 }
214
215 static void setup_erase_request(client_handle_t handle, eraseq_entry_t *erase)
216 {
217     erase_busy_t *busy;
218     region_info_t *info;
219     
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;
236             }
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);
244         }
245     }
246 } /* setup_erase_request */
247
248 /*======================================================================
249
250     MTD helper functions
251
252 ======================================================================*/
253
254 static int mtd_modify_window(window_handle_t win, mtd_mod_win_t *req)
255 {
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;
267 }
268
269 static int mtd_set_vpp(client_handle_t handle, mtd_vpp_req_t *req)
270 {
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;
281 }
282
283 static int mtd_rdy_mask(client_handle_t handle, mtd_rdy_req_t *req)
284 {
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;
296 }
297
298 int MTDHelperEntry(int func, void *a1, void *a2)
299 {
300     switch (func) {
301     case MTDRequestWindow:
302     {
303         window_handle_t w;
304         int ret = pcmcia_request_window(a1, a2, &w);
305         a1 = w;
306         return  ret;
307     }
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;
319     }
320 } /* MTDHelperEntry */
321
322 /*======================================================================
323
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.
327     
328 ======================================================================*/
329
330 static void setup_regions(client_handle_t handle, int attr,
331                           memory_handle_t *list)
332 {
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;
339
340     cs_dbg(SOCKET(handle), 1, "setup_regions(0x%p, %d, 0x%p)\n",
341            handle, attr, list);
342
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;
351     }
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;
357     }
358     
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;
367             }
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;
389         }
390         offset += device.dev[i].size;
391     }
392 } /* setup_regions */
393
394 /*======================================================================
395
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.
400     
401 ======================================================================*/
402
403 static int match_region(client_handle_t handle, memory_handle_t list,
404                         region_info_t *match)
405 {
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;
411         }
412         list = list->info.next;
413     }
414     return CS_NO_MORE_ITEMS;
415 } /* match_region */
416
417 int pcmcia_get_first_region(client_handle_t handle, region_info_t *rgn)
418 {
419     struct pcmcia_socket *s = SOCKET(handle);
420     if (CHECK_HANDLE(handle))
421         return CS_BAD_HANDLE;
422     
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;
428     }
429
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 */
435
436 int pcmcia_get_next_region(client_handle_t handle, region_info_t *rgn)
437 {
438     if (CHECK_HANDLE(handle))
439         return CS_BAD_HANDLE;
440     return match_region(handle, rgn->next, rgn);
441 } /* get_next_region */
442
443 /*======================================================================
444
445     Connect an MTD with a memory region.
446     
447 ======================================================================*/
448
449 int pcmcia_register_mtd(client_handle_t handle, mtd_reg_t *reg)
450 {
451     memory_handle_t list;
452     struct pcmcia_socket *s;
453     
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;
466     }
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 */
477
478 /*======================================================================
479
480     Erase queue management functions
481     
482 ======================================================================*/
483
484 int pcmcia_register_erase_queue(client_handle_t *handle, eraseq_hdr_t *header,
485                                  eraseq_handle_t *e)
486 {
487     eraseq_t *queue;
488
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 */
500
501 int pcmcia_deregister_erase_queue(eraseq_handle_t eraseq)
502 {
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 */
514
515 int pcmcia_check_erase_queue(eraseq_handle_t eraseq)
516 {
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 */
525
526 /*======================================================================
527
528     Look up the memory region matching the request, and return a
529     memory handle.
530     
531 ======================================================================*/
532
533 int pcmcia_open_memory(client_handle_t *handle, open_mem_t *open, memory_handle_t *mh)
534 {
535     struct pcmcia_socket *s;
536     memory_handle_t region;
537     
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;
548     }
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 */
557
558 /*======================================================================
559
560     Close a memory handle from an earlier call to OpenMemory.
561     
562     For the moment, I don't think this needs to do anything.
563     
564 ======================================================================*/
565
566 int pcmcia_close_memory(memory_handle_t handle)
567 {
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 */
573
574 /*======================================================================
575
576     Read from a memory device, using a handle previously returned
577     by a call to OpenMemory.
578     
579 ======================================================================*/
580
581 int pcmcia_read_memory(memory_handle_t handle, mem_op_t *req, caddr_t buf)
582 {
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;
590     
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 */
599
600 /*======================================================================
601
602     Write to a memory device, using a handle previously returned by
603     a call to OpenMemory.
604     
605 ======================================================================*/
606
607 int pcmcia_write_memory(memory_handle_t handle, mem_op_t *req, caddr_t buf)
608 {
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;
616     
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 */
625
626 /*======================================================================
627
628     This isn't needed for anything I could think of.
629     
630 ======================================================================*/
631
632 int pcmcia_copy_memory(memory_handle_t handle, copy_op_t *req)
633 {
634     if (CHECK_REGION(handle))
635         return CS_BAD_HANDLE;
636     return CS_UNSUPPORTED_FUNCTION;
637 }
638