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 + -
显示快捷键?