pktcdvd.c

来自「linux 内核源代码」· C语言 代码 · 共 2,596 行 · 第 1/5 页

C
2,596
字号
		}	}	/*	 * Move no longer active packets to the free list	 */	spin_lock(&pd->cdrw.active_list_lock);	list_for_each_entry_safe(pkt, next, &pd->cdrw.pkt_active_list, list) {		if (pkt->state == PACKET_FINISHED_STATE) {			list_del(&pkt->list);			pkt_put_packet_data(pd, pkt);			pkt_set_state(pkt, PACKET_IDLE_STATE);			atomic_set(&pd->scan_queue, 1);		}	}	spin_unlock(&pd->cdrw.active_list_lock);}static void pkt_count_states(struct pktcdvd_device *pd, int *states){	struct packet_data *pkt;	int i;	for (i = 0; i < PACKET_NUM_STATES; i++)		states[i] = 0;	spin_lock(&pd->cdrw.active_list_lock);	list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {		states[pkt->state]++;	}	spin_unlock(&pd->cdrw.active_list_lock);}/* * kcdrwd is woken up when writes have been queued for one of our * registered devices */static int kcdrwd(void *foobar){	struct pktcdvd_device *pd = foobar;	struct packet_data *pkt;	long min_sleep_time, residue;	set_user_nice(current, -20);	set_freezable();	for (;;) {		DECLARE_WAITQUEUE(wait, current);		/*		 * Wait until there is something to do		 */		add_wait_queue(&pd->wqueue, &wait);		for (;;) {			set_current_state(TASK_INTERRUPTIBLE);			/* Check if we need to run pkt_handle_queue */			if (atomic_read(&pd->scan_queue) > 0)				goto work_to_do;			/* Check if we need to run the state machine for some packet */			list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {				if (atomic_read(&pkt->run_sm) > 0)					goto work_to_do;			}			/* Check if we need to process the iosched queues */			if (atomic_read(&pd->iosched.attention) != 0)				goto work_to_do;			/* Otherwise, go to sleep */			if (PACKET_DEBUG > 1) {				int states[PACKET_NUM_STATES];				pkt_count_states(pd, states);				VPRINTK("kcdrwd: i:%d ow:%d rw:%d ww:%d rec:%d fin:%d\n",					states[0], states[1], states[2], states[3],					states[4], states[5]);			}			min_sleep_time = MAX_SCHEDULE_TIMEOUT;			list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {				if (pkt->sleep_time && pkt->sleep_time < min_sleep_time)					min_sleep_time = pkt->sleep_time;			}			generic_unplug_device(bdev_get_queue(pd->bdev));			VPRINTK("kcdrwd: sleeping\n");			residue = schedule_timeout(min_sleep_time);			VPRINTK("kcdrwd: wake up\n");			/* make swsusp happy with our thread */			try_to_freeze();			list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {				if (!pkt->sleep_time)					continue;				pkt->sleep_time -= min_sleep_time - residue;				if (pkt->sleep_time <= 0) {					pkt->sleep_time = 0;					atomic_inc(&pkt->run_sm);				}			}			if (kthread_should_stop())				break;		}work_to_do:		set_current_state(TASK_RUNNING);		remove_wait_queue(&pd->wqueue, &wait);		if (kthread_should_stop())			break;		/*		 * if pkt_handle_queue returns true, we can queue		 * another request.		 */		while (pkt_handle_queue(pd))			;		/*		 * Handle packet state machine		 */		pkt_handle_packets(pd);		/*		 * Handle iosched queues		 */		pkt_iosched_process_queue(pd);	}	return 0;}static void pkt_print_settings(struct pktcdvd_device *pd){	printk(DRIVER_NAME": %s packets, ", pd->settings.fp ? "Fixed" : "Variable");	printk("%u blocks, ", pd->settings.size >> 2);	printk("Mode-%c disc\n", pd->settings.block_mode == 8 ? '1' : '2');}static int pkt_mode_sense(struct pktcdvd_device *pd, struct packet_command *cgc, int page_code, int page_control){	memset(cgc->cmd, 0, sizeof(cgc->cmd));	cgc->cmd[0] = GPCMD_MODE_SENSE_10;	cgc->cmd[2] = page_code | (page_control << 6);	cgc->cmd[7] = cgc->buflen >> 8;	cgc->cmd[8] = cgc->buflen & 0xff;	cgc->data_direction = CGC_DATA_READ;	return pkt_generic_packet(pd, cgc);}static int pkt_mode_select(struct pktcdvd_device *pd, struct packet_command *cgc){	memset(cgc->cmd, 0, sizeof(cgc->cmd));	memset(cgc->buffer, 0, 2);	cgc->cmd[0] = GPCMD_MODE_SELECT_10;	cgc->cmd[1] = 0x10;		/* PF */	cgc->cmd[7] = cgc->buflen >> 8;	cgc->cmd[8] = cgc->buflen & 0xff;	cgc->data_direction = CGC_DATA_WRITE;	return pkt_generic_packet(pd, cgc);}static int pkt_get_disc_info(struct pktcdvd_device *pd, disc_information *di){	struct packet_command cgc;	int ret;	/* set up command and get the disc info */	init_cdrom_command(&cgc, di, sizeof(*di), CGC_DATA_READ);	cgc.cmd[0] = GPCMD_READ_DISC_INFO;	cgc.cmd[8] = cgc.buflen = 2;	cgc.quiet = 1;	if ((ret = pkt_generic_packet(pd, &cgc)))		return ret;	/* not all drives have the same disc_info length, so requeue	 * packet with the length the drive tells us it can supply	 */	cgc.buflen = be16_to_cpu(di->disc_information_length) +		     sizeof(di->disc_information_length);	if (cgc.buflen > sizeof(disc_information))		cgc.buflen = sizeof(disc_information);	cgc.cmd[8] = cgc.buflen;	return pkt_generic_packet(pd, &cgc);}static int pkt_get_track_info(struct pktcdvd_device *pd, __u16 track, __u8 type, track_information *ti){	struct packet_command cgc;	int ret;	init_cdrom_command(&cgc, ti, 8, CGC_DATA_READ);	cgc.cmd[0] = GPCMD_READ_TRACK_RZONE_INFO;	cgc.cmd[1] = type & 3;	cgc.cmd[4] = (track & 0xff00) >> 8;	cgc.cmd[5] = track & 0xff;	cgc.cmd[8] = 8;	cgc.quiet = 1;	if ((ret = pkt_generic_packet(pd, &cgc)))		return ret;	cgc.buflen = be16_to_cpu(ti->track_information_length) +		     sizeof(ti->track_information_length);	if (cgc.buflen > sizeof(track_information))		cgc.buflen = sizeof(track_information);	cgc.cmd[8] = cgc.buflen;	return pkt_generic_packet(pd, &cgc);}static int pkt_get_last_written(struct pktcdvd_device *pd, long *last_written){	disc_information di;	track_information ti;	__u32 last_track;	int ret = -1;	if ((ret = pkt_get_disc_info(pd, &di)))		return ret;	last_track = (di.last_track_msb << 8) | di.last_track_lsb;	if ((ret = pkt_get_track_info(pd, last_track, 1, &ti)))		return ret;	/* if this track is blank, try the previous. */	if (ti.blank) {		last_track--;		if ((ret = pkt_get_track_info(pd, last_track, 1, &ti)))			return ret;	}	/* if last recorded field is valid, return it. */	if (ti.lra_v) {		*last_written = be32_to_cpu(ti.last_rec_address);	} else {		/* make it up instead */		*last_written = be32_to_cpu(ti.track_start) +				be32_to_cpu(ti.track_size);		if (ti.free_blocks)			*last_written -= (be32_to_cpu(ti.free_blocks) + 7);	}	return 0;}/* * write mode select package based on pd->settings */static int pkt_set_write_settings(struct pktcdvd_device *pd){	struct packet_command cgc;	struct request_sense sense;	write_param_page *wp;	char buffer[128];	int ret, size;	/* doesn't apply to DVD+RW or DVD-RAM */	if ((pd->mmc3_profile == 0x1a) || (pd->mmc3_profile == 0x12))		return 0;	memset(buffer, 0, sizeof(buffer));	init_cdrom_command(&cgc, buffer, sizeof(*wp), CGC_DATA_READ);	cgc.sense = &sense;	if ((ret = pkt_mode_sense(pd, &cgc, GPMODE_WRITE_PARMS_PAGE, 0))) {		pkt_dump_sense(&cgc);		return ret;	}	size = 2 + ((buffer[0] << 8) | (buffer[1] & 0xff));	pd->mode_offset = (buffer[6] << 8) | (buffer[7] & 0xff);	if (size > sizeof(buffer))		size = sizeof(buffer);	/*	 * now get it all	 */	init_cdrom_command(&cgc, buffer, size, CGC_DATA_READ);	cgc.sense = &sense;	if ((ret = pkt_mode_sense(pd, &cgc, GPMODE_WRITE_PARMS_PAGE, 0))) {		pkt_dump_sense(&cgc);		return ret;	}	/*	 * write page is offset header + block descriptor length	 */	wp = (write_param_page *) &buffer[sizeof(struct mode_page_header) + pd->mode_offset];	wp->fp = pd->settings.fp;	wp->track_mode = pd->settings.track_mode;	wp->write_type = pd->settings.write_type;	wp->data_block_type = pd->settings.block_mode;	wp->multi_session = 0;#ifdef PACKET_USE_LS	wp->link_size = 7;	wp->ls_v = 1;#endif	if (wp->data_block_type == PACKET_BLOCK_MODE1) {		wp->session_format = 0;		wp->subhdr2 = 0x20;	} else if (wp->data_block_type == PACKET_BLOCK_MODE2) {		wp->session_format = 0x20;		wp->subhdr2 = 8;#if 0		wp->mcn[0] = 0x80;		memcpy(&wp->mcn[1], PACKET_MCN, sizeof(wp->mcn) - 1);#endif	} else {		/*		 * paranoia		 */		printk(DRIVER_NAME": write mode wrong %d\n", wp->data_block_type);		return 1;	}	wp->packet_size = cpu_to_be32(pd->settings.size >> 2);	cgc.buflen = cgc.cmd[8] = size;	if ((ret = pkt_mode_select(pd, &cgc))) {		pkt_dump_sense(&cgc);		return ret;	}	pkt_print_settings(pd);	return 0;}/* * 1 -- we can write to this track, 0 -- we can't */static int pkt_writable_track(struct pktcdvd_device *pd, track_information *ti){	switch (pd->mmc3_profile) {		case 0x1a: /* DVD+RW */		case 0x12: /* DVD-RAM */			/* The track is always writable on DVD+RW/DVD-RAM */			return 1;		default:			break;	}	if (!ti->packet || !ti->fp)		return 0;	/*	 * "good" settings as per Mt Fuji.	 */	if (ti->rt == 0 && ti->blank == 0)		return 1;	if (ti->rt == 0 && ti->blank == 1)		return 1;	if (ti->rt == 1 && ti->blank == 0)		return 1;	printk(DRIVER_NAME": bad state %d-%d-%d\n", ti->rt, ti->blank, ti->packet);	return 0;}/* * 1 -- we can write to this disc, 0 -- we can't */static int pkt_writable_disc(struct pktcdvd_device *pd, disc_information *di){	switch (pd->mmc3_profile) {		case 0x0a: /* CD-RW */		case 0xffff: /* MMC3 not supported */			break;		case 0x1a: /* DVD+RW */		case 0x13: /* DVD-RW */		case 0x12: /* DVD-RAM */			return 1;		default:			VPRINTK(DRIVER_NAME": Wrong disc profile (%x)\n", pd->mmc3_profile);			return 0;	}	/*	 * for disc type 0xff we should probably reserve a new track.	 * but i'm not sure, should we leave this to user apps? probably.	 */	if (di->disc_type == 0xff) {		printk(DRIVER_NAME": Unknown disc. No track?\n");		return 0;	}	if (di->disc_type != 0x20 && di->disc_type != 0) {		printk(DRIVER_NAME": Wrong disc type (%x)\n", di->disc_type);		return 0;	}	if (di->erasable == 0) {		printk(DRIVER_NAME": Disc not erasable\n");		return 0;	}	if (di->border_status == PACKET_SESSION_RESERVED) {		printk(DRIVER_NAME": Can't write to last track (reserved)\n");		return 0;	}	return 1;}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_writable_disc(pd, &di))		return -EROFS;	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(DRIVER_NAME": failed get_track\n");		return ret;	}	if (!pkt_writable_track(pd, &ti)) {		printk(DRIVER_NAME": can't write to this track\n");		return -EROFS;	}	/*	 * 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(DRIVER_NAME": detected zero packet size!\n");		return -ENXIO;	}	if (pd->settings.size > PACKET_MAX_SECTORS) {		printk(DRIVER_NAME": packet size is too big\n");		return -EROFS;	}	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(DRIVER_NAME": unknown data mode\n");			return -EROFS;	}	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;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?