|
|
1.1 ! root 1: /*====================================================================== ! 2: ! 3: PCMCIA Bulk Memory Services ! 4: ! 5: bulkmem.c 1.44 2002/06/29 06:23:09 ! 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: <[email protected]>. 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 ! 23: which 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: #define __NO_VERSION__ ! 35: ! 36: #include <linux/module.h> ! 37: #include <linux/kernel.h> ! 38: #include <linux/string.h> ! 39: #include <linux/errno.h> ! 40: #include <linux/slab.h> ! 41: #include <linux/mm.h> ! 42: #include <linux/sched.h> ! 43: #include <linux/timer.h> ! 44: ! 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" ! 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: socket_info_t *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, ! 87: req->Timeout*HZ/1000); ! 88: req->Function |= MTD_REQ_TIMEOUT; ! 89: break; ! 90: case MTD_WAITPOWER: ! 91: interruptible_sleep_on(&mtd->mtd_req); ! 92: break; ! 93: } ! 94: if (signal_pending(current)) ! 95: printk(KERN_NOTICE "cs: do_mtd_request interrupted!\n"); ! 96: } ! 97: if (tries == 20) { ! 98: printk(KERN_NOTICE "cs: MTD request timed out!\n"); ! 99: ret = CS_GENERAL_FAILURE; ! 100: } ! 101: wake_up_interruptible(&mtd->mtd_req); ! 102: retry_erase_list(&mtd->erase_busy, 0); ! 103: return ret; ! 104: } /* do_mtd_request */ ! 105: ! 106: /*====================================================================== ! 107: ! 108: This stuff is all for handling asynchronous erase requests. It ! 109: is complicated because all the retry stuff has to be dealt with ! 110: in timer interrupts or in the card status event handler. ! 111: ! 112: ======================================================================*/ ! 113: ! 114: static void insert_queue(erase_busy_t *head, erase_busy_t *entry) ! 115: { ! 116: DEBUG(2, "cs: adding 0x%p to queue 0x%p\n", entry, head); ! 117: entry->next = head; ! 118: entry->prev = head->prev; ! 119: head->prev->next = entry; ! 120: head->prev = entry; ! 121: } ! 122: ! 123: static void remove_queue(erase_busy_t *entry) ! 124: { ! 125: DEBUG(2, "cs: unqueueing 0x%p\n", entry); ! 126: entry->next->prev = entry->prev; ! 127: entry->prev->next = entry->next; ! 128: } ! 129: ! 130: static void retry_erase(erase_busy_t *busy, u_int cause) ! 131: { ! 132: eraseq_entry_t *erase = busy->erase; ! 133: mtd_request_t req; ! 134: client_t *mtd; ! 135: socket_info_t *s; ! 136: int ret; ! 137: ! 138: DEBUG(2, "cs: trying erase request 0x%p...\n", busy); ! 139: if (busy->next) ! 140: remove_queue(busy); ! 141: req.Function = MTD_REQ_ERASE | cause; ! 142: req.TransferLength = erase->Size; ! 143: req.DestCardOffset = erase->Offset + erase->Handle->info.CardOffset; ! 144: req.MediaID = erase->Handle->MediaID; ! 145: mtd = erase->Handle->mtd; ! 146: s = SOCKET(mtd); ! 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: DEBUG(2, " Status = %d, requeueing.\n", req.Status); ! 151: switch (req.Status) { ! 152: case MTD_WAITREQ: ! 153: case MTD_WAITPOWER: ! 154: insert_queue(&mtd->erase_busy, busy); ! 155: break; ! 156: case MTD_WAITTIMER: ! 157: case MTD_WAITRDY: ! 158: if (req.Status == MTD_WAITRDY) ! 159: insert_queue(&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: 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; ! 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: void retry_erase_list(erase_busy_t *list, u_int cause) ! 190: { ! 191: erase_busy_t tmp = *list; ! 192: ! 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; ! 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: DEBUG(0, "cs: erase timeout for entry 0x%lx\n", arg); ! 211: retry_erase((erase_busy_t *)arg, MTD_REQ_TIMEOUT); ! 212: } ! 213: ! 214: static void setup_erase_request(client_handle_t handle, eraseq_entry_t *erase) ! 215: { ! 216: erase_busy_t *busy; ! 217: region_info_t *info; ! 218: ! 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); ! 239: } ! 240: } ! 241: } /* setup_erase_request */ ! 242: ! 243: /*====================================================================== ! 244: ! 245: MTD helper functions ! 246: ! 247: ======================================================================*/ ! 248: ! 249: static int mtd_modify_window(window_handle_t win, mtd_mod_win_t *req) ! 250: { ! 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(win->sock->sock, SS_SetMemMap, &win->ctl); ! 261: return CS_SUCCESS; ! 262: } ! 263: ! 264: static int mtd_set_vpp(client_handle_t handle, mtd_vpp_req_t *req) ! 265: { ! 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(s->sock, SS_SetSocket, &s->socket)) ! 274: return CS_BAD_VPP; ! 275: return CS_SUCCESS; ! 276: } ! 277: ! 278: static int mtd_rdy_mask(client_handle_t handle, mtd_rdy_req_t *req) ! 279: { ! 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(s->sock, SS_SetSocket, &s->socket)) ! 289: return CS_GENERAL_FAILURE; ! 290: return CS_SUCCESS; ! 291: } ! 292: ! 293: int MTDHelperEntry(int func, void *a1, void *a2) ! 294: { ! 295: switch (func) { ! 296: case MTDRequestWindow: ! 297: return CardServices(RequestWindow, a1, a2, NULL); ! 298: case MTDReleaseWindow: ! 299: return CardServices(ReleaseWindow, a1, NULL, NULL); ! 300: case MTDModifyWindow: ! 301: return mtd_modify_window(a1, a2); break; ! 302: case MTDSetVpp: ! 303: return mtd_set_vpp(a1, a2); break; ! 304: case MTDRDYMask: ! 305: return mtd_rdy_mask(a1, a2); break; ! 306: default: ! 307: return CS_UNSUPPORTED_FUNCTION; break; ! 308: } ! 309: } /* MTDHelperEntry */ ! 310: ! 311: /*====================================================================== ! 312: ! 313: This stuff is used by Card Services to initialize the table of ! 314: region info used for subsequent calls to GetFirstRegion and ! 315: GetNextRegion. ! 316: ! 317: ======================================================================*/ ! 318: ! 319: static void setup_regions(client_handle_t handle, int attr, ! 320: memory_handle_t *list) ! 321: { ! 322: int i, code, has_jedec, has_geo; ! 323: u_int offset; ! 324: cistpl_device_t device; ! 325: cistpl_jedec_t jedec; ! 326: cistpl_device_geo_t geo; ! 327: memory_handle_t r; ! 328: ! 329: DEBUG(1, "cs: setup_regions(0x%p, %d, 0x%p)\n", ! 330: handle, attr, list); ! 331: ! 332: code = (attr) ? CISTPL_DEVICE_A : CISTPL_DEVICE; ! 333: if (read_tuple(handle, code, &device) != CS_SUCCESS) ! 334: return; ! 335: code = (attr) ? CISTPL_JEDEC_A : CISTPL_JEDEC_C; ! 336: has_jedec = (read_tuple(handle, code, &jedec) == CS_SUCCESS); ! 337: if (has_jedec && (device.ndev != jedec.nid)) { ! 338: #ifdef PCMCIA_DEBUG ! 339: printk(KERN_DEBUG "cs: Device info does not match JEDEC info.\n"); ! 340: #endif ! 341: has_jedec = 0; ! 342: } ! 343: code = (attr) ? CISTPL_DEVICE_GEO_A : CISTPL_DEVICE_GEO; ! 344: has_geo = (read_tuple(handle, code, &geo) == CS_SUCCESS); ! 345: if (has_geo && (device.ndev != geo.ngeo)) { ! 346: #ifdef PCMCIA_DEBUG ! 347: printk(KERN_DEBUG "cs: Device info does not match geometry tuple.\n"); ! 348: #endif ! 349: has_geo = 0; ! 350: } ! 351: ! 352: offset = 0; ! 353: for (i = 0; i < device.ndev; i++) { ! 354: if ((device.dev[i].type != CISTPL_DTYPE_NULL) && ! 355: (device.dev[i].size != 0)) { ! 356: r = kmalloc(sizeof(*r), GFP_KERNEL); ! 357: r->region_magic = REGION_MAGIC; ! 358: r->state = 0; ! 359: r->dev_info[0] = '\0'; ! 360: r->mtd = NULL; ! 361: r->info.Attributes = (attr) ? REGION_TYPE_AM : 0; ! 362: r->info.CardOffset = offset; ! 363: r->info.RegionSize = device.dev[i].size; ! 364: r->info.AccessSpeed = device.dev[i].speed; ! 365: if (has_jedec) { ! 366: r->info.JedecMfr = jedec.id[i].mfr; ! 367: r->info.JedecInfo = jedec.id[i].info; ! 368: } else ! 369: r->info.JedecMfr = r->info.JedecInfo = 0; ! 370: if (has_geo) { ! 371: r->info.BlockSize = geo.geo[i].buswidth * ! 372: geo.geo[i].erase_block * geo.geo[i].interleave; ! 373: r->info.PartMultiple = ! 374: r->info.BlockSize * geo.geo[i].partition; ! 375: } else ! 376: r->info.BlockSize = r->info.PartMultiple = 1; ! 377: r->info.next = *list; *list = r; ! 378: } ! 379: offset += device.dev[i].size; ! 380: } ! 381: } /* setup_regions */ ! 382: ! 383: /*====================================================================== ! 384: ! 385: This is tricky. When get_first_region() is called by Driver ! 386: Services, we initialize the region info table in the socket ! 387: structure. When it is called by an MTD, we can just scan the ! 388: table for matching entries. ! 389: ! 390: ======================================================================*/ ! 391: ! 392: static int match_region(client_handle_t handle, memory_handle_t list, ! 393: region_info_t *match) ! 394: { ! 395: while (list != NULL) { ! 396: if (!(handle->Attributes & INFO_MTD_CLIENT) || ! 397: (strcmp(handle->dev_info, list->dev_info) == 0)) { ! 398: *match = list->info; ! 399: return CS_SUCCESS; ! 400: } ! 401: list = list->info.next; ! 402: } ! 403: return CS_NO_MORE_ITEMS; ! 404: } /* match_region */ ! 405: ! 406: int get_first_region(client_handle_t handle, region_info_t *rgn) ! 407: { ! 408: socket_info_t *s = SOCKET(handle); ! 409: if (CHECK_HANDLE(handle)) ! 410: return CS_BAD_HANDLE; ! 411: ! 412: if ((handle->Attributes & INFO_MASTER_CLIENT) && ! 413: (!(s->state & SOCKET_REGION_INFO))) { ! 414: setup_regions(handle, 0, &s->c_region); ! 415: setup_regions(handle, 1, &s->a_region); ! 416: s->state |= SOCKET_REGION_INFO; ! 417: } ! 418: ! 419: if (rgn->Attributes & REGION_TYPE_AM) ! 420: return match_region(handle, s->a_region, rgn); ! 421: else ! 422: return match_region(handle, s->c_region, rgn); ! 423: } /* get_first_region */ ! 424: ! 425: int get_next_region(client_handle_t handle, region_info_t *rgn) ! 426: { ! 427: if (CHECK_HANDLE(handle)) ! 428: return CS_BAD_HANDLE; ! 429: return match_region(handle, rgn->next, rgn); ! 430: } /* get_next_region */ ! 431: ! 432: /*====================================================================== ! 433: ! 434: Connect an MTD with a memory region. ! 435: ! 436: ======================================================================*/ ! 437: ! 438: int register_mtd(client_handle_t handle, mtd_reg_t *reg) ! 439: { ! 440: memory_handle_t list; ! 441: socket_info_t *s; ! 442: ! 443: if (CHECK_HANDLE(handle)) ! 444: return CS_BAD_HANDLE; ! 445: s = SOCKET(handle); ! 446: if (reg->Attributes & REGION_TYPE_AM) ! 447: list = s->a_region; ! 448: else ! 449: list = s->c_region; ! 450: DEBUG(1, "cs: register_mtd(0x%p, '%s', 0x%x)\n", ! 451: handle, handle->dev_info, reg->Offset); ! 452: while (list) { ! 453: if (list->info.CardOffset == reg->Offset) break; ! 454: list = list->info.next; ! 455: } ! 456: if (list && (list->mtd == NULL) && ! 457: (strcmp(handle->dev_info, list->dev_info) == 0)) { ! 458: list->info.Attributes = reg->Attributes; ! 459: list->MediaID = reg->MediaID; ! 460: list->mtd = handle; ! 461: handle->mtd_count++; ! 462: return CS_SUCCESS; ! 463: } else ! 464: return CS_BAD_OFFSET; ! 465: } /* register_mtd */ ! 466: ! 467: /*====================================================================== ! 468: ! 469: Erase queue management functions ! 470: ! 471: ======================================================================*/ ! 472: ! 473: int register_erase_queue(client_handle_t *handle, eraseq_hdr_t *header) ! 474: { ! 475: eraseq_t *queue; ! 476: ! 477: if ((handle == NULL) || CHECK_HANDLE(*handle)) ! 478: return CS_BAD_HANDLE; ! 479: queue = kmalloc(sizeof(*queue), GFP_KERNEL); ! 480: if (!queue) return CS_OUT_OF_RESOURCE; ! 481: queue->eraseq_magic = ERASEQ_MAGIC; ! 482: queue->handle = *handle; ! 483: queue->count = header->QueueEntryCnt; ! 484: queue->entry = header->QueueEntryArray; ! 485: *handle = (client_handle_t)queue; ! 486: return CS_SUCCESS; ! 487: } /* register_erase_queue */ ! 488: ! 489: int deregister_erase_queue(eraseq_handle_t eraseq) ! 490: { ! 491: int i; ! 492: if (CHECK_ERASEQ(eraseq)) ! 493: return CS_BAD_HANDLE; ! 494: for (i = 0; i < eraseq->count; i++) ! 495: if (ERASE_IN_PROGRESS(eraseq->entry[i].State)) break; ! 496: if (i < eraseq->count) ! 497: return CS_BUSY; ! 498: eraseq->eraseq_magic = 0; ! 499: kfree(eraseq); ! 500: return CS_SUCCESS; ! 501: } /* deregister_erase_queue */ ! 502: ! 503: int check_erase_queue(eraseq_handle_t eraseq) ! 504: { ! 505: int i; ! 506: if (CHECK_ERASEQ(eraseq)) ! 507: return CS_BAD_HANDLE; ! 508: for (i = 0; i < eraseq->count; i++) ! 509: if (eraseq->entry[i].State == ERASE_QUEUED) ! 510: setup_erase_request(eraseq->handle, &eraseq->entry[i]); ! 511: return CS_SUCCESS; ! 512: } /* check_erase_queue */ ! 513: ! 514: /*====================================================================== ! 515: ! 516: Look up the memory region matching the request, and return a ! 517: memory handle. ! 518: ! 519: ======================================================================*/ ! 520: ! 521: int open_memory(client_handle_t *handle, open_mem_t *open) ! 522: { ! 523: socket_info_t *s; ! 524: memory_handle_t region; ! 525: ! 526: if ((handle == NULL) || CHECK_HANDLE(*handle)) ! 527: return CS_BAD_HANDLE; ! 528: s = SOCKET(*handle); ! 529: if (open->Attributes & MEMORY_TYPE_AM) ! 530: region = s->a_region; ! 531: else ! 532: region = s->c_region; ! 533: while (region) { ! 534: if (region->info.CardOffset == open->Offset) break; ! 535: region = region->info.next; ! 536: } ! 537: if (region && region->mtd) { ! 538: *handle = (client_handle_t)region; ! 539: DEBUG(1, "cs: open_memory(0x%p, 0x%x) = 0x%p\n", ! 540: handle, open->Offset, region); ! 541: return CS_SUCCESS; ! 542: } else ! 543: return CS_BAD_OFFSET; ! 544: } /* open_memory */ ! 545: ! 546: /*====================================================================== ! 547: ! 548: Close a memory handle from an earlier call to OpenMemory. ! 549: ! 550: For the moment, I don't think this needs to do anything. ! 551: ! 552: ======================================================================*/ ! 553: ! 554: int close_memory(memory_handle_t handle) ! 555: { ! 556: DEBUG(1, "cs: close_memory(0x%p)\n", handle); ! 557: if (CHECK_REGION(handle)) ! 558: return CS_BAD_HANDLE; ! 559: return CS_SUCCESS; ! 560: } /* close_memory */ ! 561: ! 562: /*====================================================================== ! 563: ! 564: Read from a memory device, using a handle previously returned ! 565: by a call to OpenMemory. ! 566: ! 567: ======================================================================*/ ! 568: ! 569: int read_memory(memory_handle_t handle, mem_op_t *req, caddr_t buf) ! 570: { ! 571: mtd_request_t mtd; ! 572: if (CHECK_REGION(handle)) ! 573: return CS_BAD_HANDLE; ! 574: if (req->Offset >= handle->info.RegionSize) ! 575: return CS_BAD_OFFSET; ! 576: if (req->Offset+req->Count > handle->info.RegionSize) ! 577: return CS_BAD_SIZE; ! 578: ! 579: mtd.SrcCardOffset = req->Offset + handle->info.CardOffset; ! 580: mtd.TransferLength = req->Count; ! 581: mtd.MediaID = handle->MediaID; ! 582: mtd.Function = MTD_REQ_READ; ! 583: if (req->Attributes & MEM_OP_BUFFER_KERNEL) ! 584: mtd.Function |= MTD_REQ_KERNEL; ! 585: return do_mtd_request(handle, &mtd, buf); ! 586: } /* read_memory */ ! 587: ! 588: /*====================================================================== ! 589: ! 590: Write to a memory device, using a handle previously returned by ! 591: a call to OpenMemory. ! 592: ! 593: ======================================================================*/ ! 594: ! 595: int write_memory(memory_handle_t handle, mem_op_t *req, caddr_t buf) ! 596: { ! 597: mtd_request_t mtd; ! 598: if (CHECK_REGION(handle)) ! 599: return CS_BAD_HANDLE; ! 600: if (req->Offset >= handle->info.RegionSize) ! 601: return CS_BAD_OFFSET; ! 602: if (req->Offset+req->Count > handle->info.RegionSize) ! 603: return CS_BAD_SIZE; ! 604: ! 605: mtd.DestCardOffset = req->Offset + handle->info.CardOffset; ! 606: mtd.TransferLength = req->Count; ! 607: mtd.MediaID = handle->MediaID; ! 608: mtd.Function = MTD_REQ_WRITE; ! 609: if (req->Attributes & MEM_OP_BUFFER_KERNEL) ! 610: mtd.Function |= MTD_REQ_KERNEL; ! 611: return do_mtd_request(handle, &mtd, buf); ! 612: } /* write_memory */ ! 613: ! 614: /*====================================================================== ! 615: ! 616: This isn't needed for anything I could think of. ! 617: ! 618: ======================================================================*/ ! 619: ! 620: int copy_memory(memory_handle_t handle, copy_op_t *req) ! 621: { ! 622: if (CHECK_REGION(handle)) ! 623: return CS_BAD_HANDLE; ! 624: return CS_UNSUPPORTED_FUNCTION; ! 625: } ! 626:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.