📄 blkmtd.c
字号:
} numregions--; einfo++; } if(!numregions) { /* Not a valid erase block */ err("erase: invalid erase request 0x%lX @ 0x%08zX", len, from); instr->state = MTD_ERASE_FAILED; err = -EIO; } if(instr->state != MTD_ERASE_FAILED) { /* do the erase */ DEBUG(3, "Doing erase from = %zd len = %ld\n", from, len); err = write_pages(dev, NULL, from, len, &retlen); if(err || retlen != len) { err("erase failed err = %d", err); instr->state = MTD_ERASE_FAILED; } else { instr->state = MTD_ERASE_DONE; } } DEBUG(3, "blkmtd: erase: checking callback\n"); mtd_erase_callback(instr); DEBUG(2, "blkmtd: erase: finished (err = %d)\n", err); return err;}/* read a range of the data via the page cache */static int blkmtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf){ struct blkmtd_dev *dev = mtd->priv; int err = 0; int offset; int pagenr, pages; size_t thislen = 0; DEBUG(2, "blkmtd: read: dev = `%s' from = %lld len = %zd buf = %p\n", mtd->name+9, from, len, buf); if(from > mtd->size) return -EINVAL; if(from + len > mtd->size) len = mtd->size - from; pagenr = from >> PAGE_SHIFT; offset = from - (pagenr << PAGE_SHIFT); pages = (offset+len+PAGE_SIZE-1) >> PAGE_SHIFT; DEBUG(3, "blkmtd: read: pagenr = %d offset = %d, pages = %d\n", pagenr, offset, pages); while(pages) { struct page *page; int cpylen; DEBUG(3, "blkmtd: read: looking for page: %d\n", pagenr); page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev); if(IS_ERR(page)) { err = -EIO; goto readerr; } cpylen = (PAGE_SIZE > len) ? len : PAGE_SIZE; if(offset+cpylen > PAGE_SIZE) cpylen = PAGE_SIZE-offset; memcpy(buf + thislen, page_address(page) + offset, cpylen); offset = 0; len -= cpylen; thislen += cpylen; pagenr++; pages--; if(!PageDirty(page)) page_cache_release(page); } readerr: if(retlen) *retlen = thislen; DEBUG(2, "blkmtd: end read: retlen = %zd, err = %d\n", thislen, err); return err;}/* write data to the underlying device */static int blkmtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf){ struct blkmtd_dev *dev = mtd->priv; int err; if(!len) return 0; DEBUG(2, "blkmtd: write: dev = `%s' to = %lld len = %zd buf = %p\n", mtd->name+9, to, len, buf); if(to >= mtd->size) { return -ENOSPC; } if(to + len > mtd->size) { len = mtd->size - to; } err = write_pages(dev, buf, to, len, retlen); if(err > 0) err = 0; DEBUG(2, "blkmtd: write: end, err = %d\n", err); return err;}/* sync the device - wait until the write queue is empty */static void blkmtd_sync(struct mtd_info *mtd){ /* Currently all writes are synchronous */}static void free_device(struct blkmtd_dev *dev){ DEBUG(2, "blkmtd: free_device() dev = %p\n", dev); if(dev) { if(dev->mtd_info.eraseregions) kfree(dev->mtd_info.eraseregions); if(dev->mtd_info.name) kfree(dev->mtd_info.name); if(dev->blkdev) { invalidate_inode_pages(dev->blkdev->bd_inode->i_mapping); close_bdev_excl(dev->blkdev); } kfree(dev); }}/* For a given size and initial erase size, calculate the number * and size of each erase region. Goes round the loop twice, * once to find out how many regions, then allocates space, * then round the loop again to fill it in. */static struct mtd_erase_region_info *calc_erase_regions( size_t erase_size, size_t total_size, int *regions){ struct mtd_erase_region_info *info = NULL; DEBUG(2, "calc_erase_regions, es = %zd size = %zd regions = %d\n", erase_size, total_size, *regions); /* Make any user specified erasesize be a power of 2 and at least PAGE_SIZE */ if(erase_size) { int es = erase_size; erase_size = 1; while(es != 1) { es >>= 1; erase_size <<= 1; } if(erase_size < PAGE_SIZE) erase_size = PAGE_SIZE; } else { erase_size = CONFIG_MTD_BLKDEV_ERASESIZE; } *regions = 0; do { int tot_size = total_size; int er_size = erase_size; int count = 0, offset = 0, regcnt = 0; while(tot_size) { count = tot_size / er_size; if(count) { tot_size = tot_size % er_size; if(info) { DEBUG(2, "adding to erase info off=%d er=%d cnt=%d\n", offset, er_size, count); (info+regcnt)->offset = offset; (info+regcnt)->erasesize = er_size; (info+regcnt)->numblocks = count; (*regions)++; } regcnt++; offset += (count * er_size); } while(er_size > tot_size) er_size >>= 1; } if(info == NULL) { info = kmalloc(regcnt * sizeof(struct mtd_erase_region_info), GFP_KERNEL); if(!info) break; } } while(!(*regions)); DEBUG(2, "calc_erase_regions done, es = %zd size = %zd regions = %d\n", erase_size, total_size, *regions); return info;}extern dev_t __init name_to_dev_t(const char *line);static struct blkmtd_dev *add_device(char *devname, int readonly, int erase_size){ struct block_device *bdev; int mode; struct blkmtd_dev *dev; if(!devname) return NULL; /* Get a handle on the device */#ifdef MODULE mode = (readonly) ? O_RDONLY : O_RDWR; bdev = open_bdev_excl(devname, mode, NULL);#else mode = (readonly) ? FMODE_READ : FMODE_WRITE; bdev = open_by_devnum(name_to_dev_t(devname), mode);#endif if(IS_ERR(bdev)) { err("error: cannot open device %s", devname); DEBUG(2, "blkmtd: opening bdev returned %ld\n", PTR_ERR(bdev)); return NULL; } DEBUG(1, "blkmtd: found a block device major = %d, minor = %d\n", MAJOR(bdev->bd_dev), MINOR(bdev->bd_dev)); if(MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) { err("attempting to use an MTD device as a block device"); blkdev_put(bdev); return NULL; } dev = kmalloc(sizeof(struct blkmtd_dev), GFP_KERNEL); if(dev == NULL) { blkdev_put(bdev); return NULL; } memset(dev, 0, sizeof(struct blkmtd_dev)); dev->blkdev = bdev; if(!readonly) { init_MUTEX(&dev->wrbuf_mutex); } dev->mtd_info.size = dev->blkdev->bd_inode->i_size & PAGE_MASK; /* Setup the MTD structure */ /* make the name contain the block device in */ dev->mtd_info.name = kmalloc(sizeof("blkmtd: ") + strlen(devname), GFP_KERNEL); if(dev->mtd_info.name == NULL) goto devinit_err; sprintf(dev->mtd_info.name, "blkmtd: %s", devname); dev->mtd_info.eraseregions = calc_erase_regions(erase_size, dev->mtd_info.size, &dev->mtd_info.numeraseregions); if(dev->mtd_info.eraseregions == NULL) goto devinit_err; dev->mtd_info.erasesize = dev->mtd_info.eraseregions->erasesize; DEBUG(1, "blkmtd: init: found %d erase regions\n", dev->mtd_info.numeraseregions); if(readonly) { dev->mtd_info.type = MTD_ROM; dev->mtd_info.flags = MTD_CAP_ROM; } else { dev->mtd_info.type = MTD_RAM; dev->mtd_info.flags = MTD_CAP_RAM; dev->mtd_info.erase = blkmtd_erase; dev->mtd_info.write = blkmtd_write; dev->mtd_info.writev = default_mtd_writev; dev->mtd_info.sync = blkmtd_sync; } dev->mtd_info.read = blkmtd_read; dev->mtd_info.readv = default_mtd_readv; dev->mtd_info.priv = dev; dev->mtd_info.owner = THIS_MODULE; list_add(&dev->list, &blkmtd_device_list); if (add_mtd_device(&dev->mtd_info)) { /* Device didnt get added, so free the entry */ list_del(&dev->list); goto devinit_err; } else { info("mtd%d: [%s] erase_size = %dKiB %s", dev->mtd_info.index, dev->mtd_info.name + strlen("blkmtd: "), dev->mtd_info.erasesize >> 10, readonly ? "(read-only)" : ""); } return dev; devinit_err: free_device(dev); return NULL;}/* Cleanup and exit - sync the device and kill of the kernel thread */static void __devexit cleanup_blkmtd(void){ struct list_head *temp1, *temp2; /* Remove the MTD devices */ list_for_each_safe(temp1, temp2, &blkmtd_device_list) { struct blkmtd_dev *dev = list_entry(temp1, struct blkmtd_dev, list); blkmtd_sync(&dev->mtd_info); del_mtd_device(&dev->mtd_info); info("mtd%d: [%s] removed", dev->mtd_info.index, dev->mtd_info.name + strlen("blkmtd: ")); list_del(&dev->list); free_device(dev); }}#ifndef MODULE/* Handle kernel boot params */static int __init param_blkmtd_device(char *str){ int i; for(i = 0; i < MAX_DEVICES; i++) { device[i] = str; DEBUG(2, "blkmtd: device setup: %d = %s\n", i, device[i]); strsep(&str, ","); } return 1;}static int __init param_blkmtd_erasesz(char *str){ int i; for(i = 0; i < MAX_DEVICES; i++) { char *val = strsep(&str, ","); if(val) erasesz[i] = simple_strtoul(val, NULL, 0); DEBUG(2, "blkmtd: erasesz setup: %d = %d\n", i, erasesz[i]); } return 1;}static int __init param_blkmtd_ro(char *str){ int i; for(i = 0; i < MAX_DEVICES; i++) { char *val = strsep(&str, ","); if(val) ro[i] = simple_strtoul(val, NULL, 0); DEBUG(2, "blkmtd: ro setup: %d = %d\n", i, ro[i]); } return 1;}static int __init param_blkmtd_sync(char *str){ if(str[0] == '1') sync = 1; return 1;}__setup("blkmtd_device=", param_blkmtd_device);__setup("blkmtd_erasesz=", param_blkmtd_erasesz);__setup("blkmtd_ro=", param_blkmtd_ro);__setup("blkmtd_sync=", param_blkmtd_sync);#endif/* Startup */static int __init init_blkmtd(void){ int i; info("version " VERSION); /* Check args - device[0] is the bare minimum*/ if(!device[0]) { err("error: missing `device' name\n"); return -EINVAL; } for(i = 0; i < MAX_DEVICES; i++) add_device(device[i], ro[i], erasesz[i] << 10); if(list_empty(&blkmtd_device_list)) return -EINVAL; return 0;}module_init(init_blkmtd);module_exit(cleanup_blkmtd);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -