/*

The compilation of software known as ``ataburn'' is distributed under the
following terms:

Copyright (C) 2008-2009 Issei Numata. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
 */

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/ata.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#define ATAPI_CMD_MODE_READ	1
#define ATAPI_CMD_MODE_WRITE	2

typedef struct _DVD_RZONE{
     u_char rt:1;
     u_char blank:1;
     u_char packet_inc:1;
     u_char fp:1;
     u_char data_mode:4;
     u_char lra_v:1;
     u_char nwa_v:1;

     u_int start_adr;
     u_int nwa;
     u_int free_blocks;
     u_int packet_size;
     u_int rzone_size;
     u_int last_rec_adr;
} DVD_RZONE;

typedef struct _DVD_INFO{
     u_int block_len;
     u_int capacity;

     u_char erasable:1;
     u_char disc_status:2;

     u_short n_first_rzone;
     u_short n_last_rzone;

     u_short rmd_format;
     u_char rmd_disc_status;

     DVD_RZONE rzone[16];
} DVD_INFO;

typedef struct _ATAPI{
     int fd;
    //int chan;
    //int dev;

     u_char skey;
     u_char asc;
     u_char ascq;
     u_char sksv:1;
     u_int skey_spec:16;

     DVD_INFO info;
} ATAPI;

int atapi_request_sense(ATAPI *, int);

static u_int
read_be32(u_char *buf)
{
     return buf[0] << 24 | buf[1] <<16 | buf[2] << 8 | buf[3];
}

static u_int
read_be16(u_char *buf)
{
     return buf[0] << 8 | buf[1];
}

static void
write_be16(u_char *buf, u_int b)
{
     buf[0] = b>>8;
     buf[1] = b;
}

static void
write_be24(u_char *buf, u_int b)
{
     buf[0] = b>>16;
     buf[1] = b>>8;
     buf[2] = b;
}

static void
write_be32(u_char *buf, u_int b)
{
     buf[0] = b>>24;
     buf[1] = b>>16;
     buf[2] = b>>8;
     buf[3] = b;
}

static void
dump(u_char *buf, int size)
{
     int i;

     fprintf(stderr, "          0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F");
     for(i=0 ; i<size ; ++i){
	  if(i % 16 == 0)
	       fprintf(stderr, "\n%08x ", i);
	  fprintf(stderr, "%02x ", buf[i] & 0xff);
     }
     fprintf(stderr, "\n");
}

static int
atapi_cmd_with_mode(ATAPI *atapi, u_char *ccb, u_char *buf0, int size, int mode)
{
     struct ata_ioc_request cmd;
     //struct ata_ioc_devices ata_dev;
     int ret;
     char *buf;
     int err;
     int retry;
     int fd;
     //int chan;
     //int dev;
     int atamode;

     buf = malloc(size);

Retry:
     fd = atapi->fd;
     //chan = atapi->chan;
     //dev = atapi->dev;
     err = 0;
     atamode = 0;
     retry = 0;

     //emset(&ata_dev, 0, sizeof(ata_dev));
     memset(&cmd, 0, sizeof(cmd));
     
     /*
     ata_dev.channel = 1;
     ioctl(atapi->fd, IOCATADEVICES, &ata_dev);
     */

     /*
     cmd.channel = chan;
     cmd.device = dev;
     */
     /*
#ifdef FREEBSD5
     cmd.cmd = ATAREQUEST;
#else
     cmd.cmd = ATAPICMD;
#endif
     */

     if(mode & ATAPI_CMD_MODE_READ)
	  atamode |= ATA_CMD_READ;

     if(mode & ATAPI_CMD_MODE_WRITE)
	  atamode |= ATA_CMD_WRITE;

     if(buf0)
	  memcpy(buf, buf0, size);

     memcpy(cmd.u.atapi.ccb, ccb, 16);
     cmd.flags = atamode | ATA_CMD_ATAPI;

     cmd.data = buf;
     cmd.count = size;
     cmd.timeout = 30;

     while(1){
	  ret = ioctl(fd, IOCATAREQUEST, &cmd);
	  if(ret == 0 || retry >= 3 || (ret == -1 && errno == ENOTTY))
	       break;
	  ++retry;
	  fprintf(stderr, "retry..\n");
     }

     if(ret < 0)
	  fprintf(stderr, "errno=%d\n", errno);

     if(ccb[0] != 0x3){
	  err = cmd.error;
	  if(err > 0 || (ret == -1 && errno == ENOTTY)){
	       atapi_request_sense(atapi, 0);
	       err = -1;
	  }
     }

     //NOT READYのときはリトライする
     if(err < 0 && atapi->skey == 2 && atapi->asc == 4){
	  fprintf(stderr, "waiting for ready..\n");

	  if(atapi->sksv)
	       fprintf(stderr, "progress=%5.2f%%\n", 100.0 * atapi->skey_spec / 65536);
	  sleep(1);
	  goto Retry;
     }
     //
     else if(err < 0){
	  fprintf(stderr, "sense key=%x\n", atapi->skey);
	  fprintf(stderr, "asc=%x\n", atapi->asc);
	  fprintf(stderr, "ascq=%x\n", atapi->ascq);
	  if(atapi->sksv)
	       fprintf(stderr, "skey spec=%x\n", atapi->skey_spec);
     }

     if(buf0 && (mode & ATAPI_CMD_MODE_READ))
	  memcpy(buf0, buf, size);

     free(buf);
     return err;
}

