📄 ftl_cs.c
字号:
MOD_INC_USE_COUNT; DEBUG(0, "ftl_cs: ftl_open(%d)\n", minor); link = dev_table[DEVICE_NR(minor)]; if (!DEV_OK(link)) goto failed; dev = (ftl_dev_t *)link->priv; partition = &dev->minor[REGION_NR(minor)]; if (partition->region.RegionSize == 0) goto failed; while (partition->locked) sleep_on(&ftl_wait_open); if (partition->handle == NULL) { partition->handle = (memory_handle_t)link->handle; open.Attributes = partition->region.Attributes; open.Offset = partition->region.CardOffset; ret = CardServices(OpenMemory, &partition->handle, &open); if (ret != CS_SUCCESS) { cs_error(OpenMemory, ret); goto failed; } if ((scan_header(partition) == 0) && (build_maps(partition) == 0)) { partition->state = FTL_FORMATTED; ftl_reread_partitions(minor);#ifdef PCMCIA_DEBUG printk(KERN_INFO "ftl_cs: opening %d kb FTL partition\n", partition->header.FormattedSize >> 10);#endif } else { CardServices(CloseMemory, partition->handle); partition->handle = NULL; printk(KERN_NOTICE "ftl_cs: FTL partition is invalid.\n"); goto failed; } } partition->open++; link->open++; return 0;failed: MOD_DEC_USE_COUNT; return -ENODEV;} /* ftl_open *//*====================================================================*/static FS_RELEASE_T ftl_close(struct inode *inode, struct file *file){ dev_link_t *link; int minor = MINOR(inode->i_rdev); ftl_dev_t *dev; partition_t *part; int i; DEBUG(0, "ftl_cs: ftl_close(%d)\n", minor); /* Flush all writes */ fsync_dev(inode->i_rdev); INVALIDATE_INODES(inode->i_rdev); invalidate_buffers(inode->i_rdev); link = dev_table[DEVICE_NR(minor)]; dev = (ftl_dev_t *)link->priv; part = &dev->minor[REGION_NR(minor)]; /* Wait for any pending erase operations to complete */ for (i = 0; i < part->header.NumTransferUnits; i++) { if (part->XferInfo[i].state == XFER_ERASING) sleep_on(&dev->erase_pending); if (part->XferInfo[i].state == XFER_ERASED) prepare_xfer(part, i); } link->open--; part->open--; if (part->open == 0) { CardServices(CloseMemory, part->handle); part->handle = NULL; if (part->VirtualBlockMap) { vfree(part->VirtualBlockMap); part->VirtualBlockMap = NULL; } if (part->VirtualPageMap) { kfree(part->VirtualPageMap); part->VirtualPageMap = NULL; } if (part->EUNInfo) { kfree(part->EUNInfo); part->EUNInfo = NULL; } if (part->XferInfo) { kfree(part->XferInfo); part->XferInfo = NULL; } if (part->bam_cache) { kfree(part->bam_cache); part->bam_cache = NULL; } } MOD_DEC_USE_COUNT; return (FS_RELEASE_T)0;} /* ftl_close *//*====================================================================== Read a series of sectors from an FTL partition. ======================================================================*/static int ftl_read(partition_t *part, caddr_t buffer, u_long sector, u_long nblocks){ mem_op_t req; u_int log_addr, bsize; u_long i; int ret; DEBUG(2, "ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n", part->handle, sector, nblocks); if (!(part->state & FTL_FORMATTED)) { printk(KERN_NOTICE "ftl_cs: bad partition\n"); return -EIO; } bsize = part->region.BlockSize; req.Attributes = MEM_OP_BUFFER_KERNEL; req.Count = SECTOR_SIZE; for (i = 0; i < nblocks; i++) { if (((sector+i) * SECTOR_SIZE) >= part->header.FormattedSize) { printk(KERN_NOTICE "ftl_cs: bad read offset\n"); return -EIO; } log_addr = part->VirtualBlockMap[sector+i]; if (log_addr == 0xffffffff) memset(buffer, 0, SECTOR_SIZE); else { req.Offset = (part->EUNInfo[log_addr / bsize].Offset + (log_addr % bsize)); ret = CardServices(ReadMemory, part->handle, &req, buffer); if (ret != CS_SUCCESS) { cs_error(ReadMemory, ret); return -EIO; } } buffer += SECTOR_SIZE; } return 0;} /* ftl_read *//*====================================================================== Write a series of sectors to an FTL partition ======================================================================*/static int set_bam_entry(partition_t *part, u_int log_addr, u_int virt_addr){ mem_op_t req; u_int bsize, blk;#ifdef PSYCHO_DEBUG u_int old_addr;#endif u_short eun; int ret; DEBUG(2, "ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n", part->handle, log_addr, virt_addr); bsize = part->region.BlockSize; eun = log_addr / bsize; blk = (log_addr % bsize) / SECTOR_SIZE; req.Attributes = MEM_OP_BUFFER_KERNEL; req.Count = sizeof(u_int); req.Offset = (part->EUNInfo[eun].Offset + blk * sizeof(u_int) + part->header.BAMOffset); #ifdef PSYCHO_DEBUG CardServices(ReadMemory, part->handle, &req, &old_addr); if (((virt_addr == 0xfffffffe) && !BLOCK_FREE(old_addr)) || ((virt_addr == 0) && (BLOCK_TYPE(old_addr) != BLOCK_DATA)) || (!BLOCK_DELETED(virt_addr) && (old_addr != 0xfffffffe))) { static int ne = 0; if (++ne < 5) { printk(KERN_NOTICE "ftl_cs: set_bam_entry() inconsistency!\n"); printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, old = 0x%x" ", new = 0x%x\n", log_addr, old_addr, virt_addr); } return CS_GENERAL_FAILURE; }#endif if (part->bam_index == eun) {#ifdef PSYCHO_DEBUG if (part->bam_cache[blk] != old_addr) { static int ne = 0; if (++ne < 5) { printk(KERN_NOTICE "ftl_cs: set_bam_entry() " "inconsistency!\n"); printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, cache" " = 0x%x, card = 0x%x\n", part->bam_cache[blk], old_addr); } return CS_GENERAL_FAILURE; }#endif part->bam_cache[blk] = virt_addr; } ret = CardServices(WriteMemory, part->handle, &req, &virt_addr); if (ret != CS_SUCCESS) { printk(KERN_NOTICE "ftl_cs: set_bam_entry() failed!\n"); printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, new = 0x%x\n", log_addr, virt_addr); cs_error(WriteMemory, ret); } return ret;} /* set_bam_entry */static int ftl_write(ftl_dev_t *dev, partition_t *part, caddr_t buffer, u_long sector, u_long nblocks){ mem_op_t req; u_int bsize, log_addr, virt_addr, old_addr, blk; u_long i; int ret; DEBUG(2, "ftl_cs: ftl_write(0x%p, %ld, %ld)\n", part->handle, sector, nblocks); if (!(part->state & FTL_FORMATTED)) { printk(KERN_NOTICE "ftl_cs: bad partition\n"); return -EIO; } /* See if we need to reclaim space, before we start */ while (part->FreeTotal < nblocks) { ret = reclaim_block(dev, part); if (ret != CS_SUCCESS) return ret; } bsize = part->region.BlockSize; req.Attributes = MEM_OP_BUFFER_KERNEL; req.Count = SECTOR_SIZE; virt_addr = sector * SECTOR_SIZE | BLOCK_DATA; for (i = 0; i < nblocks; i++) { if (virt_addr >= part->header.FormattedSize) { printk(KERN_NOTICE "ftl_cs: bad write offset\n"); return -EIO; } /* Grab a free block */ blk = find_free(part); if (blk == 0) { static int ne = 0; if (++ne < 5) printk(KERN_NOTICE "ftl_cs: internal error: " "no free blocks!\n"); return -ENOSPC; } /* Tag the BAM entry, and write the new block */ log_addr = part->bam_index * bsize + blk * SECTOR_SIZE; part->EUNInfo[part->bam_index].Free--; part->FreeTotal--; if (set_bam_entry(part, log_addr, 0xfffffffe)) return -EIO; part->EUNInfo[part->bam_index].Deleted++; req.Offset = (part->EUNInfo[part->bam_index].Offset + blk * SECTOR_SIZE); ret = CardServices(WriteMemory, part->handle, &req, buffer); if (ret != CS_SUCCESS) { printk(KERN_NOTICE "ftl_cs: block write failed!\n"); printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, virt_addr" " = 0x%x, Offset = 0x%x\n", log_addr, virt_addr, req.Offset); cs_error(WriteMemory, ret); return -EIO; } /* Only delete the old entry when the new entry is ready */ old_addr = part->VirtualBlockMap[sector+i]; if (old_addr != 0xffffffff) { part->VirtualBlockMap[sector+i] = 0xffffffff; part->EUNInfo[old_addr/bsize].Deleted++; if (set_bam_entry(part, old_addr, 0) != CS_SUCCESS) return -EIO; } /* Finally, set up the new pointers */ if (set_bam_entry(part, log_addr, virt_addr)) return -EIO; part->VirtualBlockMap[sector+i] = log_addr; part->EUNInfo[part->bam_index].Deleted--; buffer += SECTOR_SIZE; virt_addr += SECTOR_SIZE; } return 0;} /* ftl_write *//*====================================================================== IOCTL calls for getting device parameters.======================================================================*/static int ftl_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg){ struct hd_geometry *geo = (struct hd_geometry *)arg; int ret = 0, minor = MINOR(inode->i_rdev); dev_link_t *link; ftl_dev_t *dev; partition_t *part; u_long sect; link = dev_table[DEVICE_NR(minor)]; if (!DEV_OK(link)) return -ENODEV; dev = (ftl_dev_t *)link->priv; part = &dev->minor[REGION_NR(minor)]; switch (cmd) { case HDIO_GETGEO: ret = verify_area(VERIFY_WRITE, (long *)arg, sizeof(*geo)); if (ret) return ret; /* Sort of arbitrary: round size down to 4K boundary */ sect = part->header.FormattedSize/SECTOR_SIZE; put_user(1, (char *)&geo->heads); put_user(8, (char *)&geo->sectors); put_user((sect>>3), (short *)&geo->cylinders); put_user(ftl_hd[minor].start_sect, (u_long *)&geo->start); break; case BLKGETSIZE: ret = verify_area(VERIFY_WRITE, (long *)arg, sizeof(long)); if (ret) return ret; put_user(ftl_hd[minor].nr_sects, (long *)arg); break; case BLKRRPART: ret = ftl_reread_partitions(minor); break;#if (LINUX_VERSION_CODE < VERSION(2,3,3)) case BLKFLSBUF: if (!capable(CAP_SYS_ADMIN)) return -EACCES; fsync_dev(inode->i_rdev); invalidate_buffers(inode->i_rdev); break; RO_IOCTLS(inode->i_rdev, arg);#else case BLKROSET: case BLKROGET: case BLKFLSBUF: ret = blk_ioctl(inode->i_rdev, cmd, arg); break;#endif default: ret = -EINVAL; } return ret;} /* ftl_ioctl *//*====================================================================== Handler for block device requests======================================================================*/static int ftl_reread_partitions(int minor){ int d = DEVICE_NR(minor), r = REGION_NR(minor); ftl_dev_t *dev = dev_table[d]->priv; partition_t *part = &(dev->minor[r]); int i, whole; DEBUG(0, "ftl_cs: ftl_reread_partition(%d)\n", minor); if (part->locked || (part->open > 1)) return -EBUSY; part->locked = 1; whole = minor & ~(MAX_PART-1); for (i = 0; i < MAX_PART; i++) { if (ftl_hd[whole+i].nr_sects > 0) { kdev_t rdev = MKDEV(major_dev, whole+i); sync_dev(rdev); INVALIDATE_INODES(rdev); invalidate_buffers(rdev); } ftl_hd[whole+i].start_sect = 0; ftl_hd[whole+i].nr_sects = 0; } scan_header(part); register_disk(&ftl_gendisk, whole >> PART_BITS, MAX_PART, &ftl_blk_fops, part->header.FormattedSize/SECTOR_SIZE);#ifdef PCMCIA_DEBUG for (i = 0; i < MAX_PART; i++) { if (ftl_hd[whole+i].nr_sects > 0) printk(KERN_INFO " %d: start %ld size %ld\n", i, ftl_hd[whole+i].start_sect, ftl_hd[whole+i].nr_sects); }#endif part->locked = 0; wake_up(&ftl_wait_open); return 0;}/*====================================================================== Handler for block device requests======================================================================*/static void do_ftl_request(request_arg_t){ int ret, minor; dev_link_t *link; ftl_dev_t *dev; partition_t *part; DEBUG(2, "ftl_cs: starting do_ftl_request()\n"); do { sti(); INIT_REQUEST; minor = MINOR(CURRENT->rq_dev); link = dev_table[DEVICE_NR(minor)]; dev = (ftl_dev_t *)link->priv; part = &dev->minor[REGION_NR(minor)]; ret = 0; switch (CURRENT->cmd) { case READ: ret = ftl_read(part, CURRENT->buffer, CURRENT->sector+ftl_hd[minor].start_sect, CURRENT->current_nr_sectors); break; case WRITE: ret = ftl_write(dev, part, CURRENT->buffer, CURRENT->sector+ftl_hd[minor].start_sect, CURRENT->current_nr_sectors); break; default: panic("ftl_cs: unknown block command!\n"); } end_request((ret == 0) ? 1 : 0); } while (1);} /* do_ftl_request *//*====================================================================*/static int __init init_ftl_cs(void){ servinfo_t serv; int i; DEBUG(0, "%s\n", version); CardServices(GetCardServicesInfo, &serv); if (serv.Revision != CS_RELEASE_CODE) { printk(KERN_NOTICE "ftl_cs: Card Services release " "does not match!\n"); return -EINVAL; } register_pccard_driver(&dev_info, &ftl_attach, &ftl_detach); major_dev = register_blkdev(major_dev, "ftl", &ftl_blk_fops); if (major_dev == 0) { printk(KERN_NOTICE "ftl_cs: unable to grab major " "device number!\n"); return -ENODEV; } for (i = 0; i < MINOR_NR(MAX_DEV, 0, 0); i++) ftl_blocksizes[i] = 1024; for (i = 0; i < MAX_DEV*MAX_PART; i++) { ftl_hd[i].nr_sects = 0; ftl_hd[i].start_sect = 0; } blksize_size[major_dev] = ftl_blocksizes; ftl_gendisk.major = major_dev; blk_init_queue(BLK_DEFAULT_QUEUE(major_dev), &do_ftl_request); add_gendisk(&ftl_gendisk); init_waitqueue_head(&ftl_wait_open); return 0;}static void __exit exit_ftl_cs(void){ int i; dev_link_t *link; DEBUG(0, "ftl_cs: unloading\n"); unregister_pccard_driver(&dev_info); if (major_dev != 0) { unregister_blkdev(major_dev, "ftl"); blk_cleanup_queue(BLK_DEFAULT_QUEUE(major_dev)); blksize_size[major_dev] = NULL; } for (i = 0; i < MAX_DEV; i++) { link = dev_table[i]; if (link) { if (link->state & DEV_CONFIG) ftl_release((u_long)link); ftl_detach(link); } } del_gendisk(&ftl_gendisk);}module_init(init_ftl_cs);module_exit(exit_ftl_cs);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -