📄 pktcdvd.c
字号:
} /* 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 + -