⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pktcdvd.c

📁 Linux块设备驱动源码
💻 C
📖 第 1 页 / 共 5 页
字号:
	}	/* Start the write request */	bio_init(pkt->w_bio);	pkt->w_bio->bi_max_vecs = PACKET_MAX_SIZE;	pkt->w_bio->bi_sector = pkt->sector;	pkt->w_bio->bi_bdev = pd->bdev;	pkt->w_bio->bi_end_io = pkt_end_io_packet_write;	pkt->w_bio->bi_private = pkt;	for (f = 0; f < pkt->frames; f++) {		if ((f + 1 < pkt->frames) && (pages[f + 1] == pages[f]) &&		    (offsets[f + 1] = offsets[f] + CD_FRAMESIZE)) {			if (!bio_add_page(pkt->w_bio, pages[f], CD_FRAMESIZE * 2, offsets[f]))				BUG();			f++;		} else {			if (!bio_add_page(pkt->w_bio, pages[f], CD_FRAMESIZE, offsets[f]))				BUG();		}	}	VPRINTK("pktcdvd: vcnt=%d\n", pkt->w_bio->bi_vcnt);	atomic_set(&pkt->io_wait, 1);	pkt->w_bio->bi_rw = WRITE;	pkt_queue_bio(pd, pkt->w_bio);}static void pkt_finish_packet(struct packet_data *pkt, int uptodate){	struct bio *bio, *next;	if (!uptodate)		pkt->cache_valid = 0;	/* Finish all bios corresponding to this packet */	bio = pkt->orig_bios;	while (bio) {		next = bio->bi_next;		bio->bi_next = NULL;		bio_endio(bio, bio->bi_size, uptodate ? 0 : -EIO);		bio = next;	}	pkt->orig_bios = pkt->orig_bios_tail = NULL;}static void pkt_run_state_machine(struct pktcdvd_device *pd, struct packet_data *pkt){	int uptodate;	VPRINTK("run_state_machine: pkt %d\n", pkt->id);	for (;;) {		switch (pkt->state) {		case PACKET_WAITING_STATE:			if ((pkt->write_size < pkt->frames) && (pkt->sleep_time > 0))				return;			pkt->sleep_time = 0;			pkt_gather_data(pd, pkt);			pkt_set_state(pkt, PACKET_READ_WAIT_STATE);			break;		case PACKET_READ_WAIT_STATE:			if (atomic_read(&pkt->io_wait) > 0)				return;			if (atomic_read(&pkt->io_errors) > 0) {				pkt_set_state(pkt, PACKET_RECOVERY_STATE);			} else {				pkt_start_write(pd, pkt);			}			break;		case PACKET_WRITE_WAIT_STATE:			if (atomic_read(&pkt->io_wait) > 0)				return;			if (test_bit(BIO_UPTODATE, &pkt->w_bio->bi_flags)) {				pkt_set_state(pkt, PACKET_FINISHED_STATE);			} else {				pkt_set_state(pkt, PACKET_RECOVERY_STATE);			}			break;		case PACKET_RECOVERY_STATE:			if (pkt_start_recovery(pkt)) {				pkt_start_write(pd, pkt);			} else {				VPRINTK("No recovery possible\n");				pkt_set_state(pkt, PACKET_FINISHED_STATE);			}			break;		case PACKET_FINISHED_STATE:			uptodate = test_bit(BIO_UPTODATE, &pkt->w_bio->bi_flags);			pkt_finish_packet(pkt, uptodate);			return;		default:			BUG();			break;		}	}}static void pkt_handle_packets(struct pktcdvd_device *pd){	struct packet_data *pkt, *next;	VPRINTK("pkt_handle_packets\n");	/*	 * Run state machine for active packets	 */	list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {		if (atomic_read(&pkt->run_sm) > 0) {			atomic_set(&pkt->run_sm, 0);			pkt_run_state_machine(pd, pkt);		}	}	/*	 * 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);	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 (signal_pending(current)) {				flush_signals(current);			}			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("pktcdvd: %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("pktcdvd: 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;}/* * 0 -- we can write to this track, 1 -- we can't */static int pkt_good_track(track_information *ti){	/*	 * only good for CD-RW at the moment, not DVD-RW	 */	/*	 * FIXME: only for FP	 */	if (ti->fp == 0)		return 0;	/*	 * "good" settings as per Mt Fuji.	 */	if (ti->rt == 0 && ti->blank == 0 && ti->packet == 1)		return 0;	if (ti->rt == 0 && ti->blank == 1 && ti->packet == 1)		return 0;	if (ti->rt == 1 && ti->blank == 0 && ti->packet == 1)		return 0;	printk("pktcdvd: bad state %d-%d-%d\n", ti->rt, ti->blank, ti->packet);	return 1;}/* * 0 -- we can write to this disc, 1 -- we can't */static int pkt_good_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 0;		default:			printk("pktcdvd: Wrong disc profile (%x)\n", pd->mmc3_profile);			return 1;	}	/*	 * 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("pktcdvd: Unknown disc. No track?\n");		return 1;	}	if (di->disc_type != 0x20 && di->disc_type != 0) {		printk("pktcdvd: Wrong disc type (%x)\n", di->disc_type);		return 1;	}	if (di->erasable == 0) {		printk("pktcdvd: Disc not erasable\n");		return 1;	}	if (di->border_status == PACKET_SESSION_RESERVED) {

⌨️ 快捷键说明

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