--- readcd/readcd.c.orig Sat Jun 11 15:22:10 2005 +++ readcd/readcd.c Mon Jan 30 18:58:29 2006 @@ -86,6 +86,44 @@ BOOL ismmc; } rparm_t; +typedef struct { + int c1_errors; + int c2_errors; + int cu_errors; +} cxerror_t; + +/* initiates a scan */ +typedef int (*start_cx_func_t)(SCSI*); + +/* ends a scan */ +typedef int (*end_cx_func_t)(SCSI*); + +/* scans a certain range, should be 75 sectors. Return value is the next sector to be scanned */ +typedef int (*one_interval_func_t)(SCSI *, cxerror_t *, int, void *); + +/* describes one set of functions needed to perform an cx scan */ +typedef struct { + start_cx_func_t start_func; + end_cx_func_t end_func; + one_interval_func_t one_interval_func; +} cx_scan_procedure_t; + +LOCAL int plextor_init_cx_scan __PR((SCSI* scgp)); +LOCAL int plextor_end_scan __PR((SCSI* scgp)); +LOCAL int plextor_read_cx_values __PR((SCSI* scgp, cxerror_t *pe)); +LOCAL int nec_init_cx_scan __PR((SCSI* scgp)); +LOCAL int nec_end_scan __PR((SCSI* scgp)); +LOCAL int nec_scan_one_interval __PR((SCSI* scgp, cxerror_t *pe, int addr, void *p)); +LOCAL int plextor_scan_one_interval __PR((SCSI* scgp, cxerror_t *pe, int addr, void *p)); + + +/* currently, plextor and nec cx scanning is supported */ +cx_scan_procedure_t nec_cx = { nec_init_cx_scan, nec_end_scan, nec_scan_one_interval }; +cx_scan_procedure_t plextor_cx = { plextor_init_cx_scan, plextor_end_scan, plextor_scan_one_interval }; + +#define cx_scan_procedure_count 2 +cx_scan_procedure_t *cx_scan_procedures[cx_scan_procedure_count] = { &plextor_cx, &nec_cx }; + struct exargs { SCSI *scgp; int old_secsize; @@ -116,7 +154,11 @@ LOCAL void get_sectype __PR((SCSI *scgp, long addr, char *st)); #endif + LOCAL void readc2_disk __PR((SCSI *scgp, parm_t *parmp)); +LOCAL void readcx_disk __PR((SCSI *scgp, parm_t *parmp)); +LOCAL int readcx_disk_plextor __PR((SCSI *scgp, parm_t *parmp)); +LOCAL int readcx_disk_nec __PR((SCSI *scgp, parm_t *parmp)); LOCAL int fread_data __PR((SCSI *scgp, rparm_t *rp, caddr_t bp, long addr, int cnt)); #ifdef CLONE_WRITE LOCAL int fread_2448 __PR((SCSI *scgp, rparm_t *rp, caddr_t bp, long addr, int cnt)); @@ -185,6 +227,7 @@ BOOL is_dvd; BOOL do_write; BOOL c2scan; +BOOL cxscan; BOOL fulltoc; BOOL clone; BOOL noerror; @@ -215,6 +258,7 @@ error("\tts=# set maximum transfer size for a single SCSI command\n"); error("\t-w Switch to write mode\n"); error("\t-c2scan Do a C2 error scan\n"); + error("\t-cxscan Do a C1/C2/CU scan (only available on a few drives)\n"); #ifdef CLONE_WRITE error("\t-fulltoc Retrieve the full TOC\n"); error("\t-clone Retrieve the full TOC and all data\n"); @@ -241,8 +285,7 @@ exit(ret); } -/* CSTYLED */ -char opts[] = "debug#,d+,kdebug#,kd#,timeout#,quiet,q,verbose+,v+,Verbose+,V+,x+,xd#,silent,s,help,h,version,scanbus,dev*,sectors*,w,c2scan,fulltoc,clone,noerror,nocorr,notrunc,retries#,factor,f*,speed#,ts&,overhead,meshpoints#"; +char opts[] = "debug#,d+,kdebug#,kd#,timeout#,quiet,q,verbose+,v+,Verbose+,V+,x+,xd#,silent,s,help,h,version,scanbus,dev*,sectors*,w,c2scan,cxscan,fulltoc,clone,noerror,nocorr,notrunc,retries#,factor,f*,speed#,ts&,overhead,meshpoints#"; EXPORT int main(ac, av) @@ -285,7 +328,7 @@ &silent, &silent, &help, &help, &pversion, &scanbus, &dev, §ors, &do_write, - &c2scan, + &c2scan, &cxscan, &fulltoc, &clone, &noerror, &nocorr, ¬runc, &retries, &do_factor, &filename, @@ -372,6 +415,8 @@ exit(err); } } else { + char errstr[80]; + if (scsibus == -1 && target >= 0 && lun >= 0) scsibus = 0; @@ -380,8 +425,17 @@ scgp->kdebug = kdebug; scg_settarget(scgp, scsibus, target, lun); - if (scg__open(scgp, NULL) <= 0) - comerr("Cannot open SCSI driver.\n"); + + scgp = scg_open(dev, errstr, sizeof(errstr), 0, 0); + + if (scgp == NULL) { + errmsg("%s%sCannot open SCSI driver.\n", errstr, errstr[0]?". ":""); + errmsgno(EX_BAD, "For possible targets try 'readcd -scanbus'.%s\n", + geteuid() ? " Make sure you are root.":""); + errmsgno(EX_BAD, "For possible transport specifiers try 'readcd dev=help'.\n"); + exit(EX_BAD); + } + } scgp->silent = silent; scgp->verbose = verbose; @@ -492,7 +546,7 @@ comerrno(EX_BAD, "Not root. Will only work on CD-ROM in suid mode\n"); } - if (filename || sectors || c2scan || meshpoints || fulltoc || clone) { + if (filename || sectors || c2scan || cxscan || meshpoints || fulltoc || clone) { dorw(scgp, filename, sectors); } else { doit(scgp); @@ -673,6 +727,13 @@ if (params.name == NULL) params.name = "/dev/null"; readc2_disk(scgp, ¶ms); + } else if (cxscan) { + noerror = TRUE; + if (retries == MAX_RETRY) + retries = 10; + if (params.name == NULL) + params.name = "/dev/null"; + readcx_disk(scgp, ¶ms); } else if (do_write) write_disk(scgp, ¶ms); else @@ -1101,6 +1162,74 @@ printf("C2 errors on worst sector: %d, sectors with 100+ C2 errors: %d\n", rp.c2_maxerrs, rp.c2_badsecs); } + +LOCAL void +readcx_disk(scgp, parmp) + SCSI *scgp; + parm_t *parmp; +{ + int initialized = 0; + int command_set_index; + int command_set_count; + int addr, end, seconds; + void* p; + cxerror_t errors; + cxerror_t stats; + cxerror_t max_errors; + + + read_capacity(scgp); + print_capacity(scgp, stderr); + + command_set_count = cx_scan_procedure_count; + for (command_set_index = 0; (command_set_index < command_set_count) && (!initialized); command_set_index++) { + if ((*cx_scan_procedures[command_set_index]->start_func)(scgp) >= 0) + initialized = 1; + } + command_set_index--; + + if (!initialized) + return; + + end = scgp->cap->c_baddr + 1; + addr = 0; + + seconds = (end - addr) / 75; + + p = malloc(65536); + fillbytes(&stats, sizeof (stats), '\0'); + fillbytes(&max_errors, sizeof (max_errors), '\0'); + + while (addr < end) { + addr = (*cx_scan_procedures[command_set_index]->one_interval_func)(scgp, &errors, addr, p); + stats.c1_errors += errors.c1_errors; + stats.c2_errors += errors.c2_errors; + stats.cu_errors += errors.cu_errors; + max_errors.c1_errors = max(max_errors.c1_errors, errors.c1_errors); + max_errors.c2_errors = max(max_errors.c2_errors, errors.c2_errors); + max_errors.cu_errors = max(max_errors.cu_errors, errors.cu_errors); + printf(" %3dm %02ds: C1: %4d, C2: %4d, CU: %4d\n", + addr/75/60, addr/75%60, errors.c1_errors, errors.c2_errors, errors.cu_errors); + if (didintr) { + free(p); + (*cx_scan_procedures[command_set_index]->end_func)(scgp); + comexit(exsig); + } + + } + + printf("\n\ntotal result:\n\n"); + printf("total: C1: %5d, C2: %5d, CU: %5d\n", stats.c1_errors, stats.c2_errors, stats.cu_errors); + printf("max : C1: %5d, C2: %5d, CU: %5d\n", max_errors.c1_errors, max_errors.c2_errors, max_errors.cu_errors); + printf("avg/s: C1: %5.1f, C2: %5.1f, CU: %5.1f\n\n", (float)stats.c1_errors/seconds, + (float)stats.c2_errors/seconds, (float)stats.cu_errors/seconds); + + free(p); + + (*cx_scan_procedures[command_set_index]->end_func)(scgp); +} + + /* ARGSUSED */ LOCAL int fread_data(scgp, rp, bp, addr, cnt) @@ -1926,6 +2055,8 @@ (cdb)->count[1] = ((len) >> 8L) & 0xFF,\ (cdb)->count[2] = (len) & 0xFF) + + EXPORT int read_da(scgp, bp, addr, cnt, framesize, subcode) SCSI *scgp; @@ -1955,6 +2086,221 @@ scgp->cmdname = "read_da"; return (scg_cmd(scgp)); +} + +LOCAL int +read_sectors(scgp, p, addr, cnt) + SCSI *scgp; + void *p; + int addr; + int cnt; +{ + int clusters; + int rest; + int i; + int pos; + + if (addr + cnt > scgp->cap->c_baddr + 1) + cnt = scgp->cap->c_baddr + 1 - addr; + + clusters = cnt / 15; + rest = cnt % 15; + pos = addr; + + for (i = 0; i < clusters; i++) { + read_cd(scgp, p, pos, 15, 2352 + 294, 0xFA, 0); + pos += 15; + } + + if (rest) + read_cd(scgp, p, pos, rest, 2352 + 294, 0xFA, 0); + + return (15 * clusters + rest); +} + +LOCAL int +plextor_init_cx_scan(scgp) + SCSI *scgp; +{ + register struct scg_cmd *scmd = scgp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->size = 0; + scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; + scmd->cdb_len = SC_G5_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.cmd_cdb[0] = 0xEA; + scmd->cdb.cmd_cdb[1] = 0x15; + scmd->cdb.cmd_cdb[3] = 0x01; + + scgp->cmdname = "plextor_init_cx_scan"; + + return (scg_cmd(scgp)); +} + +LOCAL int +nec_init_cx_scan(scgp) + SCSI *scgp; +{ + register struct scg_cmd *scmd = scgp->scmd; + int res; + + /* initialize scan mode */ + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->size = 0; + scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; + scmd->cdb_len = SC_G5_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.cmd_cdb[0] = 0xF3; + scmd->cdb.cmd_cdb[1] = 0x01; + scgp->cmdname = "nec_init_cx_scan"; + res = scg_cmd(scgp); + if (res < 0) + return (res); + + /* set scan interval = 75 sectors */ + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->size = 0; + scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; + scmd->cdb_len = SC_G5_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.cmd_cdb[0] = 0xF3; + scmd->cdb.cmd_cdb[1] = 0x02; + scmd->cdb.cmd_cdb[8] = 75; + scgp->cmdname = "nec_set_cx_scan_interval"; + res = scg_cmd(scgp); + if (res < 0) + return (res); + + return (res); +} + +LOCAL int +plextor_scan_one_interval(scgp, pe, addr, p) + SCSI* scgp; + cxerror_t *pe; + int addr; + void* p; +{ + int i; + + i = read_sectors(scgp, p, addr, 75); + plextor_read_cx_values(scgp, pe); + + return (addr + i); +} + +LOCAL int +nec_scan_one_interval(scgp, pe, addr, p) + SCSI *scgp; + cxerror_t *pe; + int addr; + void* p; +{ + register struct scg_cmd *scmd = scgp->scmd; + unsigned char data[8]; + int res; + fillbytes(data, sizeof (data), '\0'); + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->size = 8; + scmd->addr = data; + scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; + scmd->cdb_len = SC_G5_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.cmd_cdb[0] = 0xF3; + scmd->cdb.cmd_cdb[1] = 0x03; + scgp->cmdname = "nec_set_cx_scan_interval"; + res = scg_cmd(scgp); + + if (res < 0) + return (res); + + pe->c1_errors = a_to_u_2_byte(data+4); + pe->c2_errors = a_to_u_2_byte(data+6); + pe->cu_errors = 0; + + return ((int)data[1] * 4500 + (int)data[2] * 75 + (int)data[3]); +} + + +LOCAL int +plextor_end_scan(scgp) + SCSI *scgp; +{ + register struct scg_cmd *scmd = scgp->scmd; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->size = 0; + scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; + scmd->cdb_len = SC_G5_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.cmd_cdb[0] = 0xEA; + scmd->cdb.cmd_cdb[1] = 0x17; + + scgp->cmdname = "plextor_end_scan"; + + return (scg_cmd(scgp)); +} + +LOCAL int +nec_end_scan(scgp) + SCSI *scgp; +{ + register struct scg_cmd *scmd = scgp->scmd; + char data[8]; + int res; + fillbytes(data, sizeof (data), '\0'); + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + scmd->size = 8; + scmd->addr = data; + + scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; + scmd->cdb_len = SC_G5_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.cmd_cdb[0] = 0xF3; + scmd->cdb.cmd_cdb[1] = 0x0F; + scgp->cmdname = "nec_end_cx_scan"; + res = scg_cmd(scgp); + if (res < 0) + return (res); + +} + +LOCAL int +plextor_read_cx_values(scgp, pe) + SCSI *scgp; + cxerror_t *pe; +{ + register struct scg_cmd *scmd = scgp->scmd; + char data[0x1A]; + int ret; + + fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); + fillbytes(data, 0x1A, '\0'); + + scmd->addr = data; + scmd->size = 0x1A; + scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; + scmd->cdb_len = SC_G5_CDBLEN; + scmd->sense_len = CCS_SENSE_LEN; + scmd->cdb.cmd_cdb[0] = 0xEA; + scmd->cdb.cmd_cdb[1] = 0x16; + scmd->cdb.cmd_cdb[2] = 0x01; + scmd->cdb.cmd_cdb[10] = 0x1A; + scgp->cmdname = "plextor_read_cx_values"; + + ret = scg_cmd(scgp); + if (ret < 0) { + return (ret); + } + + pe->c1_errors = a_to_u_2_byte(data+16) + a_to_u_2_byte(data+14) + a_to_u_2_byte(data + 12); + pe->c2_errors = a_to_u_2_byte(data+22); + pe->cu_errors = a_to_u_2_byte(data+20); + + return (ret); } EXPORT int