/* * Philips DVDR824P firmware P1.2 (possibly other revisions) fails * to return the number of avaliable descriptors in reply to GET * PERFORMANCE command. This makes it impossible to control the * recording velocity with growisofs -speed=X. In wait for firmware * upgrade this snippet makes it possible to work around the firmware * deficiency by stretching MMC specification by suggestion from * Matthias Grimrath. To compile place this file to dvd+rw-tools * source catalog and compile with following command: * * g++ -O -fno-exceptions -o philipspeed philipspeed.cpp * * You should be able to control recording speed by invoking * 'philipspeed /dev/dvd ' prior growisofs. The speed * setting persists till media reload. */ #include "transport.hxx" static unsigned char *pull_page2A (Scsi_Command &cmd) { unsigned char *page2A,header[12]; unsigned int len,bdlen; int err; cmd[0] = 0x5A; // MODE SENSE cmd[1] = 0x08; // "Disable Block Descriptors" cmd[2] = 0x2A; // "Capabilities and Mechanical Status" cmd[8] = sizeof(header); // header only to start with cmd[9] = 0; if ((err=cmd.transport(READ,header,sizeof(header)))) sperror ("MODE SENSE#2A",err), exit(FATAL_START(errno)); len = (header[0]<<8|header[1])+2; bdlen = header[6]<<8|header[7]; if (bdlen) // should never happen as we set "DBD" above { if (len < (8+bdlen+30)) fprintf (stderr,":-( LUN is impossible to bear with...\n"), exit(FATAL_START(EINVAL)); } else if (len < (8+2+(unsigned int)header[9]))// SANYO does this. len = 8+2+header[9]; page2A = (unsigned char *)malloc(len); if (page2A == NULL) fprintf (stderr,":-( memory exhausted\n"), exit(FATAL_START(ENOMEM)); cmd[0] = 0x5A; // MODE SENSE cmd[1] = 0x08; // "Disable Block Descriptors" cmd[2] = 0x2A; // "Capabilities and Mechanical Status" cmd[7] = len>>8; cmd[8] = len; // real length this time cmd[9] = 0; if ((err=cmd.transport(READ,page2A,len))) sperror ("MODE SENSE#2A",err), exit(FATAL_START(errno)); len -= 2; if (len < ((unsigned int)page2A[0]<<8|page2A[1])) // paranoia:-) page2A[0] = len>>8, page2A[1] = len; return page2A; } main(int argc,char *argv[]) { Scsi_Command cmd; unsigned char *page2A,*perf,d[8+16]; unsigned int plen,hlen,len; int err,velocity; double speed_factor; if (argc<3) fprintf (stderr,"usage: %s /dev/dvd speed\n",argv[0]), exit(1); speed_factor=atof(argv[2])/2.4; if (!cmd.associate (argv[1])) fprintf (stderr,"%s: unable to open: ",argv[1]), perror (NULL), exit(1); page2A = pull_page2A (cmd); plen = (page2A[0]<<8|page2A[1]) + 2; hlen = 8 + (page2A[6]<<8|page2A[7]); unsigned char * const p = page2A + hlen; if (plen<(hlen+32) || p[1]<(32-2)) fprintf (stderr,":-( no \"Write Speed\" descriptors...\n"), exit(1); // take number of descriptors from page2A... len = (p[30]<<8|p[31])*16+8; perf = (unsigned char *)malloc(len); if (perf == NULL) fprintf (stderr,":-( memory exhausted\n"), exit(FATAL_START(ENOMEM)); cmd[0] = 0xAC; // GET PERFORMANCE cmd[8] = p[30]; // take number of descriptors cmd[9] = p[31]; // from page2A... cmd[10]= 0x3; // ask for "Write Speed Descriptor" cmd[11]= 0; if ((err=cmd.transport(READ,perf,len))) sperror ("GET PERFORMANCE",err), exit (FATAL_START(errno)); int targetv=0,errp=0; do { memset (d,0,sizeof(d)); cmd[0]=0xAC; // GET PERFORMANCE cmd[1]=4; // ask for "Overall Write performance" cmd[9]=1; cmd[10]=0; // ask for descriptor in effect cmd[11]=0; if (errp || (errp=cmd.transport(READ,d,sizeof(d)))) #if 0 sperror ("GET CURRENT PERFORMANCE",errp), exit (FATAL_START(errno)); // well, if it passed above, we // expect it to pass this too... #else // Pioneer doesn't report current speed through GET PERFORMANCE:-( { emulated_err: if (page2A == NULL) return -1; unsigned int plen,hlen; plen = (page2A[0]<<8|page2A[1]) + 2; hlen = 8 + (page2A[6]<<8|page2A[7]); unsigned char * const p = page2A + hlen; if (plen<(hlen+32) || p[1]<(32-2)) return -1; // well, SET CD SPEED wouldn't work... velocity = p[28]<<8|p[29]; } #endif else { if ((d[4]&2) == 0) // I could have checked length too... #if 1 // at least LG GCA-4040N returns empty descriptor:-( { errp = -1; goto emulated_err; } #else { fprintf (stderr,":-( unit fails to report current write performance\n"); return -1; // well, SET CD SPEED might work... } #endif page2A = NULL; // skip over MODE SENSE#2A // I'm not sure if it works in all situations, e.g. with // ZCLV, but I have to start somewhere, so I bet on the // "Start Performance" field, at least to start with... velocity = d[8+4]<<24|d[8+5]<<16|d[8+6]<<8|d[8+7]; // Note that the value is expected to be perfectly sane! } if (speed_factor != 0.0) { int i,j=len-8,v,v0,v1,minv,closesti,closestv=0; unsigned char *wsdp=perf+8; for (minv=0x7fffffff,i=0;i>24; pd[9]=cap>>16; pd[10]=cap>>8; pd[11]=cap; #endif memcpy(pd+12,wsdp+closesti+8,4); // copy "Read Speed" memcpy(pd+20,wsdp+closesti+12,4); // copy "Write Speed" pd[18]=pd[26]=1000>>8; // set both "Read Time" and pd[19]=pd[27]=1000&0xFF; // "Write Time" to 1000ms cmd[0]=0xB6; // SET STREAMING cmd[10]=sizeof(pd); cmd[11]=0; if ((err=cmd.transport (WRITE,pd,sizeof(pd)))) sperror ("SET STREAMING",err), exit (FATAL_START(errno)); if (page2A==NULL) continue; // unit couldn't reply to GET CURRENT PERFORMANCE, we have // no other choice but to pull page 2A. Thanks to Pioneer // for messing up this code... unsigned int plen = (page2A[0]<<8|page2A[1]) + 2; cmd[0] = 0x5A; // MODE SENSE cmd[1] = 0x08; // "Disable Block Descriptors" cmd[2] = 0x2A; // "Capabilities and Mechanical Status" cmd[7] = plen>>8; cmd[8] = plen; // real length this time cmd[9] = 0; if ((err=cmd.transport(READ,page2A,plen))) sperror ("MODE SENSE#2A",err), exit(FATAL_START(errno)); } else if (targetv) { if (targetv!=velocity) fprintf (stderr,":-( Failed to change write speed: %d->%d\n", velocity,targetv), exit (FATAL_START(EINVAL)); break; } } while (targetv); printf ("%s: \"Current Write Speed\" is %.1fx1385KBps.\n", argv[1],velocity/1385.0); }