File: mt.c

package info (click to toggle)
cpio 2.4.2-39woody1
  • links: PTS
  • area: main
  • in suites: woody
  • size: 848 kB
  • ctags: 726
  • sloc: ansic: 8,183; sh: 226; makefile: 212
file content (780 lines) | stat: -rw-r--r-- 18,671 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
/* mt -- control magnetic tape drive operation
   Copyright (C) 1991, 1992, 1995 Free Software Foundation, Inc.

   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, or (at your option)
   any later version.

   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; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
   */

/* Modified for the Linux SCSI tape driver by Brian Mays from code
   written by Kai Makisara.
   Last Modified: Tue Apr 23 15:37:54 EDT 1996
*/

/* If -f is not given, the environment variable TAPE is used;
   if that is not set, a default device defined in sys/mtio.h is used.
   The device must be either a character special file or a remote
   tape drive with the form "[user@]system:path".
   The default count is 1.  Some operations ignore it.

   Exit status:
   0	success
   1	invalid operation or device name
   2	operation failed

   Operations (unique abbreviations are accepted):
   eof, weof	Write COUNT EOF marks at current position on tape.
   fsf		Forward space COUNT files.
		Tape is positioned on the first block of the file.
   bsf		Backward space COUNT files.
		Tape is positioned on the first block of the file.
   fsr		Forward space COUNT records.
   bsr		Backward space COUNT records.
   bsfm		Backward space COUNT file marks.
		Tape is positioned on the beginning-of-the-tape side of
		the file mark.
   asf		Absolute space to file number COUNT.
		Equivalent to rewind followed by fsf COUNT.
   eom		Space to the end of the recorded media on the tape
		(for appending files onto tapes).
   rewind	Rewind the tape.
   offline, rewoffl
		Rewind the tape and, if applicable, unload the tape.
   status	Print status information about the tape unit.
   retension	Rewind the tape, then wind it to the end of the reel,
		then rewind it again.
   erase	Erase the tape.
   fss		(SCSI tapes) Forward space COUNT setmarks.
   bss		(SCSI tapes) Backward space COUNT setmarks.
   wset		(SCSI tapes) Write COUNT setmarks at current position
		(only SCSI tape).
   eod, seod	Space to end of valid data.  Used on streamer tape
		drives to append data to the logical and of tape.
   setblk	(SCSI tapes) Set the block size of the drive to COUNT
		bytes per record.
   setdensity	(SCSI tapes) Set the tape density code to COUNT.  The
		proper codes to use with each drive should be looked
		up from the drive documentation.
   drvbuffer	(SCSI tapes) Set the tape drive buffer code to
		NUMBER.  The proper value for unbuffered operation is
		zero and "normal" buffered operation one. The meanings
		of other values can be found in the drive
		documentation or, in case of a SCSI-2 drive, from the
		SCSI-2 standard.
   stoptions	(SCSI tapes) Set the driver options bits to COUNT for
		the device.  The bits can be set by oring the
		following values: 1 to enable write buffering, 2 to
		enable asynchronous writes, 4 to enable read ahead, 8
		to enable debugging output (if it has been compiled to
		the driver).
   stwrthreshold 
		(SCSI tapes) The write threshold for the tape device
		is set to COUNT kilobytes.  The value must be smaller
		than or equal to the driver buffer size.
   seek		(SCSI tapes) Seek to the COUNT block on the tape.
		This operation is available on some Tandberg and
		Wangtek streamers and some SCSI-2 tape drives.
   tell		(SCSI tapes) Tell the current block on tape.  This
		operation is available on some Tandberg and Wangtek
		streamers and some SCSI-2 tape drives.
   densities	(SCSI tapes) Write explanation of some common density
		codes to standard output.
   datcompression
		(some SCSI-2 DAT tapes) Inquire or set the compression
		status (on/off).  If the COUNT is one the compression
		status is printed.  If the COUNT is zero, compression
		is disabled.  Otherwise, compression is enabled.

   David MacKenzie <djm@gnu.ai.mit.edu> */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#ifdef HAVE_SYS_MTIO_H
