--- readcd-old/readcd.c	2005-09-10 22:42:53.000000000 +0200
+++ readcd/readcd.c	2005-09-10 22:44:47.000000000 +0200
@@ -10,9 +10,8 @@
  */
 /*
  * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -86,6 +85,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 +153,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 +226,7 @@
 BOOL	is_dvd;
 BOOL	do_write;
 BOOL	c2scan;
+BOOL    cxscan;
 BOOL	fulltoc;
 BOOL	clone;
 BOOL	noerror;
@@ -215,6 +257,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,7 +284,7 @@
 	exit(ret);
 }	
 
-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)
@@ -284,7 +327,7 @@
 			&silent, &silent,
 			&help, &help, &pversion,
 			&scanbus, &dev, &sectors, &do_write,
-			&c2scan,
+			&c2scan, &cxscan,
 			&fulltoc, &clone,
 			&noerror, &nocorr,
 			&notrunc, &retries, &do_factor, &filename,
@@ -491,7 +534,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);
@@ -672,6 +715,13 @@
 		if (params.name == NULL)
 			params.name = "/dev/null";
 		readc2_disk(scgp, &params);
+	} else if (cxscan) {
+		noerror = TRUE;
+		if (retries == MAX_RETRY)
+			retries = 10;
+		if (params.name == NULL)
+			params.name = "/dev/null";
+		readcx_disk(scgp, &params);
 	} else if (do_write)
 		write_disk(scgp, &params);
 	else
@@ -1100,6 +1150,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)
@@ -1925,6 +2043,8 @@
 				(cdb)->count[1] = ((len) >> 8L) & 0xFF,\
 				(cdb)->count[2] = (len) & 0xFF)
 
+
+
 EXPORT int
 read_da(scgp, bp, addr, cnt, framesize, subcode)
 	SCSI	*scgp;
@@ -1956,6 +2076,221 @@
 	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
 read_cd(scgp, bp, addr, cnt, framesize, data, subch)
 	SCSI	*scgp;
