/*
 * This file is part of the QPxTool project.
 * Copyright (C) 2005-2006 Gennady "ShultZ" Kozlov <qpxtool@mail.ru>
 *
 * original version of CD-R(W) manufacturer identification code got from cdrecord, (C) Joerg Schilling
 *
 * 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 of the License, or
 * (at your option) any later version.
 * See the file "COPYING" for the exact licensing terms.
 *
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <unistd.h>
//#include <sys/time.h>

//#include "transport.hxx"
#include "qpx_mmc.h"

const unsigned int	bufsz_dev  = 0x0000FF;
const unsigned int	bufsz_rd   = 0x010000;
const unsigned int	bufsz_ATIP = 0x000800;

drive_info::drive_info(){
}

drive_info::drive_info(char* _device){
	device=(char*)malloc(bufsz_dev);
	strcpy(device,_device);
	rd_buf=(unsigned char*)malloc(bufsz_rd);
	ATIP=(unsigned char*)malloc(bufsz_ATIP);
	if (!cmd.associate(device, NULL)) {
//		printf("** Can't open device: %16s\n",_device);
		err=1;
		mmc=-1;
		return;
	}
	mmc=0;
	parms.interval=1;
	parms.tests=0;
	capabilities=0;
	rd_capabilities=0;
	wr_capabilities=0;
	ven_features=0;
	chk_features=0;
	plextor.gigarec=0;
	plextor.varirec_state_cd=0;
	plextor.varirec_pwr_cd=0;
	plextor.varirec_str_cd=0;
	plextor.varirec_state_dvd=0;
	plextor.varirec_pwr_dvd=0;
	plextor.varirec_str_dvd=0;
	plextor.powerec_state=0;
	plextor.plexeraser=0;
	ven_ID=0;
	dev_ID=0;
	iface_id=0;
	iface[0]=0;
	loader_id=0;
	parms.scan_speed_cd=8;
	parms.scan_speed_dvd=5;
	parms.spindown_idx=0;
	silent=0;
}

drive_info::~drive_info(){
//	delete urd_buf;
	busy=1;
//	delete pthread_t;
	delete rd_buf;
	delete ATIP;
	delete device;
}

void drivecpy(drive_info* dst, drive_info* src){
	dst->device=(char*)malloc(bufsz_dev);
//		printf("Can't copy device:(\n");
	memcpy(dst->device, src->device,bufsz_dev);
//		printf("Can't copy device:(\n");
//	if (!dst->cmd.associate(dst->device, NULL)) {
//		dst->mmc=-1;
//		printf("Can't copy device:(\n");
//		return;
//	}
	dst->mmc=src->mmc;
//	dst->memcpy(dst, src, 0xFF);
	dst->rd_buf=(unsigned char*)malloc(bufsz_rd);
	dst->ATIP=(unsigned char*)malloc(bufsz_ATIP);
	dst->ATIP_len = src->ATIP_len;
	dst->ven_ID=src->ven_ID;
	memcpy(dst->ven,src->ven,9);
	dst->dev_ID=src->dev_ID;
	memcpy(dst->dev,src->dev,17);
	memcpy(dst->fw,src->fw,5);
	memcpy(dst->serial,src->serial,17);
	memcpy(dst->TLA,src->TLA,5);
	dst->buffer_size = src->buffer_size;
	dst->capabilities = src->capabilities;
	dst->rd_capabilities = src->rd_capabilities;
	dst->wr_capabilities = src->wr_capabilities;
	dst->ven_features = src->ven_features;
	dst->chk_features = src->chk_features;

	dst->iface_id = src->iface_id;
	memcpy(dst->iface,src->iface,sizeof(str_if));
	dst->loader_id = src->loader_id;

	dst->book_plus_r = src->book_plus_r;
	dst->book_plus_rw = src->book_plus_rw;
	dst->book_plus_rdl = src->book_plus_rdl;

	memcpy(&(dst->plextor),&(src->plextor),sizeof(plex_features));
	memcpy(&(dst->astrategy),&(src->astrategy),sizeof(plex_as));
	memcpy(&(dst->plextor_silent),&(src->plextor_silent),sizeof(plex_silent));
	memcpy(&(dst->media),&(src->media),sizeof(media_info));
	memcpy(&(dst->parms),&(src->parms),sizeof(drive_parms));
	memcpy(&(dst->perf),&(src->perf),sizeof(perf_desc));
//	memcpy(dst->,src->,sizeof());

	dst->parms.tests = src->parms.tests;
	dst->parms.interval = src->parms.interval;
	dst->parms.scan_speed_cd = src->parms.scan_speed_cd;
	dst->parms.scan_speed_dvd = src->parms.scan_speed_dvd;

//	dst->media.type = src->media.type;
//	memcpy(dst->media.MID,src->media.MID,32);
//	dst->media.disc_type = src->media.disc_type;
//	dst->media.book_type = src->media.book_type;
//	dst->media.layers = src->media.layers;
	dst->media.capacity = src->media.capacity;

	dst->silent = src->silent;
}

void drive_info::cmd_clear(){
	for (int i=0; i<12; i++) cmd[i]=0;
}

/*
bool drive_info::lock(){
	if (!busy) {busy=true; return true; }
	else { return false; }
}

bool drive_info::unlock(){
	busy=false;
	return true;
}

bool drive_info::isBusy(){
	return busy;
}

void drive_info::wait_free(){
	while (busy);
}
*/

void spinup(drive_info* drive, unsigned char secs) {
	long st, et;
	char use_readcd = 0;
	const int  addt = 250;
	int i = 0;
	st = getmsecs() + addt;
	if ((drive->media.disc_type & DISC_CD) && (drive->capabilities & CAP_DAE)) use_readcd = 1;
	printf("SpinUp using READ%s command...\n", use_readcd ? " CD" : "" );
	seek(drive, 0);
//	if (use_readcd) read_cd(drive, 0, 1, 0xF8, 1);
	for ( et = getmsecs(); (et-st) < (secs*1000); et = getmsecs() ) {
		printf("Remaining: %.3f sec...\r", secs - ((et-st) / 1000.0));
		if (use_readcd) read_cd(drive, i*15, 15, 0xF8);
		else		read(drive, i*15, 15);
		i++;
	}
	seek(drive, 0);
	if (use_readcd)	read_cd(drive, 0, 1, 0xF8);
	else		read(drive, 0, 1);
	seek(drive, 0);
	usleep( addt );
}

int inquiry(drive_info* drive) {
	char	data[36];	
	if (drive->mmc == -1) return ERR_NO_DEV;
	drive->cmd_clear();
	drive->cmd[0] = MMC_INQUIRY;
	drive->cmd[4] = 36;
	drive->cmd[5] = 0;
	drive->err=drive->cmd.transport(READ,data,36);
	if (drive->err) return ERR_NO_SCSI;
	memcpy(drive->ven,data+8,8);
	drive->ven[8] = 0;
	memcpy(drive->dev,data+16,16);
	drive->dev[16] = 0;
	memcpy(drive->fw,data+32,4);
	drive->fw[4] = 0;
	if ((data[0]&0x1F) != 5) return ERR_NO_MMC;
	drive->mmc=1;
	return 0;
}

int test_unit_ready(drive_info* drive) {
	drive->cmd_clear();
	drive->cmd[0] = MMC_TEST_UNIT_READY;
	if ((drive->err=drive->cmd.transport(NONE, NULL, 0) )) return drive->err;
	return 0;
}

int request_sense(drive_info* drive, char add){
	drive->cmd_clear();
	drive->cmd[0]= MMC_REQUEST_SENSE;
	drive->cmd[4]= 0x12+add;
	if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,0x12) ))
		{sperror ("REQUEST_SENSE",drive->err); return (drive->err); }
	return 0;
}

/*
int mode_sense6(drive_info* drive, int page, int page_control, short dest_len) {
	drive->cmd_clear();
	drive->cmd[0]=MMC_MODE_SENSE6;
	drive->cmd[1]= 0x10;
	drive->cmd[2]=page_control << 6 | page;
	drive->cmd[4]=dest_len & 0xFF;
	if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,dest_len) ))
		{sperror ("MODE_SENSE(6)",drive->err); return (drive->err); }
	return 0;
}
*/

int mode_sense(drive_info* drive, int page, int page_control, int dest_len) {
	drive->cmd_clear();
	drive->cmd[0]=MMC_MODE_SENSE10;
	drive->cmd[2]=page_control << 6 | page;
	drive->cmd[7]=(dest_len >> 8) & 0xFF;
	drive->cmd[8]=dest_len & 0xFF;
	if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,dest_len) ))
		{sperror ("MODE_SENSE(10)",drive->err); return (drive->err); }
	return 0;
}

/*
int mode_select6(drive_info* drive, short dest_len) {
	drive->cmd_clear();
	drive->cmd[0]=MMC_MODE_SELECT6;
//	drive->cmd[1]= 0x10;
	drive->cmd[4]=dest_len & 0xFF;
	if ((drive->err=drive->cmd.transport(WRITE,drive->rd_buf,dest_len) ))
		{sperror ("MODE_SELECT(6)",drive->err); return (drive->err); }
	return 0;
}
*/

int mode_select(drive_info* drive, int dest_len) {
	drive->cmd_clear();
	drive->cmd[0]=MMC_MODE_SELECT10;
	drive->cmd[1]= 0x10;
	drive->cmd[7]=(dest_len >> 8) & 0xFF;
	drive->cmd[8]=dest_len & 0xFF;
	if ((drive->err=drive->cmd.transport(WRITE,drive->rd_buf,dest_len) ))
		{sperror ("MODE_SELECT(10)",drive->err); return (drive->err); }
	return 0;
}

int get_configuration(drive_info* drive, int feature_number, unsigned int* data_length, int* current, unsigned char ReqType = 0x02) {
	if (data_length) *data_length = 0;
	if (current) *current = 0;
	drive->cmd_clear();
	drive->cmd[0] = MMC_GET_CONFIGURATION;
	drive->cmd[1] = ReqType;
	drive->cmd[2] = (feature_number >> 8) & 0xFF;
	drive->cmd[3] = feature_number & 0xFF;
	drive->cmd[7] = 0;
	drive->cmd[8] = 8;
	if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,8)))
		{sperror ("GET_CONFIGURATION LENGTH",drive->err);
		return (drive->err);}
	if (data_length)
	{
		*data_length = swap4(drive->rd_buf);
//		*data_length = (drive->rd_buf[0]<<24) | (drive->rd_buf[1]<<16) | (drive->rd_buf[2]<<8) | drive->rd_buf[3];
		drive->cmd[7] = ((*data_length+4) >> 8) & 0xFF;
		drive->cmd[8] = (*data_length+4) & 0xFF;
		if (*data_length > 4)
			if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,*data_length+4)))
				{sperror ("GET_CONFIGURATION",drive->err);
				return (drive->err);}
	}
	if (current) *current = drive->rd_buf[10] & 0x01;
	return 0;
}

void detect_iface(drive_info* drive){
	unsigned int len=0;
	get_configuration(drive, FEATURE_CORE, &len, NULL);
	drive->iface_id= (drive->rd_buf[12] << 12) | (drive->rd_buf[13] << 8) | (drive->rd_buf[14] << 4) | drive->rd_buf[15];
	if (drive->iface_id<iface_id_max)
		strcpy(drive->iface,iface_list[drive->iface_id]);
	else
		strcpy(drive->iface,iface_list[iface_id_max+1]);
}

int get_mode_pages_list(drive_info* drive) {
	unsigned int len, i, ii;
	unsigned char ml=0, mn=0;
	if (!drive->silent) printf("\n** Reading supported mode pages...\n");
	if (mode_sense(drive, 0x3F, 2, 0x4000)) return 1;
	len = swap2u (drive->rd_buf);
//	printf("data len: %4X (%4d), Header:\n", len, len);
//	for (i=0; i<8; i++) printf(" 0x%02X",drive->rd_buf[i] & 0xFF); printf("\n");
	for (i=8; i<len; i+=ml) {
		mn = drive->rd_buf[i] & 0x3F;
		ml = drive->rd_buf[i+1] & 0xFF;
		ii = 0;
		while ((MODE_PAGES[ii].id != mn) && (MODE_PAGES[ii].id < 0x3F)) ii++;
		if (!drive->silent) {
			printf("Mode Page: 0x%02X [%s]", mn, MODE_PAGES[ii].name);
//			for (ii=0; ii<(ml+2); ii++) { if (!(ii%32)) printf("\n"); printf(" %02X",drive->rd_buf[i+ii] & 0xFF); }
			printf("\n");
		}
		ml += 2;
	}
	return 0;
}

int get_features_list(drive_info* drive) {
	unsigned int len, i, ii;
	unsigned short fn;
	unsigned char  fl;
	if (!drive->silent) printf("\n** Reading supported features...\n");
	if (get_configuration(drive, 0 , &len, NULL, 0)) return 1;
//	printf("data len: %4X (%4d), Header:\n", len, len);
//	for (i=0; i<8; i++) printf(" 0x%02X",drive->rd_buf[i] & 0xFF); printf("\n");

	for (i=8; i<len; i+=fl) {
		fn = swap2u (drive->rd_buf+i);
		fl = drive->rd_buf[i+3];
		ii = 0;
		while ((FEATURES[ii].id != fn) && (FEATURES[ii].id < 0xFFFF)) ii++;
		if (!drive->silent) {
			printf("Feature: 0x%04X [%s]", fn, FEATURES[ii].name);
//			for (ii=0; ii<(fl+4); ii++) { if (!(ii%32)) printf("\n"); printf(" %02X",drive->rd_buf[i+ii] & 0xFF); }
			printf("\n");
		}
		fl += 4;
	}
	return 0;
}

int get_profiles_list(drive_info* drive) {
	unsigned int len, i, ii;
	unsigned short profile;
	if (!drive->silent) printf("\n** Reading supported profiles...\n");
	if (get_configuration(drive, FEATURE_PROFILE_LIST , &len, NULL)) return 1;

	for( i = 0; i < len-8; i+=4 ) {
		profile = swap2u (drive->rd_buf+i+12);
		ii=0;
		while ((PROFILES[ii].id != profile) && (PROFILES[ii].id < 0xFFFF)) ii++;
		if (!drive->silent) printf("Profile: 0x%04X [%s]\n", profile, PROFILES[ii].name);

		switch (profile) {
			case 0x10:						// DVD-ROM
				drive->rd_capabilities |= DEVICE_DVD_ROM;
				break;
			case 0x11:						// DVD-R Sequential
				drive->rd_capabilities |= DEVICE_DVD_R;
				drive->wr_capabilities |= DEVICE_DVD_R;
				break;
			case 0x12:						// DVD-RAM
				drive->rd_capabilities |= DEVICE_DVD_RAM;
				drive->wr_capabilities |= DEVICE_DVD_RAM;
				break;
			case 0x13:						// DVD-RW Restricted Overwrite
				drive->rd_capabilities |= DEVICE_DVD_RW;
				drive->wr_capabilities |= DEVICE_DVD_RW;
				break;
			case 0x14:						// DVD-RW Sequential
				drive->rd_capabilities |= DEVICE_DVD_RW;
				drive->wr_capabilities |= DEVICE_DVD_RW;
				break;
			case 0x15:						// DVD-R DL Sequential
				drive->rd_capabilities |= (DEVICE_DVD_R_DL);
				drive->wr_capabilities |= (DEVICE_DVD_R_DL);
				break;
			case 0x16:						// DVD-R DL Layer Jump
				drive->rd_capabilities |= (DEVICE_DVD_R_DL);
				drive->wr_capabilities |= (DEVICE_DVD_R_DL);
				break;
			case 0x1A:						// DVD+RW
				drive->rd_capabilities |= DEVICE_DVD_PLUS_RW;
				drive->wr_capabilities |= DEVICE_DVD_PLUS_RW;
				break;
			case 0x1B:						// DVD+R
				drive->rd_capabilities |= DEVICE_DVD_PLUS_R;
				drive->wr_capabilities |= DEVICE_DVD_PLUS_R;
				break;
			case 0x2B:						// DVD+R DL
				drive->rd_capabilities |= (DEVICE_DVD_PLUS_R_DL);
				drive->wr_capabilities |= (DEVICE_DVD_PLUS_R_DL);
				break;
			case 0x08:						// CD-ROM
				drive->rd_capabilities |= DEVICE_CD_ROM;
				drive->wr_capabilities |= DEVICE_CD_ROM;
				break;
			case 0x09:						// CD-R
				drive->rd_capabilities |= DEVICE_CD_R;
				drive->wr_capabilities |= DEVICE_CD_R;
				break;
			case 0x0A:						// CD-RW
				drive->rd_capabilities |= DEVICE_CD_RW;
				drive->wr_capabilities |= DEVICE_CD_RW;
				break;
		}
	}
	profile = swap2(drive->rd_buf+6);
	ii=0;
	while ((PROFILES[ii].id != profile) && (PROFILES[ii].id < 0xFFFF)) ii++;
	if (!drive->silent) printf("Current: 0x%04X [%s]\n", profile, PROFILES[ii].name);

	return 0;
}

void detect_capabilities(drive_info* drive){
	unsigned int	len=4;
	drive->capabilities=CAP_SET_CD_SPEED;
	drive->rd_capabilities=0;
	drive->wr_capabilities=0;
	detect_mm_capabilities(drive);
	get_mode_pages_list(drive);
	if (drive->mmc>1) {
		// LU is MMC2 or later. Detecting capabilities by GET_CONFIGURATION
		get_profiles_list(drive);
		get_features_list(drive);
		get_configuration(drive, FEATURE_REMOVABLE_MEDIA , &len, NULL);
		if (len >= 0x0C) drive->capabilities|=CAP_REMOVABLE_MEDIA;
		get_configuration(drive, FEATURE_SMART , &len, NULL);
		if (len >= 0x08) drive->capabilities|=CAP_SMART;
		get_configuration(drive, FEATURE_MICROCODE_UPGRADE , &len, NULL);
		if (len >= 0x08) drive->capabilities|=CAP_MICROCODE_UPGRADE;
		get_configuration(drive, FEATURE_MORPHING , &len, NULL);
		if (len >= 0x08) drive->capabilities|=CAP_MORPHING;
		get_configuration(drive, FEATURE_POWER_MANAGEMENT , &len, NULL);
		if (len >= 0x08) drive->capabilities|=CAP_POWER_MANAGEMENT;
		get_configuration(drive, FEATURE_EMBEDDED_CHANGER , &len, NULL);
		if (len >= 0x08) drive->capabilities|=CAP_EMBEDDED_CHANGER;
		get_configuration(drive, FEATURE_DEFECT_MANAGEMENT , &len, NULL);
		if (len >= 0x08) drive->capabilities|=CAP_DEFECT_MANAGEMENT;
		get_configuration(drive, FEATURE_REAL_TIME_STREAMING , &len, NULL);
		if (len >= 0x08) drive->capabilities|=CAP_REAL_TIME_STREAMING;
		get_configuration(drive, FEATURE_MRW , &len, NULL);
		if (len >= 0x0C) {
			drive->rd_capabilities|=DEVICE_MRW;
			if (drive->rd_buf[12]&0x01) drive->wr_capabilities|=DEVICE_MRW;
		}
		get_configuration(drive, FEATURE_CD_READ , &len, NULL); // LU can Reed/Write CD's
		if (len >= 0x0C) {
//			drive->rd_capabilities|=DEVICE_CD_ROM;
			if (drive->rd_buf[12]&0x1) drive->capabilities|=CAP_CD_TEXT;
			if (drive->rd_buf[12]&0x2) drive->capabilities|=CAP_C2;
		}
		get_configuration(drive, FEATURE_DVD_READ , &len, NULL); // LU can Reed/Write DVD's
		if (len >= 0x08) {
			if (drive->rd_buf[14]&0x01) drive->rd_capabilities|=DEVICE_DVD_R_DL;
			get_configuration(drive, FEATURE_DVD_CPRM , &len, NULL);
			if (len >= 0x08) drive->capabilities|=CAP_DVD_CPRM;
			get_configuration(drive, FEATURE_DVD_CSS , &len, NULL);
			if (len >= 0x08) drive->capabilities|=CAP_DVD_CSS;
			get_configuration(drive, FEATURE_DVD_R_RW_WRITE , &len, NULL);
			if (len >= 0x08) {
				drive->wr_capabilities|=DEVICE_DVD_R;
				if (drive->rd_buf[12]&0x02) drive->wr_capabilities|=DEVICE_DVD_RW;
//				if (drive->rd_buf[12]&0x04) drive->wr_capabilities|=DEVICE_DVD_R_DL;
			}
			get_configuration(drive, FEATURE_LAYER_JUMP_RECORDING , &len, NULL);
			if (len >= 0x08) drive->wr_capabilities|=DEVICE_DVD_R_DL;

			get_configuration(drive, FEATURE_DVD_PLUS_R , &len, NULL);
			if (len >= 0x08) {
// 				drive->mmc=4;
				drive->rd_capabilities|=DEVICE_DVD_PLUS_R;
				if (drive->rd_buf[12]&0x01) drive->wr_capabilities|=DEVICE_DVD_PLUS_R;
			}
			get_configuration(drive, FEATURE_DVD_PLUS_RW , &len, NULL);
			if (len >= 0x0C) {
				drive->rd_capabilities|=DEVICE_DVD_PLUS_RW;
				if (drive->rd_buf[12]&0x01) drive->wr_capabilities|=DEVICE_DVD_PLUS_RW;
			}
			get_configuration(drive, FEATURE_DVD_PLUS_R_DOUBLE_LAYER , &len, NULL);
			if (len >= 0x0C) {
// 				drive->mmc=5;
				drive->rd_capabilities|=DEVICE_DVD_PLUS_R_DL;
				if (drive->rd_buf[12]&0x01) drive->wr_capabilities|=DEVICE_DVD_PLUS_R_DL;
			}
		}
		get_configuration(drive, FEATURE_DDCD_READ , &len, NULL);  // LU can Reed/Write DDCD's
		if (len >= 0x0C) {
			drive->rd_capabilities|=DEVICE_DDCD_R;
			drive->rd_capabilities|=DEVICE_DDCD_RW;
			get_configuration(drive, FEATURE_DDCD_R_WRITE , &len, NULL);
			if (len >= 0x0C) drive->wr_capabilities|=DEVICE_DDCD_R;
			get_configuration(drive, FEATURE_DDCD_RW_WRITE , &len, NULL);
			if (len >= 0x0C) drive->wr_capabilities|=DEVICE_DDCD_RW;
		}
	if (!drive->silent) printf("** Device is MMC-%d\n",drive->mmc);
	}
}

int read_atip(drive_info* drive) {
	unsigned char	data[4];
	int 	size = 0;
	int	i;
	drive->ATIP_len = 0;
	drive->cmd_clear();
	drive->cmd[0]=MMC_READ_TOC_PMA_ATIP;
	drive->cmd[1]=0;
	drive->cmd[2]=  4; // ATIP
	drive->cmd[3]=0;
	drive->cmd[8]=4;
	if ((drive->err=drive->cmd.transport(READ,data,4) ))
		{if (!drive->silent) sperror ("READ_ATIP",drive->err); drive->ATIP_len = 0; return 1;}
	size = swap2u((char*)data);
	size += 2;
	drive->cmd_clear();
	drive->cmd[0]=MMC_READ_TOC_PMA_ATIP;
	drive->cmd[1]=0;
	drive->cmd[2]=  4; // ATIP
	drive->cmd[3]=0;
	drive->cmd[7]=(size >> 8) & 0xFF;
	drive->cmd[8]=size & 0xFF;
	if ((drive->err=drive->cmd.transport(READ,drive->ATIP,size) ))
		{sperror ("READ_ATIP",drive->err); drive->ATIP_len = 0; return 1;}
	drive->ATIP_len = size;
	if (!drive->silent) {
		printf("ATIP (%d bytes):\n",size);
		for (i=0; i<(min(size, 4)); i++) printf(" %3d (%02X)",drive->ATIP[i],drive->ATIP[i]);
		if (size > 4) for (i=0; i<(size-4); i++) {
			if (!(i % 8)) printf("\n");
			else if (!(i % 4)) printf("      ");
			printf(" %3d (%02X)",drive->ATIP[i+4] & 0xFF,drive->ATIP[i+4] & 0xFF);
		}
		printf("\n");
	}
	return 0;
}

int read_toc(drive_info* drive) {
	unsigned char	data[4];
	int 	size = 0;
	int	i;
	drive->cmd_clear();
	drive->cmd[0]=MMC_READ_TOC_PMA_ATIP;
	drive->cmd[1]=0;
	drive->cmd[2]=  0; // TOC
	drive->cmd[3]=0;
	drive->cmd[8]=4;
	if ((drive->err=drive->cmd.transport(READ,data,4) ))
		{sperror ("READ_TOC",drive->err); return 1;}
//	if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,2048) ))
//		{sperror ("READ_TOC",drive->err); return 1;}
	size = swap2u((char*)data);
	size += 2;

	drive->cmd_clear();
	drive->cmd[0]=MMC_READ_TOC_PMA_ATIP;
	drive->cmd[1]=0;
	drive->cmd[2]=  0; // TOC
	drive->cmd[3]=0;
	drive->cmd[7]=(size >> 8) & 0xFF;
	drive->cmd[8]=size & 0xFF;
	if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,size) ))
		{if (!drive->silent) sperror ("READ_TOC",drive->err); return 1;}

	if (!drive->silent) {
		printf("TOC (%d bytes):\n",size);
		for (i=0; i<(min(size, 4)); i++) printf(" %3d (%02X)",drive->rd_buf[i] & 0xFF,drive->rd_buf[i] & 0xFF);
		if (size > 4) for (i=0; i<(size-4); i++) {
			if (!(i % 8)) printf("\n");
			else if (!(i % 4)) printf("      ");
			printf(" %3d (%02X)",drive->rd_buf[i+4] & 0xFF,drive->rd_buf[i+4] & 0xFF);
		}
		printf("\n");
	}
	return 0;
}

int read_capacity(drive_info* drive) {
	unsigned char data[8]; memset(data, 0, 8);
	unsigned int ccity,bsize;
	drive->cmd_clear();
	drive->cmd[0] = MMC_READ_RECORDED_CAPACITY;
	drive->cmd[9] = 0;
	drive->cmd.transport (READ,data,8);
	ccity = data[0]<<24|data[1]<<16|data[2]<<8|data[3], ccity++;
        bsize = data[4]<<24|data[5]<<16|data[6]<<8|data[7];
	drive->media.capacity = ccity; //capacity in blocks
	lba2msf(&drive->media.capacity,&drive->media.capacity_msf);
	drive->media.sectsize = bsize; //block size
        return 0;
}

int read_track_info(drive_info* drive, trk* track, unsigned int track_n){
	int size = 2048;
	drive->cmd_clear();
	drive->cmd[0] = MMC_READ_TRACK_INFORMATION;
	drive->cmd[1] = 0x01;
	drive->cmd[2] = (track_n >> 24) & 0xFF;
	drive->cmd[3] = (track_n >> 16) & 0xFF;
	drive->cmd[4] = (track_n >> 8) & 0xFF;
	drive->cmd[5] = track_n & 0xFF ;
	drive->cmd[7] = (size >> 8) & 0xFF ;
	drive->cmd[8] = size & 0xFF ;
	if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,size) ))
		{if (!drive->silent) sperror ("READ_TRACK_INFO",drive->err); return 1;}
	
//	int i,
	int len;
	len = ( drive->rd_buf[0] << 8 ) | drive->rd_buf[1];
/*	printf("\nTrack #%d info:\n  ",track->n);
	for (i=0; i<len+2; i++) { printf(" 0x%02X",drive->rd_buf[i] & 0xFF); if (!((i+1)%8)) printf("\n  ");}
	if (((i)%8)) printf("\n");*/

	track->n = ((drive->rd_buf[32]&0xFF) << 8) | (drive->rd_buf[2]&0xFF);
	track->session = ((drive->rd_buf[33]&0xFF) << 8) | (drive->rd_buf[3]&0xFF);
	track->track_mode = drive->rd_buf[5] & 0x0F;
	track->data_mode = drive->rd_buf[6] & 0x0F;
	track->start = ((drive->rd_buf[8]&0xFF) << 24) | ((drive->rd_buf[9]&0xFF) << 16) |
		((drive->rd_buf[10]&0xFF) << 8) | (drive->rd_buf[11]&0xFF);
	track->next_writable = ((drive->rd_buf[12]&0xFF) << 24) | ((drive->rd_buf[13]&0xFF) << 16) |
		((drive->rd_buf[14]&0xFF) << 8) | (drive->rd_buf[15]&0xFF);
	track->free = ((drive->rd_buf[16]&0xFF) << 24) | ((drive->rd_buf[17]&0xFF) << 16) |
		((drive->rd_buf[18]&0xFF) << 8) | (drive->rd_buf[19]&0xFF);
	track->size = ((drive->rd_buf[24]&0xFF) << 24) | ((drive->rd_buf[25]&0xFF) << 16) |
		((drive->rd_buf[26]&0xFF) << 8) | (drive->rd_buf[27]&0xFF);
	track->last_recorded = ((drive->rd_buf[28]&0xFF) << 24) | ((drive->rd_buf[29]&0xFF) << 16) |
		((drive->rd_buf[30]&0xFF) << 8) | (drive->rd_buf[31]&0xFF);
	track->end = track->start+track->size-1;

	lba2msf(&track->start,&track->msf_start);
	lba2msf(&track->next_writable,&track->msf_next);
	lba2msf(&track->last_recorded,&track->msf_last);
	lba2msf(&track->end,&track->msf_end);
	lba2msf(&track->size,&track->time);

	return 0;
}