#ifdef HAVE_SYS_IO_TRIOCTL_H
#include <sys/io/trioctl.h>
#endif
#include <sys/mtio.h>
#endif
#include <sys/file.h>
#include <fcntl.h>
#include <errno.h>
#include <getopt.h>

#if defined(HAVE_UNISTD_H)
#include <unistd.h>
#endif
#include "rmt.h"

#if defined(HAVE_STRING_H) || defined(STDC_HEADERS)
#include <string.h>
#else
#include <strings.h>
#endif

#if defined(STDC_HEADERS)
#include <stdlib.h>
#else
extern int errno;
char *getenv ();
int atoi ();
void exit ();
#endif

int fstat ();

int argmatch ();
void check_type ();
void error ();
void invalid_arg ();
void perform_operation ();
void print_status ();
void usage ();
#ifdef MTTELL
void print_position ();
#endif

int do_dat_compression(char *dev, int fn, int count);

#if defined(linux) || defined(__linux)
#define MTDATCOMP 1000		/* Random unused number.  */
#define MTDENS 1001		/* Random unused number.  */

struct densities {
  int code;
  char *name;
} density_tbl[] = {
  {0x00, "default"},
  {0x01, "NRZI (800 bpi)"},
  {0x02, "PE (1600 bpi)"},
  {0x03, "GCR (6250 bpi)"},
  {0x05, "QIC-45/60 (GCR, 8000 bpi)"},
  {0x06, "PE (3200 bpi)"},
  {0x07, "IMFM (6400 bpi)"},
  {0x08, "GCR (8000 bpi)"},
  {0x09, "GCR (37871 bpi)"},
  {0x0a, "MFM (6667 bpi)"},
  {0x0b, "PE (1600 bpi)"},
  {0x0c, "GCR (12960 bpi)"},
  {0x0d, "GCR (25380 bpi)"},
  {0x0f, "QIC-120 (GCR 10000 bpi)"},
  {0x10, "QIC-150/250 (GCR 10000 bpi)"},
  {0x11, "QIC-320/525 (GCR 16000 bpi)"},
  {0x12, "QIC-1350 (RLL 51667 bpi)"},
  {0x13, "DDS (61000 bpi)"},
  {0x14, "EXB-8200 (RLL 43245 bpi)"},
  {0x15, "EXB-8500 (RLL 45434 bpi)"},
  {0x16, "MFM 10000 bpi"},
  {0x17, "MFM 42500 bpi"},
  {0x18, "MFM 42500 bpi, 56 track pairs"},
  {0x19, "DLTtape III/DLTtape IIIxt (62500 bpi)"},
  {0x1A, "DLTtape IV (81633 bpi)"},
  {0x22, "SLR4DC"},
  {0x24, "DDS-2"},
  {0x45, "QIC-3095"},
  {0x80, "DLTtape III/DLTtape IIIxt (62500 bpi)"},
  {0x81, "DLTtape III/DLTtape IIIxt compressed (62500 bpi)"},
  {0x82, "DLTtape IV (81633 bpi)"},
  {0x83, "DLTtape IV compressed (81633 bpi)"},
  {140, "EXB-8505 compressed"},
  {144, "EXB-8205 compressed"},
  {-1, NULL}};
#endif

char *opnames[] =
{
  "eof", "weof", "fsf", "bsf", "fsr", "bsr",
  "rewind", "offline", "rewoffl", "eject", "status",
#ifdef MTBSFM
  "bsfm",
#endif
#ifdef MTEOM
  "eom",
  "eod",
  "seod",
#endif
#ifdef MTRETEN
  "retension",
#endif
#ifdef MTERASE
  "erase",
#endif
  "asf",
#ifdef MTFSFM
  "fsfm",
#endif
#ifdef MTSEEK
  "seek",
#endif
#ifdef MTTELL
  "tell",
#endif
#ifdef MTFSS
  "fss",
#endif
#ifdef MTBSS
  "bss",
#endif
#ifdef MTWSM
  "wset",
#endif
#ifdef MTSETBLK
  "setblk",
#endif
#ifdef MTSETDENSITY
  "setdensity",
#endif
#ifdef MTSETDRVBUFFER
  "drvbuffer",
#ifdef MT_ST_BOOLEANS
  "stoptions",
#endif
#ifdef MT_ST_WRITE_THRESHOLD
  "stwrthreshold",
#endif
#endif
#ifdef MTDATCOMP
  "datcompression",
#endif
#ifdef MTDENS
  "densities",
#endif
  NULL
};