int
atapi_cmd(ATAPI *atapi, u_char *ccb, u_char *buf, int size)
{
     if(buf && size >= 0)
	  return atapi_cmd_with_mode(atapi, ccb, buf, size, ATAPI_CMD_MODE_READ);
     else
	  return atapi_cmd_with_mode(atapi, ccb, buf, size, 0);
}

int
atapi_request_sense(ATAPI *atapi, int verb)
{
     int ret;
     u_char ccb[16] = {0x3};
     u_char buf[64];

     ccb[4] = 64;
     if(verb)
	  fprintf(stderr, "REQUEST SENSE:\n");
     ret = atapi_cmd(atapi, ccb, buf, 64);
     if((buf[2] & 0xf) != 0){
	  atapi->skey = buf[2] & 0xf;
	  atapi->asc = buf[12];
	  atapi->ascq = buf[13];
	  atapi->sksv = buf[15] >> 7;
	  atapi->skey_spec = read_be16(buf + 16);
     }
     if(verb || 1)
	  dump(buf, 18 + buf[7]);
     return ret;
}

// READYになるまで待つ
//
// READ DISC INFOを発行することにより実現(See 14.46.1)
// 
int
atapi_wait_for_ready(ATAPI *atapi)
{
     int ret;
     u_char ccb[16] = {
	  0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00
     };

     return atapi_cmd(atapi, ccb, 0, 0);
}

int
atapi_read_disc_info(ATAPI *atapi)
{
     int ret;
     u_char ccb[16] = {0x51};
     u_char buf[256];

     ccb[7] = 0x01;
     ccb[8] = 0x00;

     fprintf(stderr, "READ DISC INFO:\n");
     ret = atapi_cmd(atapi, ccb, buf, 256);

     atapi->info.erasable = buf[2] >> 4;
     atapi->info.disc_status = buf[2];
     atapi->info.n_first_rzone = buf[5] + (buf[10]<<8);
     atapi->info.n_last_rzone = buf[6] + (buf[11]<<8);

     fprintf(stderr, "number of first rzone=%d\n", buf[3]);
     fprintf(stderr, "number of borders=%d\n", buf[4] + (buf[9]<<8));
     fprintf(stderr, "erasable=%d\n", atapi->info.erasable);
     fprintf(stderr, "disc_status=%d\n", atapi->info.disc_status);
     fprintf(stderr, "n_first_rzone=%d\n", atapi->info.n_first_rzone);
     fprintf(stderr, "n_last_rzone=%d\n", atapi->info.n_last_rzone);

     return ret;
}

int
atapi_read_rma(ATAPI *atapi)
{
     int ret;
     u_char ccb[16] = {0xad};
     u_char buf[32768];
     
     write_be32(ccb+2, 1);
     ccb[6] = 0;	//Layer Number
     ccb[7] = 0x00;	//RMD in the last Border-out
     write_be16(ccb+8, 32768);
     fprintf(stderr, "READ DVD STRUCTURE:\n");
     dump(ccb, 16);
     ret = atapi_cmd_with_mode(atapi, ccb, buf, 32768, ATAPI_CMD_MODE_READ);
     dump(buf, (buf[0]<<8) + buf[1] + 2);
     atapi->info.rmd_format = (buf[8]<<8) + buf[9];
     atapi->info.rmd_disc_status = buf[10];
     fprintf(stderr, "rmd format=%d\n", atapi->info.rmd_format);
     fprintf(stderr, "rmd disc status=%x\n", atapi->info.rmd_disc_status);

     return ret;
}

int
atapi_close_border(ATAPI *atapi)
{
     int ret;
     u_char ccb[16] = {0x5b};
     u_char buf[256];
     
     ccb[1] = 1;
     ccb[2] = 2;	//TRACK/RZONE
     ccb[4] = 0;
     ccb[5] = 0;
     fprintf(stderr, "CLOSE BORDER:\n");
     ret = atapi_cmd(atapi, ccb, buf, 256);

     return ret;
}

int
atapi_close_rzone(ATAPI *atapi, int rzone)
{
     int ret;
     u_char ccb[16] = {0x5b};
     u_char buf[256];
     
     ccb[1] = 1;
     ccb[2] = 1;	//TRACK/RZONE
     ccb[4] = rzone>>8;
     ccb[5] = rzone;
     fprintf(stderr, "CLOSE RZONE:\n");
     ret = atapi_cmd(atapi, ccb, buf, 256);

     return ret;
}

int
atapi_read_rzone(ATAPI *atapi, int rzone)
{
     int ret;
     u_char ccb[16] = {0x52};
     u_char buf[256];
     DVD_RZONE *z;

     ccb[1] = 1;
     ccb[2] = rzone>>24;
     ccb[3] = rzone>>16;
     ccb[4] = rzone>>8;
     ccb[5] = rzone;
     ccb[7] = 0x01;
     ccb[8] = 0x00;

     fprintf(stderr, "READ RZONE:\n");
     ret = atapi_cmd(atapi, ccb, buf, 256);

     dump(buf, 34);
     z = atapi->info.rzone + rzone;

     z->rt = buf[6]>>7;
     z->blank = buf[6]>>6;
     z->packet_inc = buf[6]>>5;
     z->fp = buf[6]>>4;
     z->data_mode = buf[6];
     z->lra_v = buf[7]>>1;
     z->nwa_v = buf[7];

     z->start_adr = read_be32(buf + 8);
     z->nwa = read_be32(buf + 12);
     z->free_blocks = read_be32(buf + 16);
     z->packet_size = read_be32(buf + 20);
     z->rzone_size = read_be32(buf + 24);
     z->last_rec_adr = read_be32(buf + 28);

     fprintf(stderr, "rzone:%d rt=%d\n", rzone, z->rt);
     fprintf(stderr, "rzone:%d blank=%d\n", rzone, z->blank);
     fprintf(stderr, "rzone:%d packet_inc=%d\n", rzone, z->packet_inc);
     fprintf(stderr, "rzone:%d fp=%d\n", rzone, z->fp);
     fprintf(stderr, "rzone:%d data_mode=%d\n", rzone, z->data_mode);
     fprintf(stderr, "rzone:%d lra_v=%d\n", rzone, z->lra_v);
     fprintf(stderr, "rzone:%d nwa_v=%d\n", rzone, z->nwa_v);

     fprintf(stderr, "rzone:%d start_adr=%d\n", rzone, z->start_adr);
     fprintf(stderr, "rzone:%d nwa=%d\n", rzone, z->nwa);
     fprintf(stderr, "rzone:%d free_blocks=%d\n", rzone, z->free_blocks);
     fprintf(stderr, "rzone:%d packet_size=%d\n", rzone, z->packet_size);
     fprintf(stderr, "rzone:%d rzone_size=%d\n", rzone, z->rzone_size);
     fprintf(stderr, "rzone:%d last_rec_adr=%d\n", rzone, z->last_rec_adr);

     return ret;
}

int
atapi_mode_sense(ATAPI *ata, int page, u_char *buf_ret, int buf_size)
{
     int ret;
     u_char ccb[16] = {0x5a};//MODE SENSE
     u_char *buf;

     buf = malloc(buf_size);
     ccb[2] = page;
     ccb[7] = buf_size >> 8;
     ccb[8] = buf_size;

     fprintf(stderr, "MODE SENSE:\n");
     ret = atapi_cmd(ata, ccb, buf, buf_size);
     memcpy(buf_ret, buf, buf_size);

     free(buf);
     return ret;
}