int get_track_list(drive_info* drive){
	int i;
	if (drive->media.disc_type & DISC_CD) {
		if (!drive->silent) printf("Get CD track list\n");
		if (read_toc(drive)) {
			printf("Error reading TOC :(\n");
			drive->media.tracks = 0;
		} else {
//			drive->media.tracks = 0;
			drive->media.tracks = drive->rd_buf[3] & 0xFF;
			for (i=0; i<drive->media.tracks; i++) {
				drive->media.track[i].n = i+1;
				drive->media.track[i].session = 1;
				drive->media.track[i].start = swap4(drive->rd_buf+i*8+8);
				drive->media.track[i].end   = swap4(drive->rd_buf+i*8+16);
				drive->media.track[i].size = drive->media.track[i].end - drive->media.track[i].start;
				drive->media.track[i].free = 0;
				drive->media.track[i].last_recorded = 0;
				drive->media.track[i].track_mode = drive->rd_buf[i*8+5] & 0x0F;
//				drive->media.track[i].data_mode = 0;
				drive->media.track[i].data_mode = (drive->rd_buf[i*8+5] >> 4) & 0x0F;

				lba2msf(&drive->media.track[i].start, &drive->media.track[i].msf_start);
				lba2msf(&drive->media.track[i].next_writable, &drive->media.track[i].msf_next);
				lba2msf(&drive->media.track[i].last_recorded, &drive->media.track[i].msf_last);
				lba2msf(&drive->media.track[i].end,  &drive->media.track[i].msf_end);
				lba2msf(&drive->media.track[i].size, &drive->media.track[i].time);
			}
			printf("tracks: %d\n",drive->media.tracks);
		}
	} else if (drive->media.disc_type & DISC_DVD) {
		printf("Get DVD track list\n");
		for (i=0; i<drive->media.tracks; i++) {
//			track.n = ++i;
			read_track_info(drive, &drive->media.track[i], i+1);

		}
		if (!drive->silent) printf("tracks: %d\n",drive->media.tracks);
	}
	if ((drive->media.tracks) && (!drive->silent)) for (i=0; i<drive->media.tracks; i++) {
			printf("\nTrack #  : %d\n",   drive->media.track[i].n);
			printf("Session #: %d\n",     drive->media.track[i].session);
			printf("Track mode    : %d\n",drive->media.track[i].track_mode);
			printf("Data mode     : %d\n",drive->media.track[i].data_mode);
			printf("Track start   : %d\n",drive->media.track[i].start);
			printf("Next writable : %d\n",drive->media.track[i].next_writable);
			printf("Free          : %d\n",drive->media.track[i].free);
			printf("Size          : %d\n",drive->media.track[i].size);
			printf("Last recorded : %d\n",drive->media.track[i].last_recorded);
	}
	return 0;
}

int read_free(drive_info* drive) {
	trk track;
	if (drive->media.dstatus == 2) {
		drive->media.capacity_free = 0;
		return 0;
	}
	if (drive->media.disc_type & DISC_CD) {
		if (((drive->media.last_lead_out >> 24) & 0xFF ) == 0xFF) {
			drive->media.capacity_free = 0;
			return 0;
		} else {
			drive->media.capacity_free = drive->media.last_lead_out - drive->media.capacity - 150;
			lba2msf(&drive->media.capacity_free,&drive->media.capacity_free_msf);
			return 0;
		}
	} else if (drive->media.disc_type & DISC_DVD) {
//		track.n = drive->media.sessions+1;
		read_track_info(drive, &track, drive->media.sessions+1);
		drive->media.capacity_free = track.free;
		return 0;
	}
	drive->media.capacity_free = 0;
	return 1;
}

int read_disc_information(drive_info* drive) {
	int i=0,len=0;
	drive->cmd_clear();
	drive->cmd[0] = MMC_READ_DISC_INFORMATION;
	drive->cmd[7] = 0x08;
	drive->cmd[8] = 0x00;
	drive->cmd.transport (READ,drive->rd_buf,2048);
	len= (drive->rd_buf[0]<<8)|drive->rd_buf[1];
	if (!drive->silent) printf("Disc info length: 0x%04X\n  ",len);
	if (len!=0x20) {
		drive->media.erasable = 0;
		drive->media.dstatus = 0;
		drive->media.sstatus = 0;
		drive->media.sessions = 0;
		drive->media.tracks = 0;
		return 1;
	}
	if (!drive->silent) for (i=0; i<len+2; i++) {
		printf(" 0x%02X",drive->rd_buf[i] & 0xFF);
		if (!((i+1)%8)) printf("\n  ");
	}
	if (((i)%8)) printf("\n");
	drive->media.erasable = (drive->rd_buf[2]&0x10);
	drive->media.dstatus = drive->rd_buf[2]&0x03;
	drive->media.sstatus = (drive->rd_buf[2]>>2)&0x03;
	drive->media.sessions = (drive->rd_buf[4]|(drive->rd_buf[9]<<8));
// 	if (!drive->media.sstatus) drive->media.sessions--;
	drive->media.tracks = drive->rd_buf[6]|(drive->rd_buf[11]<<8);
	if (!drive->silent) {
		printf("   first track# on disc: %d\n", drive->rd_buf[3]);
		printf("   first track# in last session: %d\n", drive->rd_buf[5]|(drive->rd_buf[10]<<8));
		printf("   last  track# in last session: %d\n", drive->media.tracks);
		printf("   disc type: %02X\n", drive->rd_buf[8]&0xFF);
		printf("   disc ID: %02X%02X%02X%02X\n",
			drive->rd_buf[12],drive->rd_buf[13],drive->rd_buf[14],drive->rd_buf[15]);
		printf("   Last session  lead-in  start: %d:%02d.%02d\n",
			(drive->rd_buf[16]<<8)|drive->rd_buf[17],drive->rd_buf[18],drive->rd_buf[19]);
		drive->media.last_lead_out = (((drive->rd_buf[20]&0xFF) << 8) | (drive->rd_buf[21]&0xFF))*75*60 + ((drive->rd_buf[22]&0xFF)*75) + (drive->rd_buf[23]&0xFF);
		printf("   Last possible lead-out start: %d:%02d.%02d (sector 0x%08X)\n",
			(drive->rd_buf[20]<<8)|drive->rd_buf[21],drive->rd_buf[22],drive->rd_buf[23],drive->media.last_lead_out);
	}
	if (!drive->media.sstatus) {
		drive->media.sessions--;
		drive->media.tracks--;
	}
	return 0;
}

void read_disc_regions(drive_info* drive){
//#warning "read_disc_regions()"
	int len=8;
	unsigned char enc;
	unsigned char regmask;
	int i;
	drive->cmd_clear();
	drive->cmd[0] = MMC_READ_DVD_STRUCTURE;
	drive->cmd[7] = 0x01;
	drive->cmd[8] = len>>8;
	drive->cmd[9] = len & 0xFF;
	drive->cmd[11] = 0;
	if (( drive->err = drive->cmd.transport(READ,drive->rd_buf,len) ))
		{ if (!drive->silent) sperror ("READ_DISC_REGIONS",drive->err); return; }

	if (!drive->silent) {
		printf("READ_DISC_REGIONS data: ");
		for(i=0; i<len ;i++) printf(" %02X",drive->rd_buf[i] & 0xFF);
		printf("\n");
	}

	enc = drive->rd_buf[5];
	regmask = drive->rd_buf[5];

	printf("Disc is %sprotected\n", enc ? "" : "NOT ");
	printf("Disc regions       : ");
	if (regmask != 0xFF) {
		for (i=0; i<8; i++)
			if (!((regmask >> i) & 1))
				{printf("%d",i+1); drive->rpc.region = i+1;}
		printf("\n");
	} else
		printf("does not set\n");
}

void read_rpc_state(drive_info* drive){
	int len=8;
	int i;
	unsigned char regmask;
	unsigned char t;//,vl,ul;
	unsigned char sh;

	if (!(drive->rd_capabilities & DEVICE_DVD)) 
		{drive->rpc.phase = 0; return;}

	drive->cmd_clear();
	drive->cmd[0] = MMC_REPORT_KEY;
	drive->cmd[8] = len>>8;
	drive->cmd[9] = len & 0xFF;
	drive->cmd[10] = 0x08;
	drive->cmd[11] = 0;
	if (( drive->err = drive->cmd.transport(READ,drive->rd_buf,len) ))
		{ if (drive->err == 0x52400) 
			{printf("\n** Unit is RPC-I\n"); drive->rpc.phase = 1; }
		else
			{if (!drive->silent) sperror ("READ_RPC_STATE",drive->err);  drive->rpc.phase = 0; }
		return; }
/*	printf("MMC_REPORT_KEY data: ");
	for(i=0; i<len ;i++) printf(" %02X",drive->rd_buf[i] & 0xFF);
	printf("\n");*/
	if (swap2(drive->rd_buf) < 6) return;
	
	drive->rpc.ch_u = drive->rd_buf[4] & 0x07;
	drive->rpc.ch_v = (drive->rd_buf[4] >> 3) & 0x07;
	t  = (drive->rd_buf[4] >> 6) & 0x03;
	regmask = drive->rd_buf[5];
	sh = drive->rd_buf[6];

	drive->rpc.phase = 2;
	printf("\n** Unit is RPC-II\n");
	printf("Current region     : ");
	if (regmask != 0xFF) {
		for (i=0; i<8; i++)
			if (!((regmask >> i) & 1))
				{printf("%d",i+1); drive->rpc.region = i+1;}
		printf("\n");
	} else
		printf("does not set\n");
	printf("User changes left  : %d\n",drive->rpc.ch_u);
	printf("Vendor resets left : %d\n",drive->rpc.ch_v);
}

int determine_cd_type(drive_info* drive) {
//	unsigned char*	ATIP;
//	int		ATIP_len;
//	int		i;
	int ratip = read_atip(drive);
	if (ratip) {
		if (!drive->silent) printf("no ATIP found, assuming disc type: CD-ROM\n");
		return DISC_CDROM; // CD-ROM
//	} else {
//		drive->ATIP_len += 4;
	}
	if (drive->ATIP_len < 8) {
		if (!drive->silent) printf("ATIP too small, assuming disc type: CD-ROM\n");
		return DISC_CDROM; // CD-ROM
	}
/*
	printf("ATIP_len=%d\nATIP data:",drive->ATIP_len);
	for (i=0; i< drive->ATIP_len; i++) printf("%4d",(drive->ATIP[i])&0xFF);
	printf("\n");
*/
	// CD-RW?
	int cdrw = !!(drive->ATIP[6] & 0x40);
	int cdrw_subtype = (drive->ATIP[6] & 0x38) >> 3;
	if (cdrw) {
		if (!drive->silent) printf("disc type: CD-RW\n");
		switch (cdrw_subtype) {
			case 0: return DISC_CDRW | DISC_CDRWMS; break;
			case 1: return DISC_CDRW | DISC_CDRWHS; break;
			case 2: return DISC_CDRW | DISC_CDRWUS; break;
			case 3: return DISC_CDRW | DISC_CDRWUSP; break;
			default: return DISC_CDRW;
		}
	}
	if (!drive->silent) printf("disc type: CD-R\n");
	return DISC_CDR;
}