#define MTASF 600		/* Random unused number.  */
short operations[] =
{
  MTWEOF, MTWEOF, MTFSF, MTBSF, MTFSR, MTBSR,
  MTREW, MTOFFL, MTOFFL, MTOFFL, MTNOP,
#ifdef MTBSFM
  MTBSFM,
#endif
#ifdef MTEOM
  MTEOM,
  MTEOM,
  MTEOM,
#endif
#ifdef MTRETEN
  MTRETEN,
#endif
#ifdef MTERASE
  MTERASE,
#endif
  MTASF,
#ifdef MTFSFM
  MTFSFM,
#endif
#ifdef MTSEEK
  MTSEEK,
#endif
#ifdef MTTELL
  MTTELL,
#endif
#ifdef MTFSS
  MTFSS,
#endif
#ifdef MTBSS
  MTBSS,
#endif
#ifdef MTWSM
  MTWSM,
#endif
#ifdef MTSETBLK
  MTSETBLK,
#endif
#ifdef MTSETDENSITY
  MTSETDENSITY,
#endif
#ifdef MTSETDRVBUFFER
  MTSETDRVBUFFER,
#ifdef MT_ST_BOOLEANS
  MTSETDRVBUFFER,
#endif
#ifdef MT_ST_WRITE_THRESHOLD
  MTSETDRVBUFFER,
#endif
#endif
#ifdef MTDATCOMP
  MTDATCOMP,
#endif
#ifdef MTDENS
  MTDENS,
#endif
  0
};

char *cbnames[] =
{
#ifdef MT_ST_BOOLEANS
  "stoptions",
#endif
#ifdef MT_ST_WRITE_THRESHOLD
  "stwrthreshold",
#endif
  NULL
};

int count_bits[] =
{
#ifdef MT_ST_BOOLEANS
  MT_ST_BOOLEANS,
#endif
#ifdef MT_ST_WRITE_THRESHOLD
  MT_ST_WRITE_THRESHOLD,
#endif
  0
};

#ifdef MT_TAPE_INFO
  struct mt_tape_info tapes[] = MT_TAPE_INFO;
#endif

/* If nonzero, don't consider file names that contain a `:' to be
   on remote hosts; all files are local.  Always zero for mt;
   since when do local device names contain colons?  */
int f_force_local = 0;

struct option longopts[] =
{
  {"file", 1, NULL, 'f'},
  {"rsh-command", 1, NULL, 1},
  {"version", 0, NULL, 'V'},
  {"help", 0, NULL, 'H'},
  {NULL, 0, NULL, 0}
};

/* The name this program was run with.  */
char *program_name;