int
atapi_mode_select(ATAPI *ata, u_char *param)
{
     int ret;
     u_char ccb[16] = {0x55, 0x10};//MODE SELECT
     int size = param[1];

     size += 2;
     ccb[7] = size >> 8;
     ccb[8] = size;

     fprintf(stderr, "MODE SELECT:\n");
     memset(param + 2, 0, 6);
     ret = atapi_cmd_with_mode(ata, ccb, param, size, ATAPI_CMD_MODE_WRITE);

     return ret;
}

int
atapi_mode_select_write_type(ATAPI *ata, int write_type)
{
     u_char buf[58];

     atapi_mode_sense(ata, 0x05, buf, sizeof(buf));
     dump(buf, sizeof(buf));
     buf[8+2] &= ~0xf;
     buf[8+2] |= write_type;
     return atapi_mode_select(ata, buf);
}

int
atapi_mode_select_border_close(ATAPI *ata)
{
     int ret;
     u_char ccb[16] = {0x5a};//MODE SENSE
     u_char buf[256];

     ccb[2] = 0x05; // C/DVD Capabilities
     ccb[7] = 0x01; // 256	
     ccb[8] = 0x00;

     fprintf(stderr, "MODE SENSE WRITE PARAMETERS:\n");
     ret = atapi_cmd(ata, ccb, buf, 256);
     dump(buf, 58);

     ccb[0] = 0x55;
//     ccb[1] = 1;
     ccb[7] = 0x00;
     ccb[8] = 58;
     buf[8+3] &= 0x3f;
     dump(buf, 58);
     fprintf(stderr, "MODE SELECT WRITE PARAMETERS:\n");
     ret = atapi_cmd_with_mode(ata, ccb, buf, 58, ATAPI_CMD_MODE_WRITE);

     return ret;
}

int
atapi_mode_sense_write_parameters(ATAPI *ata)
{
     int ret;
     u_char ccb[16] = {0x5a};//MODE SENSE
     u_char buf[256];

     ccb[2] = 0x05; // C/DVD Capabilities
     ccb[7] = 0x01; // 256	
     ccb[8] = 0x00;

     fprintf(stderr, "MODE SENSE WRITE PARAMETERS:\n");
     ret = atapi_cmd(ata, ccb, buf, 256);

     dump(buf+8, 52);

     return ret;
}

int
atapi_mode_sense_dvd_capabilities(ATAPI *ata)
{
     int ret;
     u_char buf[256];

     fprintf(stderr, "MODE SENSE DVD CAPABILITIES:\n");
     ret = atapi_mode_sense(ata, 0x2a, buf, 256);

     dump(buf + 8, buf[1] + 2 - 8);

     if(buf[10] & 0x20)
	  fprintf(stderr, "DVD-RAM READ\n");
     if(buf[10] & 0x10)
	  fprintf(stderr, "DVD-R READ\n");
     if(buf[10] & 0x8)
	  fprintf(stderr, "DVD-ROM READ\n");
     if(buf[10] & 0x4)
	  fprintf(stderr, "Method2\n");
     if(buf[10] & 0x2)
	  fprintf(stderr, "CD-RW Rd\n");
     if(buf[10] & 0x1)
	  fprintf(stderr, "CD-R Rd\n");

     if(buf[11] & 0x20)
	  fprintf(stderr, "DVD-RAM WRITE\n");
     if(buf[11] & 0x10)
	  fprintf(stderr, "DVD-R WRITE\n");
     if(buf[11] & 0x4)
	  fprintf(stderr, "Test Write\n");
     if(buf[11] & 0x2)
	  fprintf(stderr, "CD-RW Wr\n");
     if(buf[11] & 0x1)
	  fprintf(stderr, "CD-R Wr\n");

     return ret;
}

int
atapi_reserve_rzone(ATAPI *ata, int size)
{
     u_char ccb[16] = {0x53};
     write_be32(ccb+5, size);

     return atapi_cmd(ata, ccb, 0, 0);
}

int
atapi_test_ready(ATAPI *ata)
{
     u_char ccb[16] = {0x00};

     fprintf(stderr, "TEST READY:\n");

     return atapi_cmd(ata, ccb, 0, 0);
}

int
atapi_read_capacity(ATAPI *atapi)
{
     int ret;
     u_char buf[8];
     u_char ccb[16] = {0x25};

     fprintf(stderr, "READ CAPACITY:\n");
     ret = atapi_cmd(atapi, ccb, buf, 8);
     if(ret < 0)
	  return ret;

     atapi->info.capacity = buf[0]<<24 | buf[1] <<16 | buf[2] << 8 | buf[3];
     atapi->info.block_len = buf[4]<<24 | buf[5] <<16 | buf[6] << 8 | buf[7];

     fprintf(stderr, "capacity=%d\n", atapi->info.capacity);
     fprintf(stderr, "block_len=%d\n", atapi->info.block_len);
     return ret;
}

int
atapi_blank(ATAPI *ata, int type, int arg)
{
     int ret;
     u_char ccb[16] = {0xa1};

     ccb[1] = 0x10 | type;	// MANDATORY BLANK THE DISC
     write_be32(ccb+2, arg);

     fprintf(stderr, "BLANK:\n");
     ret = atapi_cmd(ata, ccb, 0, 0);

     return ret;
}

int
atapi_start(ATAPI *ata, int start)
{
     u_char ccb[16] = {0x1b};
     ccb[4] = start;

     fprintf(stderr, "START:\n");
     return atapi_cmd(ata, ccb, 0, 0);
}

int
atapi_seek(ATAPI *ata, u_int sec)
{
     u_char ccb[16] = {0x2b};
     ccb[2] = sec >> 24;
     ccb[3] = sec >> 16;
     ccb[4] = sec >> 8;
     ccb[5] = sec;
     
     fprintf(stderr, "SEEK:\n");
     return atapi_cmd(ata, ccb, 0, 0);
}

int
atapi_read10(ATAPI *ata, u_int sec, u_char *buf, int nsec)
{
     u_char ccb[16] = {0x28};
     ccb[2] = sec >> 24;
     ccb[3] = sec >> 16;
     ccb[4] = sec >> 8;
     ccb[5] = sec;
     ccb[7] = nsec >> 8;
     ccb[8] = nsec;

     fprintf(stderr, "READ10 (sec=%d, nsec=%d):\n", sec, nsec);
     return atapi_cmd(ata, ccb, buf, nsec * 2048);
}

int
atapi_write10(ATAPI *ata, u_int sec, u_char *buf, int nsec)
{
     u_char ccb[16] = {0x2a};
     ccb[2] = sec >> 24;
     ccb[3] = sec >> 16;
     ccb[4] = sec >> 8;
     ccb[5] = sec;
     ccb[7] = nsec >> 8;
     ccb[8] = nsec;

     fprintf(stderr, "WRITE10 (sec=%d, nsec=%d):\n", sec, nsec);
     return atapi_cmd_with_mode(ata, ccb, buf, nsec * 2048, ATAPI_CMD_MODE_WRITE);
}

int
atapi_format_unit(ATAPI *ata, u_int type, u_int n)
{
     u_char ccb[16] = {0x04, 0x11};
     u_char param[12] = {0x00, 0x02, 0x00, 0x08};

     param[4+4] = (type<<2);
     if(type == 0){
	  n = 2297888;
	  write_be32(param+4, n);
	  write_be24(param+4+5, 0x800);
     }
     else if(type == 0x15){
	  write_be32(param+4, n);
	  write_be24(param+4+5, 16);
     }

     dump(param, 12);

     fprintf(stderr, "FORMAT_UNIT(type=%d):\n", type);
     return atapi_cmd_with_mode(ata, ccb, param, 12, ATAPI_CMD_MODE_WRITE);
}

static char read_buf[2048*32];

void
test_disc(ATAPI *atapi)
{
     int i;

     while(atapi_start(atapi, 3) !=0)// TRAY CLOSE & LOAD
	  sleep(1);
     //
     //あんまり意味ないかもしれないが、いろいろ調査
     //
     atapi_test_ready(atapi);
     atapi_mode_sense_dvd_capabilities(atapi);	// CHECK DRIVE CAPABILITIES
     atapi_mode_sense_write_parameters(atapi);	
     atapi_read_capacity(atapi); // CHECK DVD CAPACITY
     //
     //RZONEを表示してみたりする
     //
     atapi_read_disc_info(atapi); // CHECK DVD INFO
     for(i = atapi->info.n_first_rzone ; i <= atapi->info.n_last_rzone ; ++i)
	  if(i > 0)
	       atapi_read_rzone(atapi, i);

}

// 消去試験
void
test_blank(ATAPI *atapi, int type)
{
     while(atapi_start(atapi, 3) !=0)// TRAY CLOSE & LOAD
	  sleep(1);

     atapi_blank(atapi, type, 0);    // BLANK (SEE 14.1)
#if 0
     while(atapi_start(atapi, 2) !=0)//STOP & ELECT
	  sleep(1);
#endif
}

/**
   Disc At Onceの試験
 */
void
test_disc_at_once(ATAPI *atapi, char *file)
{
     while(atapi_start(atapi, 3) !=0)// TRAY CLOSE & LOAD
	  sleep(1);

     //
     // 書きモードを設定 
     //
     atapi_mode_select_write_type(atapi, 2);//DISC AT ONCE
     if(1){
	  int fd;
	  int sec;
	  int nsec;
	  int len;
	  off_t off;

	  fd = open(file, O_RDONLY);
	  off = lseek(fd, 0, SEEK_END);
	  nsec = off / 2048;
	  fprintf(stderr, "FILE SIZE=%d(sec)\n", nsec);

	  //RZONE確保
	  atapi_reserve_rzone(atapi, nsec);
	  off = lseek(fd, 0, SEEK_SET);
	  sec = 0;

	  while(1){
	       len = read(fd, read_buf, 2048*16);
	       if(len <= 0)
		    break;
	       if(sec % 1024 == 0)
		    fprintf(stderr, "%5.2f%%\n", 100.0 * sec / nsec);

	       while(atapi_write10(atapi, sec, read_buf, len/2048) != 0)
		    sleep(1);
	       
	       sec += len/2048;
	  }

	  close(fd);
     }

     while(atapi_start(atapi, 0) !=0)//STOP
	  sleep(1);
}

/**
   Restricted Over Writeの試験。

   事前に FORMATしておく必要があるかも
 */
void
test_restricted_over_write(ATAPI *atapi)
{
     while(atapi_start(atapi, 3) !=0)// TRAY CLOSE & LOAD
	  sleep(1);

     if(1){
	  int fd;
	  int sec;
	  int nsec;
	  int len;
	  off_t off;

	  fd = open("/export3/T3.IMG", O_RDONLY);
	  off = lseek(fd, 0, SEEK_END);
	  nsec = off / 2048;
	  fprintf(stderr, "FILE SIZE=%d(sec)\n", nsec);

	  off = lseek(fd, 0, SEEK_SET);
	  sec = 0;
	  while(1){
	       len = read(fd, read_buf, 2048*16);
	       if(len <= 0)
		    break;
	       if(sec % 1024 == 0)
		    fprintf(stderr, "%5.2f%%\n", 100.0 * sec / nsec);

	       while(atapi_write10(atapi, sec, read_buf, len/2048) != 0)
		    sleep(1);
	       
	       sec += len/2048;
	  }

	  close(fd);
     }

     atapi_wait_for_ready(atapi);
}

void
test_picking(ATAPI *atapi, int sec, int nsec)
{
     int fd;
     int len;

     fd = creat("/export3/T3.IMG", 0666);
     while(nsec > 0){
	  len = nsec > 16 ? 16 : nsec;
	  while(atapi_read10(atapi, sec, read_buf, len) != 0)
	       sleep(1);
	  write(fd, read_buf, len*2048);
	  nsec -= len;
	  sec += len;
     }
     close(fd);
     atapi_wait_for_ready(atapi);
}

int
main(int argc, char *argv[])
{
     ATAPI atapi = {0};
     if(argc < 3){
	 fprintf(stderr, "Usage: ata <device> <iso imagefile>\n");
	 exit(0);
     }

     atapi.fd = open(argv[1], O_RDWR);
     fprintf(stderr, "fd=%d\n", atapi.fd);

     test_disc(&atapi);
     test_disc_at_once(&atapi, argv[2]);
//     test_blank(&atapi, 0);
#if 0
     test_blank(&atapi, 1);
     atapi_wait_for_ready(&atapi);
     atapi_format_unit(&atapi, 0x15, 46544);
     atapi_wait_for_ready(&atapi);
#endif
//     test_restricted_over_write(&atapi);

//     atapi_format_unit(&atapi, 0x15, 10124);
//     atapi_format_unit(&atapi, 0x15,   2297888);
     atapi_read_rma(&atapi);
//     test_picking(&atapi, 0, atapi.info.rzone[1].nwa);
#if 0
     while(atapi_start(&atapi, 2) !=0)//STOP & ELECT
	  sleep(1);
#endif

     atapi_wait_for_ready(&atapi);

     close(atapi.fd);

     return 0;
}


