pktcdvd.c
来自「linux 内核源代码」· C语言 代码 · 共 2,596 行 · 第 1/5 页
C
2,596 行
memset(buf, 0, sizeof(buf)); init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ); cgc.sense = &sense; cgc.buflen = pd->mode_offset + 12; /* * caching mode page might not be there, so quiet this command */ cgc.quiet = 1; if ((ret = pkt_mode_sense(pd, &cgc, GPMODE_WCACHING_PAGE, 0))) return ret; buf[pd->mode_offset + 10] |= (!!set << 2); cgc.buflen = cgc.cmd[8] = 2 + ((buf[0] << 8) | (buf[1] & 0xff)); ret = pkt_mode_select(pd, &cgc); if (ret) { printk(DRIVER_NAME": write caching control failed\n"); pkt_dump_sense(&cgc); } else if (!ret && set) printk(DRIVER_NAME": enabled write caching on %s\n", pd->name); return ret;}static int pkt_lock_door(struct pktcdvd_device *pd, int lockflag){ struct packet_command cgc; init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); cgc.cmd[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL; cgc.cmd[4] = lockflag ? 1 : 0; return pkt_generic_packet(pd, &cgc);}/* * Returns drive maximum write speed */static int pkt_get_max_speed(struct pktcdvd_device *pd, unsigned *write_speed){ struct packet_command cgc; struct request_sense sense; unsigned char buf[256+18]; unsigned char *cap_buf; int ret, offset; memset(buf, 0, sizeof(buf)); cap_buf = &buf[sizeof(struct mode_page_header) + pd->mode_offset]; init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_UNKNOWN); cgc.sense = &sense; ret = pkt_mode_sense(pd, &cgc, GPMODE_CAPABILITIES_PAGE, 0); if (ret) { cgc.buflen = pd->mode_offset + cap_buf[1] + 2 + sizeof(struct mode_page_header); ret = pkt_mode_sense(pd, &cgc, GPMODE_CAPABILITIES_PAGE, 0); if (ret) { pkt_dump_sense(&cgc); return ret; } } offset = 20; /* Obsoleted field, used by older drives */ if (cap_buf[1] >= 28) offset = 28; /* Current write speed selected */ if (cap_buf[1] >= 30) { /* If the drive reports at least one "Logical Unit Write * Speed Performance Descriptor Block", use the information * in the first block. (contains the highest speed) */ int num_spdb = (cap_buf[30] << 8) + cap_buf[31]; if (num_spdb > 0) offset = 34; } *write_speed = (cap_buf[offset] << 8) | cap_buf[offset + 1]; return 0;}/* These tables from cdrecord - I don't have orange book *//* standard speed CD-RW (1-4x) */static char clv_to_speed[16] = { /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ 0, 2, 4, 6, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};/* high speed CD-RW (-10x) */static char hs_clv_to_speed[16] = { /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ 0, 2, 4, 6, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};/* ultra high speed CD-RW */static char us_clv_to_speed[16] = { /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ 0, 2, 4, 8, 0, 0,16, 0,24,32,40,48, 0, 0, 0, 0};/* * reads the maximum media speed from ATIP */static int pkt_media_speed(struct pktcdvd_device *pd, unsigned *speed){ struct packet_command cgc; struct request_sense sense; unsigned char buf[64]; unsigned int size, st, sp; int ret; init_cdrom_command(&cgc, buf, 2, CGC_DATA_READ); cgc.sense = &sense; cgc.cmd[0] = GPCMD_READ_TOC_PMA_ATIP; cgc.cmd[1] = 2; cgc.cmd[2] = 4; /* READ ATIP */ cgc.cmd[8] = 2; ret = pkt_generic_packet(pd, &cgc); if (ret) { pkt_dump_sense(&cgc); return ret; } size = ((unsigned int) buf[0]<<8) + buf[1] + 2; if (size > sizeof(buf)) size = sizeof(buf); init_cdrom_command(&cgc, buf, size, CGC_DATA_READ); cgc.sense = &sense; cgc.cmd[0] = GPCMD_READ_TOC_PMA_ATIP; cgc.cmd[1] = 2; cgc.cmd[2] = 4; cgc.cmd[8] = size; ret = pkt_generic_packet(pd, &cgc); if (ret) { pkt_dump_sense(&cgc); return ret; } if (!buf[6] & 0x40) { printk(DRIVER_NAME": Disc type is not CD-RW\n"); return 1; } if (!buf[6] & 0x4) { printk(DRIVER_NAME": A1 values on media are not valid, maybe not CDRW?\n"); return 1; } st = (buf[6] >> 3) & 0x7; /* disc sub-type */ sp = buf[16] & 0xf; /* max speed from ATIP A1 field */ /* Info from cdrecord */ switch (st) { case 0: /* standard speed */ *speed = clv_to_speed[sp]; break; case 1: /* high speed */ *speed = hs_clv_to_speed[sp]; break; case 2: /* ultra high speed */ *speed = us_clv_to_speed[sp]; break; default: printk(DRIVER_NAME": Unknown disc sub-type %d\n",st); return 1; } if (*speed) { printk(DRIVER_NAME": Max. media speed: %d\n",*speed); return 0; } else { printk(DRIVER_NAME": Unknown speed %d for sub-type %d\n",sp,st); return 1; }}static int pkt_perform_opc(struct pktcdvd_device *pd){ struct packet_command cgc; struct request_sense sense; int ret; VPRINTK(DRIVER_NAME": Performing OPC\n"); init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); cgc.sense = &sense; cgc.timeout = 60*HZ; cgc.cmd[0] = GPCMD_SEND_OPC; cgc.cmd[1] = 1; if ((ret = pkt_generic_packet(pd, &cgc))) pkt_dump_sense(&cgc); return ret;}static int pkt_open_write(struct pktcdvd_device *pd){ int ret; unsigned int write_speed, media_write_speed, read_speed; if ((ret = pkt_probe_settings(pd))) { VPRINTK(DRIVER_NAME": %s failed probe\n", pd->name); return ret; } if ((ret = pkt_set_write_settings(pd))) { DPRINTK(DRIVER_NAME": %s failed saving write settings\n", pd->name); return -EIO; } pkt_write_caching(pd, USE_WCACHING); if ((ret = pkt_get_max_speed(pd, &write_speed))) write_speed = 16 * 177; switch (pd->mmc3_profile) { case 0x13: /* DVD-RW */ case 0x1a: /* DVD+RW */ case 0x12: /* DVD-RAM */ DPRINTK(DRIVER_NAME": write speed %ukB/s\n", write_speed); break; default: if ((ret = pkt_media_speed(pd, &media_write_speed))) media_write_speed = 16; write_speed = min(write_speed, media_write_speed * 177); DPRINTK(DRIVER_NAME": write speed %ux\n", write_speed / 176); break; } read_speed = write_speed; if ((ret = pkt_set_speed(pd, write_speed, read_speed))) { DPRINTK(DRIVER_NAME": %s couldn't set write speed\n", pd->name); return -EIO; } pd->write_speed = write_speed; pd->read_speed = read_speed; if ((ret = pkt_perform_opc(pd))) { DPRINTK(DRIVER_NAME": %s Optimum Power Calibration failed\n", pd->name); } return 0;}/* * called at open time. */static int pkt_open_dev(struct pktcdvd_device *pd, int write){ int ret; long lba; struct request_queue *q; /* * We need to re-open the cdrom device without O_NONBLOCK to be able * to read/write from/to it. It is already opened in O_NONBLOCK mode * so bdget() can't fail. */ bdget(pd->bdev->bd_dev); if ((ret = blkdev_get(pd->bdev, FMODE_READ, O_RDONLY))) goto out; if ((ret = bd_claim(pd->bdev, pd))) goto out_putdev; if ((ret = pkt_get_last_written(pd, &lba))) { printk(DRIVER_NAME": pkt_get_last_written failed\n"); goto out_unclaim; } set_capacity(pd->disk, lba << 2); set_capacity(pd->bdev->bd_disk, lba << 2); bd_set_size(pd->bdev, (loff_t)lba << 11); q = bdev_get_queue(pd->bdev); if (write) { if ((ret = pkt_open_write(pd))) goto out_unclaim; /* * Some CDRW drives can not handle writes larger than one packet, * even if the size is a multiple of the packet size. */ spin_lock_irq(q->queue_lock); blk_queue_max_sectors(q, pd->settings.size); spin_unlock_irq(q->queue_lock); set_bit(PACKET_WRITABLE, &pd->flags); } else { pkt_set_speed(pd, MAX_SPEED, MAX_SPEED); clear_bit(PACKET_WRITABLE, &pd->flags); } if ((ret = pkt_set_segment_merging(pd, q))) goto out_unclaim; if (write) { if (!pkt_grow_pktlist(pd, CONFIG_CDROM_PKTCDVD_BUFFERS)) { printk(DRIVER_NAME": not enough memory for buffers\n"); ret = -ENOMEM; goto out_unclaim; } printk(DRIVER_NAME": %lukB available on disc\n", lba << 1); } return 0;out_unclaim: bd_release(pd->bdev);out_putdev: blkdev_put(pd->bdev);out: return ret;}/* * called when the device is closed. makes sure that the device flushes * the internal cache before we close. */static void pkt_release_dev(struct pktcdvd_device *pd, int flush){ if (flush && pkt_flush_cache(pd)) DPRINTK(DRIVER_NAME": %s not flushing cache\n", pd->name); pkt_lock_door(pd, 0); pkt_set_speed(pd, MAX_SPEED, MAX_SPEED); bd_release(pd->bdev); blkdev_put(pd->bdev); pkt_shrink_pktlist(pd);}static struct pktcdvd_device *pkt_find_dev_from_minor(int dev_minor){ if (dev_minor >= MAX_WRITERS) return NULL; return pkt_devs[dev_minor];}static int pkt_open(struct inode *inode, struct file *file){ struct pktcdvd_device *pd = NULL; int ret; VPRINTK(DRIVER_NAME": entering open\n"); mutex_lock(&ctl_mutex); pd = pkt_find_dev_from_minor(iminor(inode)); if (!pd) { ret = -ENODEV; goto out; } BUG_ON(pd->refcnt < 0); pd->refcnt++; if (pd->refcnt > 1) { if ((file->f_mode & FMODE_WRITE) && !test_bit(PACKET_WRITABLE, &pd->flags)) { ret = -EBUSY; goto out_dec; } } else { ret = pkt_open_dev(pd, file->f_mode & FMODE_WRITE); if (ret) goto out_dec; /* * needed here as well, since ext2 (among others) may change * the blocksize at mount time */ set_blocksize(inode->i_bdev, CD_FRAMESIZE); } mutex_unlock(&ctl_mutex); return 0;out_dec: pd->refcnt--;out: VPRINTK(DRIVER_NAME": failed open (%d)\n", ret); mutex_unlock(&ctl_mutex); return ret;}static int pkt_close(struct inode *inode, struct file *file){ struct pktcdvd_device *pd = inode->i_bdev->bd_disk->private_data; int ret = 0; mutex_lock(&ctl_mutex); pd->refcnt--; BUG_ON(pd->refcnt < 0); if (pd->refcnt == 0) { int flush = test_bit(PACKET_WRITABLE, &pd->flags); pkt_release_dev(pd, flush); } mutex_unlock(&ctl_mutex); return ret;}static void pkt_end_io_read_cloned(struct bio *bio, int err){ struct packet_stacked_data *psd = bio->bi_private; struct pktcdvd_device *pd = psd->pd; bio_put(bio); bio_endio(psd->bio, err); mempool_free(psd, psd_pool); pkt_bio_finished(pd);}static int pkt_make_request(struct request_queue *q, struct bio *bio){ struct pktcdvd_device *pd; char b[BDEVNAME_SIZE]; sector_t zone; struct packet_data *pkt; int was_empty, blocked_bio; struct pkt_rb_node *node; pd = q->queuedata; if (!pd) { printk(DRIVER_NAME": %s incorrect request queue\n", bdevname(bio->bi_bdev, b)); goto end_io; } /* * Clone READ bios so we can have our own bi_end_io callback. */ if (bio_data_dir(bio) == READ) { struct bio *cloned_bio = bio_clone(bio, GFP_NOIO); struct packet_stacked_data *psd = mempool_alloc(psd_pool, GFP_NOIO); psd->pd = pd; psd->bio = bio; cloned_bio->bi_bdev = pd->bdev; cloned_bio->bi_private = psd; cloned_bio->bi_end_io = pkt_end_io_read_cloned; pd->stats.secs_r += bio->bi_size >> 9; pkt_queue_bio(pd, cloned_bio); return 0; } if (!test_bit(PACKET_WRITABLE, &pd->flags)) { printk(DRIVER_NAME": WRITE for ro device %s (%llu)\n", pd->name, (unsigned long long)bio->bi_sector); goto end_io; } if (!bio->bi_size || (bio->bi_size % CD_FRAMESIZE)) { printk(DRIVER_NAME": wrong bio size\n"); goto end_io; } blk_queue_bounce(q, &bio); zone = ZONE(bio->bi_sector, pd); VPRINTK("pkt_make_request: start = %6llx stop = %6llx\n", (unsigned long long)bio->bi_sector, (unsigned long long)(bio->bi_sector + bio_sectors(bio))); /* Check if we have to split the bio */ { struct bio_pair *bp; sector_t last_zone; int first_sectors; last_zone = ZONE(bio->bi_sector + bio_sectors(bio) - 1, pd); if (last_zone != zone) { BUG_ON(last_zone != zone + pd->settings.size); first_sectors = last_zone - bio->bi_sector; bp = bio_split(bio, bio_split_pool, first_sectors); BUG_ON(!bp); pkt_make_request(q, &bp->bio1); pkt_make_request(q, &bp->bio2); bio_pair_release(bp); return 0; } } /* * If we find a matching packet in state WAITING or READ_WAIT, we can * just append this bio to that packet. */ spin_lock(&pd->cdrw.active_list_lock); blocked_bio = 0; list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) { if (pkt->sector == zone) { spin_lock(&pkt->lock); if ((pkt->state == PACKET_WAITING_STATE) || (pkt->state == PACKET_READ_WAIT_STATE)) { pkt_add_list_last(bio, &pkt->orig_bios, &pkt->orig_bios_tail); pkt->write_size += bio->bi_size / CD_FRAMESIZE; if ((pkt->write_size >= pkt->frames) && (pkt->state == PACKET_WAITING_STATE)) { atomic_inc(&pkt->run_sm); wake_up(&pd->wqueue); } spin_unlock(&pkt->lock); spin_unlock(&pd->cdrw.active_list_lock); return 0; } else { blocked_bio = 1; } spin_unlock(&pkt->lock); } } spin_unlock(&pd->cdrw.active_list_lock); /* * Test if there is enough room left in the bio work queue * (queue size >= congestion on mark). * If not, wait till the work queue size is below the congestion off mark. */ spin_lock(&pd->lock); if (pd->write_congestion_on > 0 && pd->bio_queue_size >= pd->write_congestion_on) { set_bdi_congested(&q->backing_dev_info, WRITE); do { spin_unlock(&pd->lock); congestion_wait(WRITE, HZ); spin_lock(&
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?