void
main (argc, argv)
     int argc;
     char **argv;
{
  extern char *version_string;
  short operation;
  int count;
  char *tapedev;
  int tapedesc;
  int i;
  char *rsh_command_option = getenv("MT_RSH");

  program_name = argv[0];
  tapedev = NULL;
  count = 1;

  /* Debian hack: Fixed a bug in the -V flag.  This bug has been
     reported to "bug-gnu-utils@prep.ai.mit.edu".  -BEM */
  while ((i = getopt_long (argc, argv, "f:t:VH", longopts, (int *) 0)) != -1)
    {
      switch (i)
	{
	case 'f':
	case 't':
	  tapedev = optarg;
	  break;

	case 1:
	  rsh_command_option = optarg;
	  break;

	case 'V':
	  printf ("GNU mt %s", version_string);
	  exit (0);
	  break;

	case 'H':
	default:
	  usage (stdout, 0);
	}
    }

  if (optind == argc)
    usage (stderr, 1);

  i = argmatch (argv[optind], opnames);
  if (i < 0)
    {
      invalid_arg ("tape operation", argv[optind], i);
      exit (1);
    }
  operation = operations[i];

  i = argmatch (argv[optind], cbnames);

  if (++optind < argc)
    /* Debian hack: Replaced the atoi function call with strtol so
       that hexidecimal values can be used for the count parameter.
       This bug has been reported to "bug-gnu-utils@prep.ai.mit.edu".
       (97/12/5) -BEM */
#if defined(STDC_HEADERS)
    count = (int) strtol (argv[optind], NULL, 0);
#else
    count = atoi (argv[optind]);
#endif
  if (++optind < argc)
    usage (stderr, 1);
  if (i >= 0)
    count |= count_bits[i];

  if (tapedev == NULL)
    {
      tapedev = getenv ("TAPE");
      if (tapedev == NULL)
#ifdef DEFTAPE			/* From sys/mtio.h.  */
        tapedev = DEFTAPE;
#else
	error (1, 0, "no tape device specified");
#endif
    }

#ifdef MTDENS
  if (operation == MTDENS)
    {
      printf("Some SCSI tape density codes:\ncode   explanation\n");
      for (i=0; density_tbl[i].code >= 0; i++)
	printf("0x%02x   %s\n", density_tbl[i].code, density_tbl[i].name);
      exit (0);
    }
#endif

  if ( (operation == MTWEOF)
#ifdef MTERASE
       || (operation == MTERASE)
#endif
#ifdef MTWSM
       || (operation == MTWSM)
#endif
#ifdef MTSETDRVBUFFER
       || (operation == MTSETDRVBUFFER)
#endif
#ifdef MTDATCOMP
       || (operation == MTDATCOMP)
#endif
	)
    tapedesc = rmtopen (tapedev, O_WRONLY, 0, rsh_command_option);
  else
    tapedesc = rmtopen (tapedev, O_RDONLY, 0, rsh_command_option);
  if (tapedesc == -1)
    error (1, errno, "%s", tapedev);
  check_type (tapedev, tapedesc);

#ifdef MTDATCOMP
  if (operation == MTDATCOMP)
    do_dat_compression(tapedev, tapedesc, count);
  else
#endif
#ifdef MTTELL
  if (operation == MTTELL)
    print_position (tapedev, tapedesc);
  else
#endif
  {
    if (operation == MTASF)
      {
	perform_operation (tapedev, tapedesc, MTREW, 1);
	operation = MTFSF;
      }
    perform_operation (tapedev, tapedesc, operation, count);
    if (operation == MTNOP)
      print_status (tapedev, tapedesc);
  }
  
  if (rmtclose (tapedesc) == -1)
    error (2, errno, "%s", tapedev);

  exit (0);
}

void
check_type (dev, desc)
     char *dev;
     int desc;
{
  struct stat stats;

  if (_isrmt (desc))
    return;
  if (fstat (desc, &stats) == -1)
    error (1, errno, "%s", dev);
  if ((stats.st_mode & S_IFMT) != S_IFCHR)
    error (1, 0, "%s is not a character special file", dev);
}

void
perform_operation (dev, desc, op, count)
     char *dev;
     int desc;
     short op;
     int count;
{
  struct mtop control;

  control.mt_op = op;
  control.mt_count = count;
  /* Debian hack: The rmtioctl function returns -1 in case of an
     error, not 0.  This bug has been reported to
     "bug-gnu-utils@prep.ai.mit.edu".  (96/7/10) -BEM */
  if (rmtioctl (desc, MTIOCTOP, &control) == -1)
    error (2, errno, "%s", dev);
}

#ifdef MTTELL
void
print_position (dev, desc)
     char *dev;
     int desc;
{
  struct mtpos position;

  if (rmtioctl (desc, MTIOCPOS, &position) == -1)
    error (2, errno, "%s", dev);
  printf("At block %d.\n", (int) position.mt_blkno);

}
#endif

void
print_status (dev, desc)
     char *dev;
     int desc;
{
  struct mtget status;

  if (rmtioctl (desc, MTIOCGET, &status))
    error (2, errno, "%s", dev);

#ifdef MT_ISFTAPE_FLAG
  if ((int) status.mt_type & MT_ISFTAPE_FLAG)
    {
      printf ("qic-117 drive type = 0x%05x, ", (int) status.mt_type & 0x1ffff);
      if (GMT_DR_OPEN( (long) status.mt_gstat))
	{
	  printf ("no tape or door open\n");
	}
      else
	{
	  printf ("%s-line%s\n",
		  GMT_ONLINE( (long) status.mt_gstat) ? "on" : "off",
		  GMT_WR_PROT( (long) status.mt_gstat) ?
		  " and write-protected" : "");
	}
    }
  else
#endif
    {
#ifdef MT_TAPE_INFO
      struct mt_tape_info *mt;
      for (mt = tapes; mt->t_type; mt++)
	if (mt->t_type == status.mt_type) break;
      if (mt->t_type != 0)
	printf ("drive type = %s\n", mt->t_name);
      else
#endif
	printf ("drive type = %d\n", (int) status.mt_type);
    }
#if defined(hpux) || defined(__hpux)
  printf ("drive status (high) = %d\n", (int) status.mt_dsreg1);
  printf ("drive status (low) = %d\n", (int) status.mt_dsreg2);
#else
  printf ("drive status = %d\n", (int) status.mt_dsreg);
#endif
  printf ("sense key error = %d\n", (int) status.mt_erreg);
  printf ("residue count = %d\n", (int) status.mt_resid);
#if !defined(ultrix) && !defined(__ultrix__) && !defined(hpux) && !defined(__hpux) && !defined(__osf__)
  printf ("file number = %d\n", (int) status.mt_fileno);
  printf ("block number = %d\n", (int) status.mt_blkno);
#endif
#if defined(linux) || defined(__linux)
  if (status.mt_type == MT_ISSCSI1 ||
      status.mt_type == MT_ISSCSI2)
    {
      int dens, i;
      char *density;
      dens = (status.mt_dsreg & MT_ST_DENSITY_MASK) >> MT_ST_DENSITY_SHIFT;
      density = "unknown";
      for (i=0; density_tbl[i].code >= 0; i++)
	if (density_tbl[i].code == dens)
	  {
	    density = density_tbl[i].name;
	    break;
	  }
      printf("Tape block size %d bytes. Density code 0x%x (%s).\n",
	     (int)((status.mt_dsreg & MT_ST_BLKSIZE_MASK) >>
		   MT_ST_BLKSIZE_SHIFT),
	     dens, density);

      printf("Soft error count since last status=%d\n",
	     (int)(status.mt_erreg & MT_ST_SOFTERR_MASK) >>
	     MT_ST_SOFTERR_SHIFT);
      printf("General status bits on (%x):\n", (unsigned)status.mt_gstat);
      if (GMT_EOF(status.mt_gstat))
	printf(" EOF");
      if (GMT_BOT(status.mt_gstat))
	printf(" BOT");
      if (GMT_EOT(status.mt_gstat))
	printf(" EOT");
      if (GMT_SM(status.mt_gstat))
	printf(" SM");
      if (GMT_EOD(status.mt_gstat))
	printf(" EOD");
      if (GMT_WR_PROT(status.mt_gstat))
	printf(" WR_PROT");
      if (GMT_ONLINE(status.mt_gstat))
	printf(" ONLINE");
      if (GMT_D_6250(status.mt_gstat))
	printf(" D_6250");
      if (GMT_D_1600(status.mt_gstat))
	printf(" D_1600");
      if (GMT_D_800(status.mt_gstat))
	printf(" D_800");
      if (GMT_DR_OPEN(status.mt_gstat))
	printf(" DR_OPEN");	  
      if (GMT_IM_REP_EN(status.mt_gstat))
	printf(" IM_REP_EN");
      printf("\n");
    }
  else
    {
      printf("gstat = %0x\n", (unsigned)status.mt_gstat);
    }
#endif
}