int read_mediaid_dvd(drive_info* drive){
	int		i;
	unsigned int	len;
	char		header[40];
	union { unsigned char _e[4+40],_11[4+256]; } dvd;
	unsigned char	format; // 0x11 +, 0x0E -
	if (drive->media.disc_type & DISC_DVDminus)
		format=0x0E;
	else
		format=0x11;
	drive->cmd_clear();
	drive->cmd[0] = MMC_READ_DVD_STRUCTURE;
	drive->cmd[7] = format;
	drive->cmd[9] = 4;
	drive->cmd[11] = 0;
	drive->err = drive->cmd.transport(READ,header,4);//))
	len = (header[0]<<8|header[1]) + 2;
	if (len>sizeof(dvd)) len=sizeof(dvd);
	drive->cmd_clear();
	drive->cmd[0] = MMC_READ_DVD_STRUCTURE;
	drive->cmd[7] = format;
	drive->cmd[8] = len>>8;
	drive->cmd[9] = len;
	drive->cmd[11] = 0;
	drive->err = drive->cmd.transport(READ,&dvd,len);
	drive->media.MID[0] = 0;
	if (drive->media.disc_type & DISC_DVDminus) {
		memcpy(drive->media.MID,dvd._e+21,6);
		drive->media.MID[6]='/';
		memcpy(drive->media.MID+7,dvd._e+29,6);
		drive->media.MID[12]=0;
//		drive->media.MID[12]='/';
//		memcpy(drive->media.MID+13,dvd._e+37,6);
// 		drive->media.MID[18]=0;
	} else {
		memcpy(drive->media.MID,dvd._11+23,8);
		drive->media.MID[8]='/';
		memcpy(drive->media.MID+9,dvd._11+31,3);
		drive->media.MID[12]=0;
//		for (i=12; i<19; i++) drive->media.MID[i]=0;
	}
	for (i=0; i<12; i++) if(drive->media.MID[i] == 0) drive->media.MID[i]=0x20;
	return 0;
}

int read_mediaid_cd(drive_info* drive)
{
//	printf("read_mediaid_cd()\n");
	msf	lin;
	int type;
//	int atippr = read_atip(drive);
//	if (!atippr) drive->ATIP_len+=4; else return 1;
	if (!drive->ATIP_len) return 1;
	lin.m=drive->ATIP[8];
	lin.s=drive->ATIP[9];
	lin.f=drive->ATIP[10];
	type = lin.f % 10;
	lin.f -= type;
	int idx=0;
	int nf=1;
	while (mi[idx].lin.m && nf)
		if (lin.m == mi[idx].lin.m && lin.s == mi[idx].lin.s && lin.f == mi[idx].lin.f) nf=0;
		else idx++;
	
//	strncpy(drive->media.MID,mi[idx].name,47));
	sprintf(drive->media.MID,"[%02d:%02d.%02d] %s",lin.m,lin.s,lin.f,mi[idx].name);
	return 0;
}

int determine_disc_type(drive_info* drive) {
	int current = 0;
//	int i=0;
	drive->media.disc_type = DISC_NODISC;
// 	drive->media.type = Media_NoMedia;
	if (drive->mmc>1) {
		get_configuration(drive, FEATURE_PROFILE_LIST, NULL, &current);
		switch (drive->rd_buf[7]) {
			case 0: drive->media.disc_type = DISC_NODISC; break;
			case PROFILE_CD_ROM:		drive->media.disc_type = DISC_CDROM; break;
			case PROFILE_CD_R: 		drive->media.disc_type = DISC_CDR; break;
			case PROFILE_CD_RW:		drive->media.disc_type = DISC_CDRW; break;
			case PROFILE_DVD_ROM:		drive->media.disc_type = DISC_DVDROM; break;
			case PROFILE_DVD_R_SEQ:		drive->media.disc_type = DISC_DVDminusR; break;
			case PROFILE_DVD_RAM:		drive->media.disc_type = DISC_DVDRAM; break;
			case PROFILE_DVD_RW_RESTOV:	drive->media.disc_type = DISC_DVDminusRWR; break;
			case PROFILE_DVD_RW_SEQ:	drive->media.disc_type = DISC_DVDminusRWS; break;
			case PROFILE_DVD_R_DL_SEQ:	drive->media.disc_type = DISC_DVDminusRDL; break;
			case PROFILE_DVD_R_DL_JUMP:	drive->media.disc_type = DISC_DVDminusRDLJ; break;
			case PROFILE_DVD_PLUS_RW:	drive->media.disc_type = DISC_DVDplusRW; break;
			case PROFILE_DVD_PLUS_R:	drive->media.disc_type = DISC_DVDplusR; break;
			case PROFILE_DVD_PLUS_R_DL:	drive->media.disc_type = DISC_DVDplusRDL; break;

			default: drive->media.disc_type = DISC_UN; break;
		}
		if (!drive->media.disc_type) return 0;
		read_disc_information(drive);
		if (drive->media.disc_type & DISC_CD) {
			drive->media.disc_type = determine_cd_type(drive);
			read_mediaid_cd(drive);
			if (!drive->silent) printf("** MID: '%s'\n",drive->media.MID);
			return 0;
		} else if (drive->media.disc_type & DISC_DVD) {
// 			drive->media.type=Media_DVD;
 			drive->rd_buf[4]=0;
			drive->cmd_clear();
			drive->cmd[0] = MMC_READ_DVD_STRUCTURE;
			drive->cmd[7] = 0;//0x11; //dvd_dash;
			drive->cmd[9] = 36;
			drive->cmd[11] = 0;
			if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,36))) 
				if (!drive->silent) sperror ("READ_DVD_STRUCTURE",drive->err);
			drive->media.book_type = (drive->rd_buf[4] & 0xFF);
			drive->media.layers = 1 + ((drive->rd_buf[6] & 0x60) >> 5);
			read_mediaid_dvd(drive);
			if (!drive->silent) printf("** MID: '%s'\n",drive->media.MID);
			if ((!(drive->wr_capabilities & DEVICE_DVD))||(drive->media.disc_type & DISC_DVDROM)) {
				if (!drive->silent) {
					printf("Device can't write DVD's or media detected as DVD-ROM,\n");
					printf("trying to corectly detect DVD type...\n");
				}
				switch ((drive->media.book_type>>4)&0x0F){
					case BOOK_DVD_R:
						if (drive->media.layers == 1) 
							drive->media.disc_type = DISC_DVDminusR;
						else
							drive->media.disc_type = DISC_DVDminusRDL;
						break;
					case BOOK_DVD_RW:
						drive->media.disc_type = DISC_DVDminusRWS; break;
					case BOOK_DVD_PR:
						drive->media.disc_type = DISC_DVDplusR; break;
					case BOOK_DVD_PRW:
						drive->media.disc_type = DISC_DVDplusRW; break;
					case BOOK_DVD_PR_DL:
						drive->media.disc_type = DISC_DVDplusRDL; break;
					case BOOK_DVD_ROM:
						if (strncmp(drive->media.MID,"        /   ",12)) {
							if (!drive->silent) printf("MID found:)\n");
							if (drive->media.erasable) {
								drive->media.disc_type = DISC_DVDplusRW;
							} else {
								if (drive->media.layers == 1)
									drive->media.disc_type = DISC_DVDplusR;
								else
									drive->media.disc_type = DISC_DVDplusRDL;
							}
						}
						break;
					default:
						break;
				}
			}
			read_writer_info(drive);
			if (!drive->silent) printf("** Writer used: '%s'\n",drive->media.writer);
/*
			read_disc_regions(drive);
			printf("DVD Copyright info: ");
			for (i=0;i<4;i++) printf("0x%02X ",drive->rd_buf[i]);
			printf("\n");
*/
			return 0;
		}
	} else {
		read_capacity(drive);
		if (drive->media.capacity) {
// 			drive->media.type=Media_CD;
			drive->media.disc_type = DISC_CDROM;
			read_disc_information(drive);
		}
		return 0;
	}
	return 1;
}

int get_spindown(drive_info* drive) {
	mode_sense(drive, 0x0D, 00, 192);
	if (drive->err)
		{drive->parms.spindown_idx=spindowns; return (drive->err);}
	drive->parms.spindown_idx = drive->rd_buf[11] & 0x0F;
	return 0;
}

int set_spindown(drive_info* drive) {
	int	i;
	for (i=0; i<16; i++) drive->rd_buf[i]=0;
	drive->rd_buf[8] = 0x0D;
	drive->rd_buf[9] = 0x06;
	drive->rd_buf[11] = drive->parms.spindown_idx & 0x0F;
	drive->rd_buf[13] = 0x3C;
	drive->rd_buf[15] = 0x4B;
	mode_select(drive, 16);
	return 0;
}

int get_performance(drive_info* drive) {
	const int max_descs=52;
	const int desc_len=16;
	int	len, descn;
//	int	i;
	int	j,offs;
	drive->cmd_clear();
	drive->cmd[0] = MMC_GET_PERFORMANCE;
	drive->cmd[1] = 0x00;
	drive->cmd[8] = (max_descs >> 8) & 0xFF;
	drive->cmd[9] = max_descs & 0xFF;
	if ((drive->err=drive->cmd.transport(READ,drive->rd_buf, 1024)))
		{ sperror ("GET_PERFORMANCE",drive->err); return (drive->err); }
//	return 1;
	len = (drive->rd_buf[0]<<24) | (drive->rd_buf[1]<<16) | (drive->rd_buf[2]<<8) | (drive->rd_buf[3]);
	descn = len/desc_len;
//	printf("Performance data length: %d; decriptors: %d\n",len, descn);

//	printf("GET_PERFORMANCE response dump:\n");
//	for (j=0; j*8<len; j++) {
//		for (i=0; i<8; i++) printf(" 0x%02X",drive->rd_buf[i]);
//		printf("\n");
//	}
	for (j=0; j<descn; j++) {
		offs = 8+j*desc_len;
		printf("\tDescriptor #%02d:",j);
//		for (i=0; i<desc_len; i++)  printf(" 0x%02X",(drive->rd_buf[offs+i])&0xFF);
//		printf("\n");
		drive->perf.lba_s = ((drive->rd_buf[offs]<<24)&0xFF000000) | ((drive->rd_buf[offs+1]<<16)&0xFF0000) |
			 ((drive->rd_buf[offs+2]<<8)&0xFF00) | ((drive->rd_buf[offs+3])&0xFF);
		offs = 8+j*desc_len+4;
		drive->perf.spd_s = ((drive->rd_buf[offs]<<24)&0xFF000000) | ((drive->rd_buf[offs+1]<<16)&0xFF0000) |
			 ((drive->rd_buf[offs+2]<<8)&0xFF00) | ((drive->rd_buf[offs+3])&0xFF);
		offs = 8+j*desc_len+8;
		drive->perf.lba_e = ((drive->rd_buf[offs]<<24)&0xFF000000) | ((drive->rd_buf[offs+1]<<16)&0xFF0000) |
			 ((drive->rd_buf[offs+2]<<8)&0xFF00) | ((drive->rd_buf[offs+3])&0xFF);
		offs = 8+j*desc_len+12;
		drive->perf.spd_e = ((drive->rd_buf[offs]<<24)&0xFF000000) | ((drive->rd_buf[offs+1]<<16)&0xFF0000) |
			 ((drive->rd_buf[offs+2]<<8)&0xFF00) | ((drive->rd_buf[offs+3])&0xFF);
		printf("\t%dkB/s@%d -> %dkB/s@%d\n",
			drive->perf.spd_s,drive->perf.lba_s,drive->perf.spd_e,drive->perf.lba_e);
	}
	return 0;
}

