📄 pktcdvd.c
字号:
printk("pktcdvd: Can't write to last track (reserved)\n"); return 1; } return 0;}static int pkt_probe_settings(struct pktcdvd_device *pd){ struct packet_command cgc; unsigned char buf[12]; disc_information di; track_information ti; int ret, track; init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ); cgc.cmd[0] = GPCMD_GET_CONFIGURATION; cgc.cmd[8] = 8; ret = pkt_generic_packet(pd, &cgc); pd->mmc3_profile = ret ? 0xffff : buf[6] << 8 | buf[7]; memset(&di, 0, sizeof(disc_information)); memset(&ti, 0, sizeof(track_information)); if ((ret = pkt_get_disc_info(pd, &di))) { printk("failed get_disc\n"); return ret; } if (pkt_good_disc(pd, &di)) return -ENXIO; switch (pd->mmc3_profile) { case 0x1a: /* DVD+RW */ printk("pktcdvd: inserted media is DVD+RW\n"); break; case 0x13: /* DVD-RW */ printk("pktcdvd: inserted media is DVD-RW\n"); break; case 0x12: /* DVD-RAM */ printk("pktcdvd: inserted media is DVD-RAM\n"); break; default: printk("pktcdvd: inserted media is CD-R%s\n", di.erasable ? "W" : ""); break; } pd->type = di.erasable ? PACKET_CDRW : PACKET_CDR; track = 1; /* (di.last_track_msb << 8) | di.last_track_lsb; */ if ((ret = pkt_get_track_info(pd, track, 1, &ti))) { printk("pktcdvd: failed get_track\n"); return ret; } if (pkt_good_track(&ti)) { printk("pktcdvd: can't write to this track\n"); return -ENXIO; } /* * we keep packet size in 512 byte units, makes it easier to * deal with request calculations. */ pd->settings.size = be32_to_cpu(ti.fixed_packet_size) << 2; if (pd->settings.size == 0) { printk("pktcdvd: detected zero packet size!\n"); pd->settings.size = 128; } if (pd->settings.size > PACKET_MAX_SECTORS) { printk("pktcdvd: packet size is too big\n"); return -ENXIO; } pd->settings.fp = ti.fp; pd->offset = (be32_to_cpu(ti.track_start) << 2) & (pd->settings.size - 1); if (ti.nwa_v) { pd->nwa = be32_to_cpu(ti.next_writable); set_bit(PACKET_NWA_VALID, &pd->flags); } /* * in theory we could use lra on -RW media as well and just zero * blocks that haven't been written yet, but in practice that * is just a no-go. we'll use that for -R, naturally. */ if (ti.lra_v) { pd->lra = be32_to_cpu(ti.last_rec_address); set_bit(PACKET_LRA_VALID, &pd->flags); } else { pd->lra = 0xffffffff; set_bit(PACKET_LRA_VALID, &pd->flags); } /* * fine for now */ pd->settings.link_loss = 7; pd->settings.write_type = 0; /* packet */ pd->settings.track_mode = ti.track_mode; /* * mode1 or mode2 disc */ switch (ti.data_mode) { case PACKET_MODE1: pd->settings.block_mode = PACKET_BLOCK_MODE1; break; case PACKET_MODE2: pd->settings.block_mode = PACKET_BLOCK_MODE2; break; default: printk("pktcdvd: unknown data mode\n"); return 1; } return 0;}/* * enable/disable write caching on drive */static int pkt_write_caching(struct pktcdvd_device *pd, int set){ struct packet_command cgc; struct request_sense sense; unsigned char buf[64]; int ret; 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("pktcdvd: write caching control failed\n"); pkt_dump_sense(&cgc); } else if (!ret && set) printk("pktcdvd: 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("pktcdvd: Disc type is not CD-RW\n"); return 1; } if (!buf[6] & 0x4) { printk("pktcdvd: 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("pktcdvd: Unknown disc sub-type %d\n",st); return 1; } if (*speed) { printk("pktcdvd: Max. media speed: %d\n",*speed); return 0; } else { printk("pktcdvd: 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("pktcdvd: 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))) { DPRINTK("pktcdvd: %s failed probe\n", pd->name); return -EIO; } if ((ret = pkt_set_write_settings(pd))) { DPRINTK("pktcdvd: %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("pktcdvd: 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("pktcdvd: write speed %ux\n", write_speed / 176); break; } read_speed = write_speed; if ((ret = pkt_set_speed(pd, write_speed, read_speed))) { DPRINTK("pktcdvd: %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("pktcdvd: %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; request_queue_t *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 = pkt_get_last_written(pd, &lba))) { printk("pktcdvd: pkt_get_last_written failed\n"); goto out_putdev; } 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_putdev; /* * 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_putdev; if (write) printk("pktcdvd: %lukB available on disc\n", lba << 1); return 0;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("pktcdvd: %s not flushing cache\n", pd->name); pkt_lock_door(pd, 0); pkt_set_speed(pd, MAX_SPEED, MAX_SPEED); blkdev_put(pd->bdev);}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("pktcdvd: entering open\n"); down(&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 { if (pkt_open_dev(pd, file->f_mode & FMODE_WRITE)) { ret = -EIO; 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); } up(&ctl_mutex); return 0;out_dec: pd->refcnt--;out: VPRINTK("pktcdvd: failed open (%d)\n", ret); up(&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; down(&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); } up(&ctl_mutex); return ret;}static void *psd_pool_alloc(gfp_t gfp_mask, void *data){ return kmalloc(sizeof(struct packet_stacked_data), gfp_mask);}static void psd_pool_free(void *ptr, void *data){ kfree(ptr);}static int pkt_end_io_read_cloned(struct bio *bio, unsigned int bytes_done, int err){ struct packet_stacked_data *psd = bio->bi_private; struct pktcdvd_device *pd = psd->pd; if (bio->bi_size) return 1; bio_put(bio); bio_endio(psd->bio, psd->bio->bi_size, err);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -