/*
* This file has been modified for the cdrkit suite.
*
* The behaviour and appearence of the program code below can differ to a major
* extent from the version distributed by the original author(s).
*
* For details, see Changelog file distributed with the cdrkit package. If you
* received this file from another source then ask the distributing person for
* a log of modifications.
*
*/
/* @(#)drv_simul.c 1.48 05/05/16 Copyright 1998-2005 J. Schilling */
/*
* Simulation device driver
*
* Copyright (c) 1998-2005 J. Schilling
*/
/*
* This program is free software; you can redistribute it and/or modify
* 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
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; see the file COPYING. If not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef DEBUG
#define DEBUG
#endif
#include <mconfig.h>
#include <stdio.h>
#include <standard.h>
#include <stdxlib.h>
#include <unixstd.h>
#include <errno.h>
#include <strdefs.h>
#include <timedefs.h>
#include <utypes.h>
#include <btorder.h>
#include <schily.h>
/*#include <usalio.h>*/
#include <usal/scsidefs.h>
#include <usal/scsireg.h>
#include <usal/scsitransp.h>
#include <libport.h>
#include "wodim.h"
extern int silent;
extern int verbose;
extern int lverbose;
static int simul_load(SCSI *usalp, cdr_t *);
static int simul_unload(SCSI *usalp, cdr_t *);
static cdr_t *identify_simul(SCSI *usalp, cdr_t *, struct scsi_inquiry *);
static int init_simul(SCSI *usalp, cdr_t *dp);
static int getdisktype_simul(SCSI *usalp, cdr_t *dp);
static int speed_select_simul(SCSI *usalp, cdr_t *dp, int *speedp);
static int next_wr_addr_simul(SCSI *usalp, track_t *trackp, long *ap);
static int cdr_write_simul(SCSI *usalp, caddr_t bp, long sectaddr, long size,
int blocks, BOOL islast);
static int open_track_simul(SCSI *usalp, cdr_t *dp, track_t *trackp);
static int close_track_simul(SCSI *usalp, cdr_t *dp, track_t *trackp);
static int open_session_simul(SCSI *usalp, cdr_t *dp, track_t *trackp);
static int fixate_simul(SCSI *usalp, cdr_t *dp, track_t *trackp);
static void tv_sub(struct timeval *tvp1, struct timeval *tvp2);
static int simul_load(SCSI *usalp, cdr_t *dp)
{
return (0);
}
static int simul_unload(SCSI *usalp, cdr_t *dp)
{
return (0);
}
cdr_t cdr_cdr_simul = {
0, 0,
CDR_TAO|CDR_SAO|CDR_PACKET|CDR_RAW|CDR_RAW16|CDR_RAW96P|CDR_RAW96R|CDR_SRAW96P|CDR_SRAW96R|CDR_TRAYLOAD|CDR_SIMUL,
CDR_CDRW_ALL,
40, 372,
"cdr_simul",
"simulation CD-R driver for timing/speed tests",
0,
(dstat_t *)0,
identify_simul,
drive_attach,
init_simul,
getdisktype_simul,
simul_load,
simul_unload,
buf_dummy,
cmd_dummy, /* recovery_needed */
(int(*)(SCSI *, cdr_t *, int))cmd_dummy, /* recover */
speed_select_simul,
select_secsize,
next_wr_addr_simul,
(int(*)(SCSI *, Ulong))cmd_ill, /* reserve_track */
cdr_write_simul,
(int(*)(track_t *, void *, BOOL))cmd_dummy, /* gen_cue */
(int(*)(SCSI *usalp, cdr_t *, track_t *))cmd_dummy, /* send_cue */
(int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy, /* leadin */
open_track_simul,
close_track_simul,
open_session_simul,
cmd_dummy,
cmd_dummy, /* abort */
read_session_offset,
fixate_simul,
cmd_dummy, /* stats */
blank_dummy,
format_dummy,
(int(*)(SCSI *, caddr_t, int, int))NULL, /* no OPC */
cmd_dummy, /* opt1 */
cmd_dummy, /* opt2 */
};
cdr_t cdr_dvd_simul = {
0, 0,
CDR_TAO|CDR_SAO|CDR_PACKET|CDR_RAW|CDR_RAW16|CDR_RAW96P|CDR_RAW96R|CDR_SRAW96P|CDR_SRAW96R|CDR_DVD|CDR_TRAYLOAD|CDR_SIMUL,
CDR_CDRW_ALL,
2, 1000,
"dvd_simul",
"simulation DVD-R driver for timing/speed tests",
0,
(dstat_t *)0,
identify_simul,
drive_attach,
init_simul,
getdisktype_simul,
simul_load,
simul_unload,
buf_dummy,
cmd_dummy, /* recovery_needed */
(int(*)(SCSI *, cdr_t *, int))cmd_dummy, /* recover */
speed_select_simul,
select_secsize,
next_wr_addr_simul,
(int(*)(SCSI *, Ulong))cmd_ill, /* reserve_track */
cdr_write_simul,
(int(*)(track_t *, void *, BOOL))cmd_dummy, /* gen_cue */
(int(*)(SCSI *usalp, cdr_t *, track_t *))cmd_dummy, /* send_cue */
(int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy, /* leadin */
open_track_simul,
close_track_simul,
open_session_simul,
cmd_dummy,
cmd_dummy, /* abort */
read_session_offset,
fixate_simul,
cmd_dummy, /* stats */
blank_dummy,
format_dummy,
(int(*)(SCSI *, caddr_t, int, int))NULL, /* no OPC */
cmd_dummy, /* opt1 */
cmd_dummy, /* opt2 */
};
static cdr_t *
identify_simul(SCSI *usalp, cdr_t *dp, struct scsi_inquiry *ip)
{
return (dp);
}
static long simul_nwa;
static int simul_speed = 1;
static int simul_dummy;
static int simul_isdvd;
static int simul_bufsize = 1024;
static Uint sleep_rest;
static Uint sleep_max;
static Uint sleep_min;
static int
init_simul(SCSI *usalp, cdr_t *dp)
{
return (speed_select_simul(usalp, dp, NULL));
}
static int
getdisktype_simul(SCSI *usalp, cdr_t *dp)
{
dstat_t *dsp = dp->cdr_dstat;
if (strcmp(dp->cdr_drname, cdr_cdr_simul.cdr_drname) == 0) {
dsp->ds_maxblocks = 333000;
simul_isdvd = FALSE;
} else {
dsp->ds_maxblocks = 2464153; /* 4.7 GB */
/* dsp->ds_maxblocks = 1927896;*/ /* 3.95 GB */
dsp->ds_flags |= DSF_DVD;
simul_isdvd = TRUE;
}
return (drive_getdisktype(usalp, dp));
}
static int
speed_select_simul(SCSI *usalp, cdr_t *dp, int *speedp)
{
long val;
char *p;
BOOL dummy = (dp->cdr_cmdflags & F_DUMMY) != 0;
if (speedp)
simul_speed = *speedp;
simul_dummy = dummy;
if ((p = getenv("CDR_SIMUL_BUFSIZE")) != NULL) {
if (getnum(p, &val) == 1)
simul_bufsize = val / 1024;
}
/*
* sleep_max is the time to empty the drive's buffer in µs.
* sector size is from 2048 bytes to 2352 bytes.
* If sector size is 2048 bytes, 1k takes 6.666 ms.
* If sector size is 2352 bytes, 1k takes 5.805 ms.
* We take the 6 ms as an average between both values.
* simul_bufsize is the number of kilobytes in drive buffer.
*/
sleep_max = 6 * 1000 * simul_bufsize / simul_speed;
/*
* DVD single speed is 1385 * 1000 Bytes/s (676.27 sectors/s)
*/
if ((dp->cdr_flags & CDR_DVD) != 0)
sleep_max = 739 * simul_bufsize / simul_speed;
if (lverbose) {
printf("Simulation drive buffer size: %d KB\n", simul_bufsize);
printf("Maximum reserve time in drive buffer: %d.%3.3d ms for speed %dx\n",
sleep_max / 1000,
sleep_max % 1000,
simul_speed);
}
return (0);
}
static int
next_wr_addr_simul(SCSI *usalp, track_t *trackp, long *ap)
{
/*
* This will most likely not 100% correct for TAO CDs
* but it is better than returning 0 in all cases.
*/
if (ap)
*ap = simul_nwa;
return (0);
}
static int
cdr_write_simul(SCSI *usalp, caddr_t bp /* address of buffer */,
long sectaddr /* disk address (sector) to put */,
long size /* number of bytes to transfer */,
int blocks /* sector count */,
BOOL islast /* last write for track */)
{
Uint sleep_time;
Uint sleep_diff;
struct timeval tv1;
static struct timeval tv2;
if (lverbose > 1 && islast)
printf("\nWriting last record for this track.\n");
simul_nwa += blocks;
gettimeofday(&tv1, (struct timezone *)0);
if (tv2.tv_sec != 0) { /* Already did gettimeofday(&tv2) */
tv_sub(&tv1, &tv2);
if (sleep_rest != 0) {
sleep_diff = tv1.tv_sec * 1000000 + tv1.tv_usec;
if (sleep_min > (sleep_rest - sleep_diff))
sleep_min = (sleep_rest - sleep_diff);
if (sleep_diff > sleep_rest) {
printf("Buffer underrun: actual delay was %d.%3.3d ms, max delay was %d.%3.3d ms.\n",
sleep_diff / 1000,
sleep_diff % 1000,
sleep_rest / 1000,
sleep_rest % 1000);
if (!simul_dummy)
return (-1);
}
/*
* If we spent time outside the write function
* subtract this time.
*/
sleep_diff = tv1.tv_sec * 1000000 + tv1.tv_usec;
if (sleep_rest >= sleep_diff)
sleep_rest -= sleep_diff;
else
sleep_rest = 0;
}
}
/*
* Speed 1 ist 150 Sektoren/s
* Bei DVD 767.27 Sektoren/s
*/
sleep_time = 1000000 * blocks / 75 / simul_speed;
if (simul_isdvd)
sleep_time = 1000000 * blocks / 676 / simul_speed;
sleep_time += sleep_rest;
if (sleep_time > sleep_max) {
int mod;
long rsleep;
sleep_rest = sleep_max;
sleep_time -= sleep_rest;
mod = sleep_time % 20000;
sleep_rest += mod;
sleep_time -= mod;
if (sleep_time > 0) {
gettimeofday(&tv1, (struct timezone *)0);
usleep(sleep_time);
gettimeofday(&tv2, (struct timezone *)0);
tv2.tv_sec -= tv1.tv_sec;
tv2.tv_usec -= tv1.tv_usec;
rsleep = tv2.tv_sec * 1000000 + tv2.tv_usec;
sleep_rest -= rsleep - sleep_time;
}
} else {
sleep_rest = sleep_time;
}
gettimeofday(&tv2, (struct timezone *)0);
return (size);
}
static int
open_track_simul(SCSI *usalp, cdr_t *dp, track_t *trackp)
{
sleep_min = 999 * 1000000;
return (0);
}
static int
close_track_simul(SCSI *usalp, cdr_t *dp, track_t *trackp)
{
if (lverbose) {
printf("Remaining reserve time in drive buffer: %d.%3.3d ms\n",
sleep_rest / 1000,
sleep_rest % 1000);
printf("Minimum reserve time in drive buffer: %d.%3.3d ms\n",
sleep_min / 1000,
sleep_min % 1000);
}
usleep(sleep_rest);
sleep_rest = 0;
return (0);
}
static int
open_session_simul(SCSI *usalp, cdr_t *dp, track_t *trackp)
{
simul_nwa = 0L;
return (0);
}
static int
fixate_simul(SCSI *usalp, cdr_t *dp, track_t *trackp)
{
return (0);
}
static void
tv_sub(struct timeval *tvp1, struct timeval *tvp2)
{
tvp1->tv_sec -= tvp2->tv_sec;
tvp1->tv_usec -= tvp2->tv_usec;
while (tvp1->tv_usec < 0) {
tvp1->tv_usec += 1000000;
tvp1->tv_sec -= 1;
}
}
syntax highlighted by Code2HTML, v. 0.9.1