void
usage (fp, status)
  FILE *fp;
  int status;
{
  fprintf (fp, "\
Usage: %s [-V] [-f device] [--file=device] [--rsh-command=command]\n\
\t[--help] [--version] operation [count]\n",
	   program_name);
  exit (status);
}

#if defined(linux) || defined(__linux)
/*** Get and set the DAT compression (Mode Page 15) ***/

#define MODE_SENSE 0x1a
#define MODE_SELECT 0x15

int
read_mode_page(int fn, int page, int length, unsigned char *buffer,
	       int do_mask)
{
  int result, *ip;
  unsigned char tmpbuffer[30], *cmd;

  memset(tmpbuffer, 0, 14);
  ip = (int *)&(tmpbuffer[0]);
  *ip = 0;
  *(ip+1) = length + 4;

  cmd = &(tmpbuffer[8]);
  cmd[0] = MODE_SENSE;
  cmd[1] = 8;
  cmd[2] = page;
  if (do_mask)
    cmd[2] |= 0x40;  /* Get changeable parameter mask */
  cmd[4] = length + 4;

  result = ioctl(fn, 1, tmpbuffer);
  if (result) {
    fprintf(stderr, "Can't read mode page. Are you sure you are root?\n");
    return 0;
  }
  memcpy(buffer, tmpbuffer + 8, length + 4);
  return 1;
}


int
write_mode_page(int fn, int page, int length, unsigned char *buffer)
{
  int result, *ip;
  unsigned char tmpbuffer[40], *cmd;

  memset(tmpbuffer, 0, 14);
  ip = (int *)&(tmpbuffer[0]);
  *ip = length + 4;
  *(ip+1) = 0;

  cmd = &(tmpbuffer[8]);
  cmd[0] = MODE_SELECT;
  cmd[1] = 0x10;
  cmd[4] = length + 4;

  memcpy(tmpbuffer + 14, buffer, length + 4);
  tmpbuffer[14] = 0;  /* reserved data length */
  tmpbuffer[18] &= 0x3f;  /* reserved bits in page code byte */

  result = ioctl(fn, 1, tmpbuffer);
  if (result) {
    fprintf(stderr, "Can't write mode page.\n");
    return 0;
  }
  return 1;
}


int
do_dat_compression(char *dev, int fn, int count)
{
  int i;
  unsigned char buffer[30], mask[30];

  if (!read_mode_page(fn, 0x0f, 16, buffer, 0)) {
    error (2, errno, "%s", dev);
  }

  if (count != 1) {
    if (read_mode_page(fn, 0x0f, 16, mask, 1))
      if (mask[4+2] & 0x80 == 0)
        fprintf(stderr, "Device does not allow setting this parameter.\n");
    if (count == 0)
      buffer[4+2] &= 0x7f;
    else {
      buffer[4+2] |= 0x80;
      if (buffer[4+2+1] & 0x80 == 0) {
        fprintf(stderr, "Warning: Decompression is disabled.  Enabling.\n");
        buffer[4+2+1] |= 0x80;
      }
    }
      buffer[1] = 0;
    if (!write_mode_page(fn, 0x0f, 16, buffer)) {
      error (2, errno, "%s", dev);
    }
    if (!read_mode_page(fn, 0x0f, 16, buffer, 0)) {  /* Re-read to check */
      error (2, errno, "%s", dev);
    }
  }

  if (buffer[4+2] & 0x80)
    printf("Compression on.\n");
  else
    printf("Compression off.\n");
  if (buffer[4+2] & 0x40)
    printf("Compression capable.\n");
  else
    printf("Compression not capable.\n");
  if (buffer[4+2+1] & 0x80)
    printf("Decompression capable.\n");
  else
    printf("Decompression not capable.\n");

  return 1;
}
#endif