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