int get_write_speed(drive_info* drive) {
	int	offs;
	mode_sense(drive, 0x2A, 00, 256);
	offs=0; while (((drive->rd_buf[offs]) & 0x3F) != 0x2A) offs++;
	drive->parms.write_speed_kb = swap2(drive->rd_buf+offs+28);
	return 0;
}

int get_write_speed_tbl(drive_info* drive) {
	int	offs;
	int	i, spdcnt;
	mode_sense(drive, 0x2A, 00, 256);
	offs=0; while (((drive->rd_buf[offs]) & 0x3F) != 0x2A) offs++;
//	drive->parms.write_speed_kb = swap2(drive->rd_buf+offs+28);
	spdcnt = swap2(drive->rd_buf+offs+30);
	for (i=0; i<speed_tbl_size;i++)
		drive->parms.wr_speed_tbl_kb[i] = 0;
//	printf("== Write speeds: %d\n",spdcnt);
	for (i=0; (i<spdcnt) && (i<speed_tbl_size); i++) {
		drive->parms.wr_speed_tbl_kb[i] = swap2(drive->rd_buf+offs+32+i*4+2);
//		printf("  Speed #%02d: %d kB/s\n",i,drive->parms.wr_speed_tbl_kb[i]);
	}
	return 0;
}

int get_read_speed(drive_info* drive) {
	int	offs;
	mode_sense(drive, 0x2A, 00, 256);
	offs=0; while (((drive->rd_buf[offs]) & 0x3F) != 0x2A) offs++;
	drive->parms.read_speed_kb = swap2(drive->rd_buf+offs+14);
	return 0;
}

int set_streaming(drive_info* drive) {
	char data[28]; memset(data, 0, 28);

	int* start_lba  = (int*)&data[4];
	int* end_lba    = (int*)&data[8];
	int* read_size  = (int*)&data[12];
	int* read_time  = (int*)&data[16];
	int* write_size = (int*)&data[20];
	int* write_time = (int*)&data[24];

	read_capacity(drive);

	*start_lba = swap4(0);
	*end_lba = swap4(drive->media.capacity);
//	*end_lba = swap4(0);
	*read_time = swap4(1000);
	*read_size = swap4(drive->parms.read_speed_kb);
	*write_time = swap4(1000);
	*write_size = swap4(drive->parms.write_speed_kb);
	drive->cmd_clear();
	drive->cmd[0] = MMC_SET_STREAMING;
	drive->cmd[10] = 28;
	if ((drive->err=drive->cmd.transport(WRITE,data,28)))
		{sperror ("SET_STREAMING",drive->err); return (drive->err); }
	return 0;
}

int set_cd_speed(drive_info* drive) {
	int	speed = 0xFFFF;
	int	speed_wr = 0xFFFF;
	if (drive->parms.read_speed_kb) speed = drive->parms.read_speed_kb;
	if (drive->parms.write_speed_kb) speed_wr = drive->parms.write_speed_kb;

	drive->cmd_clear();
	drive->cmd[0] = MMC_SET_SPEED;
	drive->cmd[1] = 0x01;
	drive->cmd[2] = (speed >> 8) & 0xFF;
	drive->cmd[3] = speed & 0xFF;
//	drive->cmd[4] = 0xFF;
//	drive->cmd[5] = 0xFF;
	drive->cmd[4] = (speed_wr >> 8) & 0xFF;
	drive->cmd[5] = speed_wr & 0xFF;
	if ((drive->err=drive->cmd.transport(NONE,NULL,0) )) {
//		if (drive->err != 0x23A02) drive->capabilities&=(NCAP_SET_CD_SPEED);
//		sperror ("SET_CD_SPEED",drive->err);
		return (drive->err);
	}
	return 0;
}

//#define __STREAMING_PRIOR
int set_read_speed(drive_info* drive) {
	int	rez=0;
	if ((rez = set_cd_speed(drive)))
		rez = set_streaming(drive);
/*
#ifdef __STREAMING_PRIOR
	if (drive->media.disc_type & DISC_CD) {
		rez = set_cd_speed(drive);
	} else if (drive->media.disc_type & DISC_DVD) {
		{ if ((rez = set_cd_speed(drive))) rez = set_streaming(drive); }
	}
#else
	if ((drive->capabilities & CAP_SET_CD_SPEED) )//&& (drive->media.disc_type & DISC_CD))
		{ if ((rez = set_cd_speed(drive))) rez = set_streaming(drive); }
	else if (drive->capabilities & CAP_REAL_TIME_STREAMING)
		{ rez = set_streaming(drive); }
#endif*/
	return rez;
}

int get_media_status(drive_info* drive){
	drive->cmd_clear();
	drive->cmd[0]=MMC_GET_EVENT_STATUS_NOTIFICATION;
	drive->cmd[1]=0x01;
	drive->cmd[4]=0x10;
	drive->cmd[7]=0;
	drive->cmd[8]=8;
	if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,8)))
		{ sperror ("GET_EVENT_STATUS",drive->err); return (drive->err); }
	if (drive->rd_buf[5] & 0x01) drive->parms.status |= STATUS_OPEN;
	else drive->parms.status &= (~STATUS_OPEN);
	if (drive->rd_buf[5] & 0x02) drive->parms.status |= STATUS_MEDIA_PRESENT;
	else drive->parms.status &= (~STATUS_MEDIA_PRESENT);
	return 0;
}

int load_eject(drive_info* drive, bool load){
	drive->cmd_clear();
	drive->cmd[0]=MMC_START_STOP_UNIT;
	drive->cmd[4]=0x02 | load;
	if ((drive->err=drive->cmd.transport(NONE,NULL,0)))
		{ sperror ("LOAD_EJECT",drive->err); return (drive->err); }
	return 0;
}

int get_lock(drive_info* drive){
//	printf("get_lock()\n");
	int offs;
	if (mode_sense(drive, 0x2A, 0, 256)) 
		{ sperror ("GET_LOCK",drive->err); return (drive->err); }
	offs=0; while (((drive->rd_buf[offs]) & 0x3F) != 0x2A) offs++;
	if (drive->rd_buf[offs+6] & 0x02) drive->parms.status |= STATUS_LOCK;
	else drive->parms.status &= (~STATUS_LOCK);
#ifndef __PXCONTROL
	printf("--- Disc %slocked\n",(drive->parms.status & STATUS_LOCK) ? "" : "UN");
#endif
	return 0;
}

int set_lock(drive_info* drive){
	int lock;
//	printf("--- %slocking disc...\n",(drive->parms.status & STATUS_LOCK) ? "" : "UN");
	if (drive->parms.status & STATUS_LOCK) lock=1; else lock=0;	
	drive->cmd_clear();
	drive->cmd[0]=MMC_PREVENT_ALLOW_MEDIUM_REMIVAL;
	drive->cmd[4]=lock;
	if ((drive->err=drive->cmd.transport(NONE, NULL, 0)))
		{ sperror ("SET_LOCK",drive->err); get_lock(drive); return (drive->err); }
	get_lock(drive);
	return 0;
}

int play_audio_msf(drive_info* drive, int beg, int end){
	drive->cmd_clear();
	drive->cmd[0]=MMC_PLAY_AUDIO_MSF;
	drive->cmd[3]=(beg>>16) & 0xFF;
	drive->cmd[4]=(beg>>8) & 0xFF;
	drive->cmd[5]=beg & 0xFF;
	drive->cmd[6]=(end>>16) & 0xFF;
	drive->cmd[7]=(end>>8) & 0xFF;
	drive->cmd[8]=end & 0xFF;
	if ((drive->err=drive->cmd.transport(NONE, NULL, 0)))
		{ sperror ("PLAY_AUDIO_MSF",drive->err); return (drive->err); }
	return 0;
}

int play_audio(drive_info* drive, int beg, short int len){
	drive->cmd_clear();
	drive->cmd[0]=MMC_PLAY_AUDIO;
	drive->cmd[2]=(beg>>24) & 0xFF;
	drive->cmd[3]=(beg>>16) & 0xFF;
	drive->cmd[4]=(beg>>8) & 0xFF;
	drive->cmd[5]=beg & 0xFF;
	drive->cmd[7]=(len>>8) & 0xFF;
	drive->cmd[8]=len & 0xFF;
	if ((drive->err=drive->cmd.transport(NONE, NULL, 0)))
		{ sperror ("PLAY_AUDIO",drive->err); return (drive->err); }
	return 0;
}

int seek(drive_info* drive, int lba, unsigned char flags){
	drive->cmd_clear();
	drive->cmd[0]=MMC_SEEK;
	drive->cmd[2]=(lba>>24) & 0xFF;
	drive->cmd[3]=(lba>>16) & 0xFF;
	drive->cmd[4]=(lba>>8) & 0xFF;
	drive->cmd[5]=lba & 0xFF;

	drive->cmd[9]=flags;
	if ((drive->err=drive->cmd.transport(NONE, NULL, 0)))
		{ sperror ("SEEK",drive->err); return (drive->err); }
	return 0;
}


int read_cd(drive_info* drive, int lba, int sector_count, unsigned char flags, unsigned char FUA) {
//	int transfer_length = sector_count * 3072;

//	int sect_data = 2352;
	int sect_data = 3072;

	int transfer_length = sector_count * sect_data;

	drive->cmd_clear();
	drive->cmd[0]=MMC_READ_CD;
	drive->cmd[1]= FUA ? 0x08 : 0x00;
	drive->cmd[2]=(lba>>24) & 0xFF;
	drive->cmd[3]=(lba>>16) & 0xFF;
	drive->cmd[4]=(lba>>8) & 0xFF;
	drive->cmd[5]=lba & 0xFF;
	drive->cmd[8]=sector_count;
	drive->cmd[9]=flags;
	if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,transfer_length)))
		{ sperror ("READ_CD",drive->err); return (drive->err); }
	return 0;
}

int read(drive_info* drive, int lba, int sector_count, unsigned char FUA) {
//	int transfer_length = sector_count * 3072;
	int transfer_length = sector_count * 2048;
	drive->cmd_clear();
	drive->cmd[0]=MMC_READ;
	drive->cmd[1]= FUA ? 0x08 : 0x00;
	drive->cmd[2]=(lba>>24) & 0xFF;
	drive->cmd[3]=(lba>>16) & 0xFF;
	drive->cmd[4]=(lba>>8) & 0xFF;
	drive->cmd[5]=lba & 0xFF;
	drive->cmd[8]=sector_count;
	if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,transfer_length)))
		{sperror ("READ",drive->err); return (drive->err);}
	return 0;
}

int read_one_ecc_block(drive_info* drive, int lba) {
	drive->cmd_clear();
	drive->cmd[0] = MMC_READ;
	drive->cmd[2] = (lba>>24) & 0xFF;
	drive->cmd[3] = (lba>>16) & 0xFF;
	drive->cmd[4] = (lba>>8) & 0xFF;
	drive->cmd[5] = lba & 0xFF;
	drive->cmd[8] = 0x10;
	if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,0x8000)))
		{sperror ("READ_ONE_ECC_BLOCK",drive->err); return (drive->err);}
//	if ((drive->err=drive->cmd.transport(READ,(void*)((unsigned char*)drive->rd_buf+(0x0001<<14)),0x34)))
//		{sperror ("READ_ONE_ECC_BLOCK",drive->err); return (drive->err);}
	return 0;
}

int get_drive_serial_number(drive_info* drive) {
//	char data[2048]; memset(data, 0, sizeof(data));
	unsigned int data_length;
	unsigned int length;
	get_configuration(drive, FEATURE_LOGICAL_UNIT_SERIAL_NUMBER, &data_length, NULL);
	length = drive->rd_buf[11]; drive->rd_buf[12+length]=0;
	if (data_length>8) strncpy(drive->serial, (char*)drive->rd_buf+12, 16);
	else drive->serial[0]=0;
	return 1;
}

int read_buffer_capacity(drive_info* drive){
	if (mode_sense(drive, 0x2A, 0, 192)) return 1;
	drive->buffer_size=((drive->rd_buf[20] & 0xFF) << 8 ) | (drive->rd_buf[21] & 0xFF);
	printf("Buffer capacity: 0x%04X (%d)KB\n", drive->buffer_size, drive->buffer_size);
	return 0;
}

int read_writer_info(drive_info* drive)
{
	if (!(drive->media.disc_type & (DISC_DVDminusR | DISC_DVDminusRWS | DISC_DVDminusRWR))) {
		strcpy(drive->media.writer, "n/a (only for DVD-R(W))");
		return 1;
	}
	char format=0x0D;
	drive->media.writer[0]=0;
	drive->rd_buf[8]=0;
	drive->cmd_clear();
	drive->cmd[0]=MMC_READ_DVD_STRUCTURE;
	drive->cmd[5]=0x62;
	drive->cmd[7]=format;
	drive->cmd[8]=8;
	drive->cmd[9]=8;
	if ((drive->err = drive->cmd.transport(READ,drive->rd_buf,2056)) || (!drive->rd_buf[8])) {
		printf("Read Writer Info Method 1 failed\n");
		drive->cmd_clear();
		drive->cmd[0]=MMC_READ_DVD_STRUCTURE;
		drive->cmd[5]=0x02;
		drive->cmd[7]=format;
		drive->cmd[8]=8;
		drive->cmd[9]=8;
		if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,2056))) {
			printf("Read Writer Info Method 2 failed\n");
			return 1;
		}
	}
	for (int k=0;k<0x3F;k++) {
		if (!drive->rd_buf[8+k]) drive->rd_buf[8+k]=0x20;
	}
	strncpy(drive->media.writer, (char*)drive->rd_buf+8, 0x3F);
	remove_double_spaces(drive->media.writer);
//	remove_end_spaces(drive->media.writer);
	return 0;
}

int detect_mm_capabilities(drive_info* drive){
	char len;
	int offs;
	int i,j;
	if (mode_sense(drive, 0x2A, 0, 256)) return 1;
	offs=0; while (((drive->rd_buf[offs]) & 0x3F) != 0x2A) offs++;
	len=drive->rd_buf[offs+1];
	if (!drive->silent) printf("CD parameters page length: 0x%02X\n",len);
	if (len >= 28) {drive->mmc=3;}
	else if (len >= 24) {drive->mmc=2;}
	else {drive->mmc=1;}
	if (!drive->silent) for (i=offs; i<(offs+len+2);i+=8){
		for (j=0;j<8;j++) printf(" %02X",drive->rd_buf[i+j] & 0xFF);
		printf("\n");
	}
//	if (drive->mmc) drive->rd_capabilities|=DEVICE_CD_ROM;
	if (drive->rd_buf[offs+2] & 0x01)drive->rd_capabilities|=DEVICE_CD_R;
	if (drive->rd_buf[offs+2] & 0x02)drive->rd_capabilities|=DEVICE_CD_RW;

	if (drive->rd_buf[offs+3] & 0x01)drive->wr_capabilities|=DEVICE_CD_R;
	if (drive->rd_buf[offs+3] & 0x02)drive->wr_capabilities|=DEVICE_CD_RW;
	if (drive->rd_buf[offs+3] & 0x04)drive->capabilities|=CAP_TEST_WRITE;

	if (drive->rd_buf[offs+4] & 0x01)drive->capabilities|=CAP_CD_AUDIO;
	if (drive->rd_buf[offs+4] & 0x02)drive->capabilities|=CAP_COMPOSITE;
	if (drive->rd_buf[offs+4] & 0x04)drive->capabilities|=CAP_DIGITAL_PORT_1;
	if (drive->rd_buf[offs+4] & 0x08)drive->capabilities|=CAP_DIGITAL_PORT_2;
	if (drive->rd_buf[offs+4] & 0x10)drive->capabilities|=CAP_MODE2_FORM1;
	if (drive->rd_buf[offs+4] & 0x20)drive->capabilities|=CAP_MODE2_FORM2;
	if (drive->rd_buf[offs+4] & 0x40)drive->capabilities|=CAP_MULTISESSION;

	if (drive->rd_buf[offs+5] & 0x01)drive->capabilities|=CAP_DAE;
	if (drive->rd_buf[offs+5] & 0x02)drive->capabilities|=CAP_ACCURATE_STREAM;
	if (drive->rd_buf[offs+5] & 0x10)drive->capabilities|=CAP_C2;
	if (drive->rd_buf[offs+5] & 0x20)drive->capabilities|=CAP_ISRC;
	if (drive->rd_buf[offs+5] & 0x40)drive->capabilities|=CAP_UPC;
	if (drive->rd_buf[offs+5] & 0x80)drive->capabilities|=CAP_READ_BAR_CODE;	

	if (drive->rd_buf[offs+6] & 0x01)drive->capabilities|=CAP_LOCK;
	if (drive->rd_buf[offs+6] & 0x08)drive->capabilities|=CAP_EJECT;

	drive->loader_id = (drive->rd_buf[offs+6] >> 5) & 0x07;

	if (drive->rd_buf[offs+7] & 0x10)drive->capabilities|=CAP_SIDE_CHANGE;

	switch (drive->mmc) {
		case 3:
			drive->parms.write_speed_kb=drive->rd_buf[37] << 8 | drive->rd_buf[36];
		case 2:
			if (drive->rd_buf[offs+2] & 0x08)drive->rd_capabilities|=DEVICE_DVD_ROM;
			if (drive->rd_buf[offs+2] & 0x10)drive->rd_capabilities|=DEVICE_DVD_R;
			if (drive->rd_buf[offs+2] & 0x20)drive->rd_capabilities|=DEVICE_DVD_RAM;
			if (drive->rd_buf[offs+3] & 0x10)drive->wr_capabilities|=DEVICE_DVD_R;
			if (drive->rd_buf[offs+3] & 0x20)drive->wr_capabilities|=DEVICE_DVD_RAM;
//			break;
		case 1:
			drive->parms.max_read_speed_kb=drive->rd_buf[offs+9] <<8 | drive->rd_buf[offs+8];
			drive->parms.read_speed_kb=drive->rd_buf[offs+15] <<8 | drive->rd_buf[offs+14];
			drive->parms.max_write_speed_kb=drive->rd_buf[offs+19] <<8 | drive->rd_buf[offs+19];
			drive->parms.write_speed_kb=drive->rd_buf[offs+21] << 8 | drive->rd_buf[offs+20];
			break;
	}
	if (!drive->silent) printf("Max speeds:\tR@%dKBps / W@%dKBps\nCurrent speeds:\tR@%dKBps / W@%dKBps\n",
		drive->parms.max_read_speed_kb, drive->parms.max_write_speed_kb,
		drive->parms.read_speed_kb, drive->parms.write_speed_kb);
	return 0;
}

