Imported Upstream version 1.1.11
[platform/upstream/cdrkit.git] / wodim / drv_simul.c
1 /*
2  * This file has been modified for the cdrkit suite.
3  *
4  * The behaviour and appearence of the program code below can differ to a major
5  * extent from the version distributed by the original author(s).
6  *
7  * For details, see Changelog file distributed with the cdrkit package. If you
8  * received this file from another source then ask the distributing person for
9  * a log of modifications.
10  *
11  */
12
13 /* @(#)drv_simul.c      1.48 05/05/16 Copyright 1998-2005 J. Schilling */
14 /*
15  *      Simulation device driver
16  *
17  *      Copyright (c) 1998-2005 J. Schilling
18  */
19 /*
20  * This program is free software; you can redistribute it and/or modify
21  * it under the terms of the GNU General Public License version 2
22  * as published by the Free Software Foundation.
23  *
24  * This program is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27  * GNU General Public License for more details.
28  *
29  * You should have received a copy of the GNU General Public License along with
30  * this program; see the file COPYING.  If not, write to the Free Software
31  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32  */
33
34 #ifndef DEBUG
35 #define DEBUG
36 #endif
37 #include <mconfig.h>
38
39 #include <stdio.h>
40 #include <standard.h>
41 #include <stdxlib.h>
42 #include <unixstd.h>
43 #include <errno.h>
44 #include <strdefs.h>
45 #include <timedefs.h>
46 #include <utypes.h>
47 #include <btorder.h>
48 #include <schily.h>
49
50 /*#include <usalio.h>*/
51 #include <usal/scsidefs.h>
52 #include <usal/scsireg.h>
53 #include <usal/scsitransp.h>
54
55 #include <libport.h>
56
57 #include "wodim.h"
58
59 extern  int     silent;
60 extern  int     verbose;
61 extern  int     lverbose;
62
63 static  int     simul_load(SCSI *usalp, cdr_t *);
64 static  int     simul_unload(SCSI *usalp, cdr_t *);
65 static  cdr_t   *identify_simul(SCSI *usalp, cdr_t *, struct scsi_inquiry *);
66 static  int     init_simul(SCSI *usalp, cdr_t *dp);
67 static  int     getdisktype_simul(SCSI *usalp, cdr_t *dp);
68 static  int     speed_select_simul(SCSI *usalp, cdr_t *dp, int *speedp);
69 static  int     next_wr_addr_simul(SCSI *usalp, track_t *trackp, long *ap);
70 static  int     cdr_write_simul(SCSI *usalp, caddr_t bp, long sectaddr, long size, 
71                                                                                  int blocks, BOOL islast);
72 static  int     open_track_simul(SCSI *usalp, cdr_t *dp, track_t *trackp);
73 static  int     close_track_simul(SCSI *usalp, cdr_t *dp, track_t *trackp);
74 static  int     open_session_simul(SCSI *usalp, cdr_t *dp, track_t *trackp);
75 static  int     fixate_simul(SCSI *usalp, cdr_t *dp, track_t *trackp);
76 static  void    tv_sub(struct timeval *tvp1, struct timeval *tvp2);
77
78 static int simul_load(SCSI *usalp, cdr_t *dp)
79 {
80         return (0);
81 }
82
83 static int simul_unload(SCSI *usalp, cdr_t *dp)
84 {
85         return (0);
86 }
87
88 cdr_t   cdr_cdr_simul = {
89         0, 0,
90         CDR_TAO|CDR_SAO|CDR_PACKET|CDR_RAW|CDR_RAW16|CDR_RAW96P|CDR_RAW96R|CDR_SRAW96P|CDR_SRAW96R|CDR_TRAYLOAD|CDR_SIMUL,
91         CDR_CDRW_ALL,
92         40, 372,
93         "cdr_simul",
94         "simulation CD-R driver for timing/speed tests",
95         0,
96         (dstat_t *)0,
97         identify_simul,
98         drive_attach,
99         init_simul,
100         getdisktype_simul,
101         simul_load,
102         simul_unload,
103         buf_dummy,
104         cmd_dummy,                                      /* recovery_needed */
105         (int(*)(SCSI *, cdr_t *, int))cmd_dummy,        /* recover      */
106         speed_select_simul,
107         select_secsize,
108         next_wr_addr_simul,
109         (int(*)(SCSI *, Ulong))cmd_ill, /* reserve_track        */
110         cdr_write_simul,
111         (int(*)(track_t *, void *, BOOL))cmd_dummy,     /* gen_cue */
112         (int(*)(SCSI *usalp, cdr_t *, track_t *))cmd_dummy, /* send_cue */
113         (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy, /* leadin */
114         open_track_simul,
115         close_track_simul,
116         open_session_simul,
117         cmd_dummy,
118         cmd_dummy,                                      /* abort        */
119         read_session_offset,
120         fixate_simul,
121         cmd_dummy,                                      /* stats        */
122         blank_dummy,
123         format_dummy,
124         (int(*)(SCSI *, caddr_t, int, int))NULL,        /* no OPC       */
125         cmd_dummy,                                      /* opt1         */
126         cmd_dummy,                                      /* opt2         */
127 };
128
129 cdr_t   cdr_dvd_simul = {
130         0, 0,
131         CDR_TAO|CDR_SAO|CDR_PACKET|CDR_RAW|CDR_RAW16|CDR_RAW96P|CDR_RAW96R|CDR_SRAW96P|CDR_SRAW96R|CDR_DVD|CDR_TRAYLOAD|CDR_SIMUL,
132         CDR_CDRW_ALL,
133         2, 1000,
134         "dvd_simul",
135         "simulation DVD-R driver for timing/speed tests",
136         0,
137         (dstat_t *)0,
138         identify_simul,
139         drive_attach,
140         init_simul,
141         getdisktype_simul,
142         simul_load,
143         simul_unload,
144         buf_dummy,
145         cmd_dummy,                                      /* recovery_needed */
146         (int(*)(SCSI *, cdr_t *, int))cmd_dummy,        /* recover      */
147         speed_select_simul,
148         select_secsize,
149         next_wr_addr_simul,
150         (int(*)(SCSI *, Ulong))cmd_ill, /* reserve_track        */
151         cdr_write_simul,
152         (int(*)(track_t *, void *, BOOL))cmd_dummy,     /* gen_cue */
153         (int(*)(SCSI *usalp, cdr_t *, track_t *))cmd_dummy, /* send_cue */
154         (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy, /* leadin */
155         open_track_simul,
156         close_track_simul,
157         open_session_simul,
158         cmd_dummy,
159         cmd_dummy,                                      /* abort        */
160         read_session_offset,
161         fixate_simul,
162         cmd_dummy,                                      /* stats        */
163         blank_dummy,
164         format_dummy,
165         (int(*)(SCSI *, caddr_t, int, int))NULL,        /* no OPC       */
166         cmd_dummy,                                      /* opt1         */
167         cmd_dummy,                                      /* opt2         */
168 };
169
170 static cdr_t *
171 identify_simul(SCSI *usalp, cdr_t *dp, struct scsi_inquiry *ip)
172 {
173         return (dp);
174 }
175
176 static  long    simul_nwa;
177 static  int     simul_speed = 1;
178 static  int     simul_dummy;
179 static  int     simul_isdvd;
180 static  int     simul_bufsize = 1024;
181 static  Uint    sleep_rest;
182 static  Uint    sleep_max;
183 static  Uint    sleep_min;
184
185 static int
186 init_simul(SCSI *usalp, cdr_t *dp)
187 {
188         return (speed_select_simul(usalp, dp, NULL));
189 }
190
191 static int
192 getdisktype_simul(SCSI *usalp, cdr_t *dp)
193 {
194         dstat_t *dsp = dp->cdr_dstat;
195
196         if (strcmp(dp->cdr_drname, cdr_cdr_simul.cdr_drname) == 0) {
197                 dsp->ds_maxblocks = 333000;
198                 simul_isdvd = FALSE;
199         } else {
200                 dsp->ds_maxblocks = 2464153;    /* 4.7 GB  */
201 /*              dsp->ds_maxblocks = 1927896;*/  /* 3.95 GB */
202                 dsp->ds_flags |= DSF_DVD;
203                 simul_isdvd = TRUE;
204         }
205         return (drive_getdisktype(usalp, dp));
206 }
207
208
209 static int
210 speed_select_simul(SCSI *usalp, cdr_t *dp, int *speedp)
211 {
212         long    val;
213         char    *p;
214         BOOL    dummy = (dp->cdr_cmdflags & F_DUMMY) != 0;
215
216         if (speedp)
217                 simul_speed = *speedp;
218         simul_dummy = dummy;
219
220         if ((p = getenv("CDR_SIMUL_BUFSIZE")) != NULL) {
221                 if (getnum(p, &val) == 1)
222                         simul_bufsize = val / 1024;
223         }
224
225         /*
226          * sleep_max is the time to empty the drive's buffer in µs.
227          * sector size is from 2048 bytes to 2352 bytes.
228          * If sector size is 2048 bytes, 1k takes 6.666 ms.
229          * If sector size is 2352 bytes, 1k takes 5.805 ms.
230          * We take the 6 ms as an average between both values.
231          * simul_bufsize is the number of kilobytes in drive buffer.
232          */
233         sleep_max = 6 * 1000 * simul_bufsize / simul_speed;
234
235         /*
236          * DVD single speed is 1385 * 1000 Bytes/s (676.27 sectors/s)
237          */
238         if ((dp->cdr_flags & CDR_DVD) != 0)
239                 sleep_max = 739 * simul_bufsize / simul_speed;
240
241         if (lverbose) {
242                 printf("Simulation drive buffer size: %d KB\n", simul_bufsize);
243                 printf("Maximum reserve time in drive buffer: %d.%3.3d ms for speed %dx\n",
244                                         sleep_max / 1000,
245                                         sleep_max % 1000,
246                                         simul_speed);
247         }
248         return (0);
249 }
250
251 static int
252 next_wr_addr_simul(SCSI *usalp, track_t *trackp, long *ap)
253 {
254         /*
255          * This will most likely not 100% correct for TAO CDs
256          * but it is better than returning 0 in all cases.
257          */
258         if (ap)
259                 *ap = simul_nwa;
260         return (0);
261 }
262
263
264 static int
265 cdr_write_simul(SCSI *usalp, caddr_t bp /* address of buffer */, 
266                 long sectaddr   /* disk address (sector) to put */, 
267                 long size       /* number of bytes to transfer */, 
268                 int blocks      /* sector count */, 
269                 BOOL islast     /* last write for track */)
270 {
271         Uint    sleep_time;
272         Uint    sleep_diff;
273
274         struct timeval  tv1;
275     static      struct timeval  tv2;
276
277         if (lverbose > 1 && islast)
278                 printf("\nWriting last record for this track.\n");
279
280         simul_nwa += blocks;
281
282         gettimeofday(&tv1, (struct timezone *)0);
283         if (tv2.tv_sec != 0) {          /* Already did gettimeofday(&tv2) */
284                 tv_sub(&tv1, &tv2);
285                 if (sleep_rest != 0) {
286                         sleep_diff = tv1.tv_sec * 1000000 + tv1.tv_usec;
287
288                         if (sleep_min > (sleep_rest - sleep_diff))
289                                 sleep_min = (sleep_rest - sleep_diff);
290
291                         if (sleep_diff > sleep_rest) {
292                                 printf("\aBuffer underrun: actual delay was %d.%3.3d ms, max delay was %d.%3.3d ms.\n",
293                                                 sleep_diff / 1000,
294                                                 sleep_diff % 1000,
295                                                 sleep_rest / 1000,
296                                                 sleep_rest % 1000);
297                                 if (!simul_dummy)
298                                         return (-1);
299                         }
300                         /*
301                          * If we spent time outside the write function
302                          * subtract this time.
303                          */
304                         sleep_diff = tv1.tv_sec * 1000000 + tv1.tv_usec;
305                         if (sleep_rest >= sleep_diff)
306                                 sleep_rest -= sleep_diff;
307                         else
308                                 sleep_rest = 0;
309                 }
310         }
311         /*
312          * Speed 1 ist 150 Sektoren/s
313          * Bei DVD 767.27 Sektoren/s
314          */
315         sleep_time = 1000000 * blocks / 75 / simul_speed;
316         if (simul_isdvd)
317                 sleep_time = 1000000 * blocks / 676 / simul_speed;
318
319         sleep_time += sleep_rest;
320
321         if (sleep_time > sleep_max) {
322                 int     mod;
323                 long    rsleep;
324
325                 sleep_rest = sleep_max;
326                 sleep_time -= sleep_rest;
327                 mod = sleep_time % 20000;
328                 sleep_rest += mod;
329                 sleep_time -= mod;
330                 if (sleep_time > 0) {
331                         gettimeofday(&tv1, (struct timezone *)0);
332                         usleep(sleep_time);
333                         gettimeofday(&tv2, (struct timezone *)0);
334                         tv2.tv_sec -= tv1.tv_sec;
335                         tv2.tv_usec -= tv1.tv_usec;
336                         rsleep = tv2.tv_sec * 1000000 + tv2.tv_usec;
337                         sleep_rest -= rsleep - sleep_time;
338                 }
339         } else {
340                 sleep_rest = sleep_time;
341         }
342
343         gettimeofday(&tv2, (struct timezone *)0);
344         return (size);
345 }
346
347 static int
348 open_track_simul(SCSI *usalp, cdr_t *dp, track_t *trackp)
349 {
350         sleep_min = 999 * 1000000;
351         return (0);
352 }
353
354 static int
355 close_track_simul(SCSI *usalp, cdr_t *dp, track_t *trackp)
356 {
357         if (lverbose) {
358                 printf("Remaining reserve time in drive buffer: %d.%3.3d ms\n",
359                                         sleep_rest / 1000,
360                                         sleep_rest % 1000);
361                 printf("Minimum reserve time in drive buffer: %d.%3.3d ms\n",
362                                         sleep_min / 1000,
363                                         sleep_min % 1000);
364         }
365         usleep(sleep_rest);
366         sleep_rest = 0;
367         return (0);
368 }
369
370 static int
371 open_session_simul(SCSI *usalp, cdr_t *dp, track_t *trackp)
372 {
373         simul_nwa = 0L;
374         return (0);
375 }
376
377 static int
378 fixate_simul(SCSI *usalp, cdr_t *dp, track_t *trackp)
379 {
380         return (0);
381 }
382
383 static void
384 tv_sub(struct timeval *tvp1, struct timeval *tvp2)
385 {
386         tvp1->tv_sec -= tvp2->tv_sec;
387         tvp1->tv_usec -= tvp2->tv_usec;
388
389         while (tvp1->tv_usec < 0) {
390                 tvp1->tv_usec += 1000000;
391                 tvp1->tv_sec -= 1;
392         }
393 }