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>
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
,
71 return CS_GENERAL_FAILURE
;
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
);
79 switch (req
->Status
) {
81 /* Not that we should ever need this... */
82 interruptible_sleep_on_timeout(&mtd
->mtd_req
, HZ
);
86 interruptible_sleep_on_timeout(&mtd
->mtd_req
, req
->Timeout
*HZ
/1000);
87 req
->Function
|= MTD_REQ_TIMEOUT
;
90 interruptible_sleep_on(&mtd
->mtd_req
);
93 if (signal_pending(current
))
94 printk(KERN_NOTICE
"cs: do_mtd_request interrupted!\n");
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);
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
);
117 entry
->prev
= head
->prev
;
118 head
->prev
->next
= 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
;
137 DEBUG(2, "cs: trying erase request 0x%p...\n", 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
;
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
) {
153 insert_queue(&mtd
->erase_busy
, busy
);
157 if (req
.Status
== MTD_WAITRDY
)
158 insert_queue(&s
->erase_busy
, busy
);
159 mod_timer(&busy
->timeout
, jiffies
+ req
.Timeout
*HZ
/1000);
163 /* update erase queue status */
164 DEBUG(2, " Ret = %d\n", ret
);
167 erase
->State
= ERASE_PASSED
; break;
168 case CS_WRITE_PROTECTED
:
169 erase
->State
= ERASE_MEDIA_WRPROT
; break;
171 erase
->State
= ERASE_BAD_OFFSET
; break;
173 erase
->State
= ERASE_BAD_SIZE
; break;
175 erase
->State
= ERASE_BAD_SOCKET
; break;
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
);
182 /* Resubmit anything waiting for a request to finish */
183 wake_up_interruptible(&mtd
->mtd_req
);
184 retry_erase_list(&mtd
->erase_busy
, 0);
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
)
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
)
218 if (CHECK_REGION(erase
->Handle
))
219 erase
->State
= ERASE_BAD_SOCKET
;
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
;
230 busy
= kmalloc(sizeof(erase_busy_t
), GFP_KERNEL
);
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 /*======================================================================
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
);
263 static int mtd_set_vpp(client_handle_t handle
, mtd_vpp_req_t
*req
)
266 if (CHECK_HANDLE(handle
))
267 return CS_BAD_HANDLE
;
268 if (req
->Vpp1
!= req
->Vpp2
)
271 s
->socket
.Vpp
= req
->Vpp1
;
272 if (s
->ss_entry
->set_socket(s
->sock
, &s
->socket
))
277 static int mtd_rdy_mask(client_handle_t handle
, mtd_rdy_req_t
*req
)
280 if (CHECK_HANDLE(handle
))
281 return CS_BAD_HANDLE
;
283 if (req
->Mask
& CS_EVENT_READY_CHANGE
)
284 s
->socket
.csc_mask
|= SS_READY
;
286 s
->socket
.csc_mask
&= ~SS_READY
;
287 if (s
->ss_entry
->set_socket(s
->sock
, &s
->socket
))
288 return CS_GENERAL_FAILURE
;
292 int MTDHelperEntry(int func
, void *a1
, void *a2
)
295 case MTDRequestWindow
:
298 int ret
= pcmcia_request_window(a1
, a2
, &w
);
299 (window_handle_t
*)a1
= w
;
303 case MTDReleaseWindow
:
304 return pcmcia_release_window(a1
);
305 case MTDModifyWindow
:
306 return mtd_modify_window(a1
, a2
); break;
308 return mtd_set_vpp(a1
, a2
); break;
310 return mtd_rdy_mask(a1
, a2
); break;
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
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
;
329 cistpl_device_t device
;
330 cistpl_jedec_t jedec
;
331 cistpl_device_geo_t geo
;
334 DEBUG(1, "cs: setup_regions(0x%p, %d, 0x%p)\n",
337 code
= (attr
) ? CISTPL_DEVICE_A
: CISTPL_DEVICE
;
338 if (read_tuple(handle
, code
, &device
) != CS_SUCCESS
)
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
)) {
344 printk(KERN_DEBUG
"cs: Device info does not match JEDEC info.\n");
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
)) {
352 printk(KERN_DEBUG
"cs: Device info does not match geometry tuple.\n");
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
;
364 r
->dev_info
[0] = '\0';
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
;
371 r
->info
.JedecMfr
= jedec
.id
[i
].mfr
;
372 r
->info
.JedecInfo
= jedec
.id
[i
].info
;
374 r
->info
.JedecMfr
= r
->info
.JedecInfo
= 0;
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
;
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)) {
406 list
= list
->info
.next
;
408 return CS_NO_MORE_ITEMS
;
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
);
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
;
448 if (CHECK_HANDLE(handle
))
449 return CS_BAD_HANDLE
;
451 if (reg
->Attributes
& REGION_TYPE_AM
)
455 DEBUG(1, "cs: register_mtd(0x%p, '%s', 0x%x)\n",
456 handle
, handle
->dev_info
, reg
->Offset
);
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
;
469 return CS_BAD_OFFSET
;
472 /*======================================================================
474 Erase queue management functions
476 ======================================================================*/
478 int pcmcia_register_erase_queue(client_handle_t
*handle
, eraseq_hdr_t
*header
,
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
;
493 } /* register_erase_queue */
495 int pcmcia_deregister_erase_queue(eraseq_handle_t eraseq
)
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
)
504 eraseq
->eraseq_magic
= 0;
507 } /* deregister_erase_queue */
509 int pcmcia_check_erase_queue(eraseq_handle_t eraseq
)
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
]);
518 } /* check_erase_queue */
520 /*======================================================================
522 Look up the memory region matching the request, and return a
525 ======================================================================*/
527 int pcmcia_open_memory(client_handle_t
*handle
, open_mem_t
*open
, memory_handle_t
*mh
)
530 memory_handle_t region
;
532 if ((handle
== NULL
) || CHECK_HANDLE(*handle
))
533 return CS_BAD_HANDLE
;
535 if (open
->Attributes
& MEMORY_TYPE_AM
)
536 region
= s
->a_region
;
538 region
= s
->c_region
;
540 if (region
->info
.CardOffset
== open
->Offset
) break;
541 region
= region
->info
.next
;
543 if (region
&& region
->mtd
) {
545 DEBUG(1, "cs: open_memory(0x%p, 0x%x) = 0x%p\n",
546 handle
, open
->Offset
, region
);
549 return CS_BAD_OFFSET
;
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
;
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
)
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
)
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
);
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
)
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
)
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
);
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
;