int convert_to_ID (drive_info* drive) {
	if (!strncmp(drive->ven,"PLEXTOR ",8)) {
		drive->ven_ID=WR_PLEXTOR;
		if(!strncmp(drive->dev,"CD-R   PREMIUM",14))
			drive->dev_ID=PLEXTOR_PREMIUM;
		else
		if(!strncmp(drive->dev,"DVD-ROM PX-130",14))
			{ drive->ven_ID=RD_BENQ;
			drive->dev_ID=BENQ_DV1650V;}
		else
		if(!strncmp(drive->dev,"DVDR   PX-708A2",15))
			drive->dev_ID=PLEXTOR_708A2;
		else
		if(!strncmp(drive->dev,"DVDR   PX-712",13))
			drive->dev_ID=PLEXTOR_712;
		else
		if(!strncmp(drive->dev,"DVDR   PX-714",13))
			drive->dev_ID=PLEXTOR_716;
		else
		if(!strncmp(drive->dev,"DVDR   PX-716",13))
			drive->dev_ID=PLEXTOR_716;
		else
		if(!strncmp(drive->dev,"DVDR   PX-740",13))
			{ drive->ven_ID=WR_BENQ;
			drive->dev_ID=BENQ_DW1640;}
		else
/*		if(!strncmp(drive->dev,"DVDR   PX-750",13))
		else*/
		if(!strncmp(drive->dev,"DVDR   PX-755",13))
			drive->dev_ID=PLEXTOR_760;
		else
		if(!strncmp(drive->dev,"DVDR   PX-760",13))
			drive->dev_ID=PLEXTOR_760;
		else
			drive->dev_ID=PLEXTOR_OLD;
//			{ drive->ven_ID = WR_GENERIC; drive->dev_ID = 0; }
	}


	else if (!strncmp(drive->ven,"PIONEER ",8)) {
		drive->ven_ID=WR_PIONEER;
		if(!strncmp(drive->dev,"DVD-RW  DVR-106",15))
			drive->dev_ID=PIO_DVR_106;
		else
		if(!strncmp(drive->dev,"DVD-RW  DVR-107",15))
			drive->dev_ID=PIO_DVR_107;
		else
		if(!strncmp(drive->dev,"DVD-RW  DVR-108",15))
			drive->dev_ID=PIO_DVR_108;
		else
		if(!strncmp(drive->dev,"DVD-RW  DVR-109",15))
			drive->dev_ID=PIO_DVR_109;
		else
		if(!strncmp(drive->dev,"DVD-RW  DVR-110",15))
			drive->dev_ID=PIO_DVR_110;
		else
		if(!strncmp(drive->dev,"DVD-RW  DVR-111",15))
			drive->dev_ID=PIO_DVR_111;
		else
			drive->dev_ID=PIO_OLD;
//			{ drive->ven_ID = WR_GENERIC; drive->dev_ID = 0; }
	}


	else if (!strncmp(drive->ven,"ASUS    ",8)) {
		drive->ven_ID=WR_PIONEER;
		if(!strncmp(drive->dev,"DRW-0402P",9))
			drive->dev_ID=PIO_DVR_106;
		else
		if(!strncmp(drive->dev,"DRW-0804P",9))
			drive->dev_ID=PIO_DVR_107;
		else
		if(!strncmp(drive->dev,"DRW-1604P",9))
			drive->dev_ID=PIO_DVR_108;
		else
		if(!strncmp(drive->dev,"DRW-1608P ",10))
			drive->dev_ID=PIO_DVR_109;
		else
		if(!strncmp(drive->dev,"DRW-1608P2",10))
			drive->dev_ID=PIO_DVR_110;
		else
		if(!strncmp(drive->dev,"DRW-1608P3",10))
			drive->dev_ID=PIO_DVR_111;
		else
			drive->dev_ID=PIO_OLD;
	}


	else if (!strncmp(drive->ven,"_NEC    ",8)) {
		drive->ven_ID=WR_NEC;
		if(!strncmp(drive->dev,"DVD_RW ND-352",13))
			drive->dev_ID=NEC_3520;
		else
		if(!strncmp(drive->dev,"DVD_RW ND-353",13))
			drive->dev_ID=NEC_3530;
		else
		if(!strncmp(drive->dev,"DVD_RW ND-354",13))
			drive->dev_ID=NEC_3540;
		else
		if(!strncmp(drive->dev,"DVD_RW ND-355",13))
			drive->dev_ID=NEC_4550;
		else
		if(!strncmp(drive->dev,"DVD_RW ND-357",13))
			drive->dev_ID=NEC_4570;
		else
		if(!strncmp(drive->dev,"DVD_RW ND-365",13))
			drive->dev_ID=NEC_4650;
		else
		if(!strncmp(drive->dev,"DVD_RW ND-455",13))
			drive->dev_ID=NEC_4550;
		else
		if(!strncmp(drive->dev,"DVD_RW ND-457",13))
			drive->dev_ID=NEC_4570;
		else
		if(!strncmp(drive->dev,"DVD_RW ND-465",13))
			drive->dev_ID=NEC_4650;
		else
			drive->dev_ID=NEC_OLD;
//			{ drive->ven_ID = WR_GENERIC; drive->dev_ID = 0; }
	}


	else if (!strncmp(drive->ven,"LITE-ON ",8)) {
		drive->ven_ID=WR_LITEON;
		if(!strncmp(drive->dev,"LTR-52327S",10))
			drive->dev_ID=LTN_LTR_52327;
		else
		if(!strncmp(drive->dev,"DVDRW SOHW-811S",14))
			drive->dev_ID=LTN_SOHW_811S;
		else
		if(!strncmp(drive->dev,"DVDRW SOHW-812S",14))
			drive->dev_ID=LTN_SOHW_812S;
		else
		if(!strncmp(drive->dev,"DVDRW SOHW-832S",14))
			drive->dev_ID=LTN_SOHW_832S;
		else
		if(!strncmp(drive->dev,"DVDRW SOHW-1653S",15))
			drive->dev_ID=LTN_SOHW_1653S;
		else
		if(!strncmp(drive->dev,"DVDRW SOHW-1673S",15))
			drive->dev_ID=LTN_SOHW_1673S;
		else
		if(!strncmp(drive->dev,"DVDRW SOHW-1693S",15))
			drive->dev_ID=LTN_SOHW_1693S;
		else
		if(!strncmp(drive->dev,"DVDRW SHW-1635S",14))
			drive->dev_ID=LTN_SHW_1635S;
		else
			drive->dev_ID=LTN_OLD;
	}


	else if (!strncmp(drive->ven,"BENQ    ",8)) {
		drive->ven_ID=WR_BENQ;
		if(!strncmp(drive->dev,"DVD DD DW1620",13))
			drive->dev_ID=BENQ_DW1620;
		else
		if(!strncmp(drive->dev,"DVD DD DW1640",13))
			drive->dev_ID=BENQ_DW1640;
		else
			drive->dev_ID=BENQ_OLD;
	}


	else if (!strncmp(drive->ven,"TEAC    ",8)) {
//		if(!strncmp(drive->dev,"DW-552GA",8))
		if(!strncmp(drive->dev,"DV-W50D",7)) {
			drive->ven_ID=WR_PIONEER;
			drive->dev_ID=PIO_DVR_106;
		} else
		if(!strncmp(drive->dev,"DV-W58D",7)) {
			drive->ven_ID=WR_PIONEER;
			drive->dev_ID=PIO_DVR_107;
		} else
		if(!strncmp(drive->dev,"DV-W516D",8)) {
			drive->ven_ID=WR_PIONEER;
			drive->dev_ID=PIO_DVR_108;
		} else
		if(!strncmp(drive->dev,"DV-W58G",7)) {
			drive->ven_ID=WR_LITEON;
			drive->dev_ID=LTN_SOHW_811S;
		} else
		if(!strncmp(drive->dev,"DV-W58G-A",9)) {
			drive->ven_ID=WR_LITEON;
			drive->dev_ID=LTN_SOHW_812S;
		} else {
			drive->ven_ID = WR_GENERIC;
			drive->dev_ID = 0;
		}
	}


	else if (!strncmp(drive->ven,"SONY    ",8)) {
		drive->ven_ID=WR_LITEON;
		if(!strncmp(drive->dev,"DVD RW DRU-700A",15))
			drive->dev_ID=LTN_SOHW_832S;
		else
			drive->dev_ID=LTN_OLD;
	}

	else { drive->ven_ID = WR_GENERIC; drive->dev_ID = 0; }
	return 0;
}

int detect_check_capabilities(drive_info* drive){
	drive->chk_features=0;
	switch (drive->ven_ID) {
		case WR_PLEXTOR:
			switch (drive->dev_ID) {
				case PLEXTOR_760:
				case PLEXTOR_716:
					drive->chk_features|=CHK_TA;
				case PLEXTOR_712:
				case PLEXTOR_708A2:
					drive->chk_features|=CHK_JB_DVD;
					drive->chk_features|=CHK_PI;
					drive->chk_features|=CHK_PO;
				case PLEXTOR_PREMIUM:
					drive->chk_features|=CHK_JB_CD;
					drive->chk_features|=CHK_CX;
					drive->chk_features|=CHK_FETE;
			}
			break;
		case WR_PIONEER:
			drive->chk_features|=CHK_CX;
			if (drive->rd_capabilities & DEVICE_DVD) {
				drive->chk_features|=CHK_PIE;
//				drive->chk_features|=CHK_PIF;
			}
			break;
		case WR_NEC:
			drive->chk_features|=CHK_CX;
			if (drive->rd_capabilities & DEVICE_DVD) {
				drive->chk_features|=CHK_PIE;
				drive->chk_features|=CHK_PIF;
			}
			break;
		case WR_LITEON:
			drive->chk_features|=CHK_CX;
//			drive->chk_features|=CHK_JB_CD;
			if (drive->rd_capabilities & DEVICE_DVD) {
				drive->chk_features|=CHK_PIE;
//				drive->chk_features|=CHK_PIF;
//				drive->chk_features|=CHK_JB_DVD;
			}
			break;
		case RD_BENQ:
			drive->chk_features|=CHK_CX;
			drive->chk_features|=CHK_JB_CD;
			if (drive->rd_capabilities & DEVICE_DVD) {
				drive->chk_features|=CHK_PIE;
//				drive->chk_features|=CHK_PIF;
				drive->chk_features|=CHK_JB_DVD;
			}
			break;
		case WR_BENQ:
			drive->chk_features|=CHK_CX;
//			drive->chk_features|=CHK_JB_CD;
			if (drive->rd_capabilities & DEVICE_DVD) {
				drive->chk_features|=CHK_PIE;
//				drive->chk_features|=CHK_PIF;
//				drive->chk_features|=CHK_JB_DVD;
			}
			break;
		case WR_GENERIC:
			if (drive->capabilities | CAP_C2)
				drive->chk_features|=CHK_CX;
			break;
		default:
			break;
	}
	return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1