Imported Upstream version 3.1.0
[debian/amanda] / ndmp-src / ndma_robot_simulator.c
1 /*
2  * Copyright (c) 1998,1999,2000
3  *      Traakan, Inc., Los Altos, CA
4  *      All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice unmodified, this list of conditions, and the following
11  *    disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 /*
30  * Project:  NDMJOB
31  * Ident:    $Id: $
32  *
33  * Description:
34  *
35  */
36
37
38 #include "ndmagents.h"
39
40 #ifndef NDMOS_OPTION_NO_ROBOT_AGENT
41 #ifdef NDMOS_OPTION_ROBOT_SIMULATOR
42
43 #include "scsiconst.h"
44
45 #define ROBOT_CONTROLLER 0
46 #define ROBOT_ID 7
47 #define ROBOT_LUN 1
48
49 /*
50  * interface
51  */
52
53 int
54 ndmos_scsi_initialize (struct ndm_session *sess)
55 {
56         struct ndm_robot_agent *        ra = &sess->robot_acb;
57
58         NDMOS_MACRO_ZEROFILL(&ra->sim_dir);
59         NDMOS_MACRO_ZEROFILL(&ra->scsi_state);
60         ra->scsi_state.error = NDMP9_DEV_NOT_OPEN_ERR;
61         ra->scsi_state.target_controller = ROBOT_CONTROLLER;
62         ra->scsi_state.target_id = ROBOT_ID;
63         ra->scsi_state.target_lun = ROBOT_LUN;
64
65         return 0;
66 }
67
68 void
69 ndmos_scsi_sync_state (struct ndm_session *sess)
70 {
71 }
72
73 ndmp9_error
74 ndmos_scsi_open (struct ndm_session *sess, char *name)
75 {
76         struct stat                     st;
77         struct ndm_robot_agent *        ra = &sess->robot_acb;
78
79         if (!name || strlen(name) > sizeof(ra->sim_dir)-1)
80                 return NDMP9_NO_DEVICE_ERR;
81
82         /* check that it's a directory */
83         if (stat (name, &st) < 0)
84                 return NDMP9_NO_DEVICE_ERR;
85         if (!S_ISDIR(st.st_mode))
86                 return NDMP9_NO_DEVICE_ERR;
87
88         strncpy(ra->sim_dir, name, sizeof(ra->sim_dir)-1);
89         ra->scsi_state.error = NDMP9_NO_ERR;
90
91         return NDMP9_NO_ERR;
92 }
93
94 ndmp9_error
95 ndmos_scsi_close (struct ndm_session *sess)
96 {
97         ndmos_scsi_initialize(sess);
98         return NDMP9_NO_ERR;
99 }
100
101 /* deprecated */
102 ndmp9_error
103 ndmos_scsi_set_target (struct ndm_session *sess)
104 {
105         return NDMP9_NOT_SUPPORTED_ERR;
106 }
107
108
109 ndmp9_error
110 ndmos_scsi_reset_device (struct ndm_session *sess)
111 {
112         struct ndm_robot_agent *        ra = &sess->robot_acb;
113
114         /* this is easy.. */
115         return ra->scsi_state.error;
116 }
117
118 /* deprecated */
119 ndmp9_error
120 ndmos_scsi_reset_bus (struct ndm_session *sess)
121 {
122         return NDMP9_NOT_SUPPORTED_ERR;
123 }
124
125 /*
126  * Robot state management
127  ****************************************************************
128  */
129
130 /* xxx_FIRST must be in order! */
131 #define IE_FIRST 0
132 #define IE_COUNT 2
133 #define MTE_FIRST 16
134 #define MTE_COUNT 1
135 #define DTE_FIRST 128
136 #define DTE_COUNT 2
137 #define STORAGE_FIRST 1024
138 #define STORAGE_COUNT 10
139
140 #if (IE_FIRST+IE_COUNT > MTE_FIRST) \
141  || (MTE_FIRST+MTE_COUNT > DTE_FIRST) \
142  || (DTE_FIRST+MTE_COUNT > STORAGE_FIRST)
143 #error element addresses overlap or are in the wrong order
144 #endif
145
146 #define IS_IE_ADDR(a) ((a) >= IE_FIRST && (a) < IE_FIRST+IE_COUNT)
147 #define IS_MTE_ADDR(a) ((a) >= MTE_FIRST && (a) < MTE_FIRST+MTE_COUNT)
148 #define IS_DTE_ADDR(a) ((a) >= DTE_FIRST && (a) < DTE_FIRST+DTE_COUNT)
149 #define IS_STORAGE_ADDR(a) ((a) >= STORAGE_FIRST && (a) < STORAGE_FIRST+STORAGE_COUNT)
150
151 struct element_state {
152         int full;
153         int medium_type;
154         int source_element;
155         char pvoltag[32];
156         char avoltag[32];
157 };
158
159 struct robot_state {
160         struct element_state mte[MTE_COUNT];
161         struct element_state storage[STORAGE_COUNT];
162         struct element_state ie[IE_COUNT];
163         struct element_state dte[DTE_COUNT];
164 };
165
166 static void
167 robot_state_init(struct robot_state *rs)
168 {
169         int i;
170
171         /* invent some nice data, with some nice voltags and whatnot */
172
173         NDMOS_API_BZERO(rs, sizeof(*rs));
174
175         /* (nothing to do for MTEs) */
176
177         for (i = 0; i < STORAGE_COUNT; i++) {
178                 struct element_state *es = &rs->storage[i];
179                 es->full = 1;
180
181                 es->medium_type = 1; /* data */
182                 es->source_element = 0;
183                 snprintf(es->pvoltag, sizeof(es->pvoltag), "PTAG%02XXX                        ", i);
184                 snprintf(es->avoltag, sizeof(es->avoltag), "ATAG%02XXX                        ", i);
185         }
186
187         /* (i/e are all empty) */
188
189         /* (dte's are all empty) */
190 }
191
192 static void
193 robot_state_load(struct ndm_session *sess, struct robot_state *rs)
194 {
195         int fd;
196         char filename[PATH_MAX];
197
198         /* N.B. writing a struct to disk like this isn't portable, but this
199          * is test code, so it's OK for now. */
200
201         snprintf(filename, sizeof filename, "%s/state", sess->robot_acb.sim_dir);
202         fd = open(filename, O_RDONLY, 0666);
203         if (fd < 0) {
204                 robot_state_init(rs);
205                 return;
206         }
207         if (read(fd, (void *)rs, sizeof(*rs)) < sizeof(*rs)) {
208                 robot_state_init(rs);
209                 return;
210         }
211
212         close(fd);
213 }
214
215 static int
216 robot_state_save(struct ndm_session *sess, struct robot_state *rs)
217 {
218         int fd;
219         char filename[PATH_MAX];
220
221         /* N.B. writing a struct to disk like this isn't portable, but this
222          * is test code, so it's OK for now. */
223
224         snprintf(filename, sizeof filename, "%s/state", sess->robot_acb.sim_dir);
225         fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0666);
226         if (fd < 0)
227                 return -1;
228         if (write(fd, (void *)rs, sizeof(*rs)) < sizeof(*rs))
229                 return -1;
230         close(fd);
231
232         return 0;
233 }
234
235 static int
236 robot_state_move(struct ndm_session *sess, struct robot_state *rs, int src, int dest)
237 {
238         char src_filename[PATH_MAX];
239         struct element_state *src_elt;
240         char dest_filename[PATH_MAX];
241         struct element_state *dest_elt;
242         struct stat st;
243         char pos[PATH_MAX];
244
245         /* TODO: audit that the tape device is not using this volume right now */
246
247         ndmalogf(sess, 0, 3, "moving medium from %d to %d", src, dest);
248
249         if (IS_IE_ADDR(src)) {
250                 src_elt = &rs->ie[src - IE_FIRST];
251                 snprintf(src_filename, sizeof(src_filename), "%s/ie%d",
252                     sess->robot_acb.sim_dir, src - IE_FIRST);
253         } else if (IS_DTE_ADDR(src)) {
254                 src_elt = &rs->dte[src - DTE_FIRST];
255                 snprintf(src_filename, sizeof(src_filename), "%s/drive%d",
256                     sess->robot_acb.sim_dir, src - DTE_FIRST);
257         } else if (IS_STORAGE_ADDR(src)) {
258                 src_elt = &rs->storage[src - STORAGE_FIRST];
259                 snprintf(src_filename, sizeof(src_filename), "%s/slot%d",
260                     sess->robot_acb.sim_dir, src - STORAGE_FIRST);
261         } else {
262                 ndmalogf(sess, 0, 3, "invalid src address %d", src);
263                 return -1;
264         }
265
266         if (IS_IE_ADDR(dest)) {
267                 dest_elt = &rs->ie[dest - IE_FIRST];
268                 snprintf(dest_filename, sizeof(dest_filename), "%s/ie%d",
269                     sess->robot_acb.sim_dir, dest - IE_FIRST);
270         } else if (IS_DTE_ADDR(dest)) {
271                 dest_elt = &rs->dte[dest - DTE_FIRST];
272                 snprintf(dest_filename, sizeof(dest_filename), "%s/drive%d",
273                     sess->robot_acb.sim_dir, dest - DTE_FIRST);
274         } else if (IS_STORAGE_ADDR(dest)) {
275                 dest_elt = &rs->storage[dest - STORAGE_FIRST];
276                 snprintf(dest_filename, sizeof(dest_filename), "%s/slot%d",
277                     sess->robot_acb.sim_dir, dest - STORAGE_FIRST);
278         } else {
279                 ndmalogf(sess, 0, 3, "invalid dst address %d", src);
280                 return -1;
281         }
282
283         if (!src_elt->full) {
284                 ndmalogf(sess, 0, 3, "src not full");
285                 return -1;
286         }
287
288         if (dest_elt->full) {
289                 ndmalogf(sess, 0, 3, "dest full");
290                 return -1;
291         }
292
293         /* OK, enough checking, let's do it */
294         /* delete the destination, if it exists */
295         if (stat (dest_filename, &st) >= 0) {
296                 ndmalogf(sess, 0, 3, "unlink %s", dest_filename);
297                 if (unlink(dest_filename) < 0) {
298                         ndmalogf(sess, 0, 0, "error unlinking: %s", strerror(errno));
299                         return -1;
300                 }
301         }
302
303         /* and move the source if it exists */
304         if (stat (src_filename, &st) >= 0) {
305                 ndmalogf(sess, 0, 3, "move %s to %s", src_filename, dest_filename);
306                 if (rename(src_filename, dest_filename) < 0) {
307                         ndmalogf(sess, 0, 0, "error renaming: %s", strerror(errno));
308                         return -1;
309                 }
310         } else {
311                 /* otherwise touch the destination file */
312                 ndmalogf(sess, 0, 3, "touch %s", dest_filename);
313                 int fd = open(dest_filename, O_CREAT | O_WRONLY, 0666);
314                 if (fd < 0) {
315                         ndmalogf(sess, 0, 0, "error touching: %s", strerror(errno));
316                         return -1;
317                 }
318                 close(fd);
319         }
320
321         /* blow away any tape-drive .pos files */
322         snprintf(pos, sizeof(pos), "%s.pos", src_filename);
323         unlink(pos); /* ignore errors */
324         snprintf(pos, sizeof(pos), "%s.pos", dest_filename);
325         unlink(pos); /* ignore errors */
326
327         /* update state */
328         *dest_elt = *src_elt;
329         ndmalogf(sess, 0, 3, "setting dest's source_element to %d", src);
330         dest_elt->source_element = src;
331         src_elt->full = 0;
332
333
334         ndmalogf(sess, 0, 3, "move successful");
335         return 0;
336 }
337
338 /*
339  * SCSI commands
340  ****************************************************************
341  */
342
343 /*
344  * Utilities
345  */
346
347 static ndmp9_error
348 scsi_fail_with_sense_code(struct ndm_session *sess,
349             ndmp9_execute_cdb_reply *reply,
350             int status, int sense_key, int asq)
351 {
352         unsigned char ext_sense[] = {
353                 0x72, /* current errors */
354                 sense_key & SCSI_SENSE_SENSE_KEY_MASK,
355                 (asq >> 8) & 0xff,
356                 (asq     ) & 0xff,
357                 0,
358                 0,
359                 0,
360                 0 };
361
362         ndmalogf(sess, 0, 3, "sending failure; status=0x%02x sense_key=0x%02x asq=0x%04x",
363                     status, sense_key, asq);
364
365         reply->status = status;
366         reply->ext_sense.ext_sense_len = sizeof(ext_sense);
367         reply->ext_sense.ext_sense_val = NDMOS_API_MALLOC(sizeof(ext_sense));
368         NDMOS_API_BCOPY(ext_sense, reply->ext_sense.ext_sense_val, sizeof(ext_sense));
369
370         return NDMP9_NO_ERR;
371 }
372
373 /*
374  * Command implementations
375  */
376
377 static ndmp9_error
378 execute_cdb_test_unit_ready (struct ndm_session *sess,
379   ndmp9_execute_cdb_request *request,
380   ndmp9_execute_cdb_reply *reply)
381 {
382         unsigned char *cdb = (unsigned char *)request->cdb.cdb_val;
383         char *response;
384         int response_len;
385         char *p;
386
387         if (request->cdb.cdb_len != 6)
388                 return scsi_fail_with_sense_code(sess, reply,
389                     SCSI_STATUS_CHECK_CONDITION,
390                     SCSI_SENSE_KEY_ILLEGAL_REQUEST,
391                     ASQ_INVALID_FIELD_IN_CDB);
392
393         /* yep, we're ready! */
394
395         return NDMP9_NO_ERR;
396 }
397
398 static ndmp9_error
399 execute_cdb_inquiry (struct ndm_session *sess,
400   ndmp9_execute_cdb_request *request,
401   ndmp9_execute_cdb_reply *reply)
402 {
403         unsigned char *cdb = (unsigned char *)request->cdb.cdb_val;
404         char *response;
405         int response_len;
406         char *p;
407
408         /* N.B.: only page code 0 is supported */
409         if (request->cdb.cdb_len != 6
410             || request->data_dir != NDMP9_SCSI_DATA_DIR_IN
411             || cdb[1] & 0x01
412             || cdb[2] != 0
413             || request->datain_len < 96
414             || ((cdb[3] << 8) + cdb[4]) < 96)
415                 return scsi_fail_with_sense_code(sess, reply,
416                     SCSI_STATUS_CHECK_CONDITION,
417                     SCSI_SENSE_KEY_ILLEGAL_REQUEST,
418                     ASQ_INVALID_FIELD_IN_CDB);
419
420         response_len = 96;
421         p = response = NDMOS_API_MALLOC(response_len);
422         NDMOS_API_BZERO(response, response_len);
423         *(p++) = 0x08;  /* media changer */
424         *(p++) = 0;     /* RMB=0 */
425         *(p++) = 6;     /* VERSION=SPC-4 */
426         *(p++) = 2;     /* !NORMACA, !HISUP, RESPONSE DATA FORMAT = 2 */
427         *(p++) = 92;    /* remaining bytes */
428         *(p++) = 0;     /* lots of flags, all 0 */
429         *(p++) = 0;     /* lots of flags, all 0 */
430         *(p++) = 0;     /* lots of flags, all 0 */
431         NDMOS_API_BCOPY("NDMJOB  ", p, 8); p += 8;
432         NDMOS_API_BCOPY("FakeRobot        ", p, 16); p += 16;
433         NDMOS_API_BCOPY("1.0 ", p, 4); p += 4;
434         /* remainder is zero */
435
436         reply->datain.datain_len = response_len;
437         reply->datain.datain_val = response;
438
439         return NDMP9_NO_ERR;
440 }
441
442 static ndmp9_error
443 execute_cdb_mode_sense_6 (struct ndm_session *sess,
444   ndmp9_execute_cdb_request *request,
445   ndmp9_execute_cdb_reply *reply)
446 {
447         unsigned char *cdb = (unsigned char *)request->cdb.cdb_val;
448         int page, subpage;
449         char *response;
450         int response_len;
451         char *p;
452
453         if (request->cdb.cdb_len != 6
454             || request->data_dir != NDMP9_SCSI_DATA_DIR_IN)
455                 return scsi_fail_with_sense_code(sess, reply,
456                     SCSI_STATUS_CHECK_CONDITION,
457                     SCSI_SENSE_KEY_ILLEGAL_REQUEST,
458                     ASQ_INVALID_FIELD_IN_CDB);
459         page = cdb[2] & 0x3f;
460         subpage = cdb[3];
461
462         switch ((page << 8) + subpage) {
463         case 0x1D00: /* Element Address Assignment */
464                 if (request->datain_len < 20 || cdb[4] < 20)
465                         return scsi_fail_with_sense_code(sess, reply,
466                             SCSI_STATUS_CHECK_CONDITION,
467                             SCSI_SENSE_KEY_ILLEGAL_REQUEST,
468                             ASQ_INVALID_FIELD_IN_CDB);
469
470                 response_len = 24;
471                 p = response = NDMOS_API_MALLOC(response_len);
472                 NDMOS_API_BZERO(response, response_len);
473                 *(p++) = response_len;
474                 *(p++) = 0; /* reserved medium type */
475                 *(p++) = 0; /* reserved device-specific parameter */
476                 *(p++) = 0; /* block descriptor length (DBD = 0 above)*/
477                 *(p++) = 0x1D; /* page code */
478                 *(p++) = 18; /* remaining bytes */
479                 *(p++) = (MTE_FIRST >> 8) & 0xff;
480                 *(p++) = MTE_FIRST & 0xff;
481                 *(p++) = (MTE_COUNT >> 8) & 0xff;
482                 *(p++) = MTE_COUNT & 0xff;
483                 *(p++) = (STORAGE_FIRST >> 8) & 0xff;
484                 *(p++) = STORAGE_FIRST & 0xff;
485                 *(p++) = (STORAGE_COUNT >> 8) & 0xff;
486                 *(p++) = STORAGE_COUNT & 0xff;
487                 *(p++) = (IE_FIRST >> 8) & 0xff;
488                 *(p++) = IE_FIRST & 0xff;
489                 *(p++) = (IE_COUNT >> 8) & 0xff;
490                 *(p++) = IE_COUNT & 0xff;
491                 *(p++) = (DTE_FIRST >> 8) & 0xff;
492                 *(p++) = DTE_FIRST & 0xff;
493                 *(p++) = (DTE_COUNT >> 8) & 0xff;
494                 *(p++) = DTE_COUNT & 0xff;
495                 /* remainder is zero */
496                 break;
497
498         default:
499                 return scsi_fail_with_sense_code(sess, reply,
500                     SCSI_STATUS_CHECK_CONDITION,
501                     SCSI_SENSE_KEY_ILLEGAL_REQUEST,
502                     ASQ_INVALID_FIELD_IN_CDB);
503         }
504
505         reply->datain.datain_len = response_len;
506         reply->datain.datain_val = response;
507
508         return NDMP9_NO_ERR;
509 }
510
511 static ndmp9_error
512 execute_cdb_read_element_status (struct ndm_session *sess,
513   ndmp9_execute_cdb_request *request,
514   ndmp9_execute_cdb_reply *reply)
515 {
516         unsigned char *cdb = (unsigned char *)request->cdb.cdb_val;
517         struct robot_state rs;
518         int min_addr, max_elts;
519         char *response;
520         int response_len;
521         int required_len;
522         int num_elts = IE_COUNT + MTE_COUNT + DTE_COUNT + STORAGE_COUNT;
523         char *p;
524
525         if (request->cdb.cdb_len != 12
526             || request->data_dir != NDMP9_SCSI_DATA_DIR_IN)
527                 return scsi_fail_with_sense_code(sess, reply,
528                     SCSI_STATUS_CHECK_CONDITION,
529                     SCSI_SENSE_KEY_ILLEGAL_REQUEST,
530                     ASQ_INVALID_FIELD_IN_CDB);
531         min_addr = (cdb[2] << 8) + cdb[3];
532         max_elts = (cdb[4] << 8) + cdb[5];
533         response_len = (cdb[7] << 16) + (cdb[8] << 8) + cdb[9];
534
535         if (response_len < 8) {
536                 return scsi_fail_with_sense_code(sess, reply,
537                     SCSI_STATUS_CHECK_CONDITION,
538                     SCSI_SENSE_KEY_ILLEGAL_REQUEST,
539                     ASQ_INVALID_FIELD_IN_CDB);
540         }
541
542         /* this is bogus, but we don't allow "partial" status requests */
543         if (min_addr > IE_FIRST || max_elts < num_elts) {
544                 return scsi_fail_with_sense_code(sess, reply,
545                     SCSI_STATUS_CHECK_CONDITION,
546                     SCSI_SENSE_KEY_ILLEGAL_REQUEST,
547                     ASQ_INVALID_FIELD_IN_CDB);
548         }
549
550         robot_state_load(sess, &rs);
551         robot_state_save(sess, &rs);
552
553         /* calculate the total space required */
554         required_len = 8; /* element status data header */
555         if (MTE_COUNT) {
556                 required_len += 8; /* element status page header */
557                 required_len += 12 * MTE_COUNT; /* element status descriptor w/o tags */
558         }
559         if (STORAGE_COUNT) {
560                 required_len += 8; /* element status page header */
561                 required_len += 84 * STORAGE_COUNT; /* element status descriptor w/ tags */
562         }
563         if (IE_COUNT) {
564                 required_len += 8; /* element status page header */
565                 required_len += 84 * IE_COUNT; /* element status descriptor w/ tags */
566         }
567         if (DTE_COUNT) {
568                 required_len += 8; /* element status page header */
569                 required_len += 84 * DTE_COUNT; /* element status descriptor w/ tags */
570         }
571
572         p = response = NDMOS_API_MALLOC(response_len);
573         NDMOS_API_BZERO(response, response_len);
574
575         /* write the element status data header */
576         *(p++) = IE_FIRST >> 8; /* first element address */
577         *(p++) = IE_FIRST & 0xff;
578         *(p++) = num_elts >> 8; /* number of elements */
579         *(p++) = num_elts & 0xff;
580         *(p++) = 0; /* reserved */
581         *(p++) = (required_len-8) >> 16; /* remaining byte count of report */
582         *(p++) = ((required_len-8) >> 8) & 0xff;
583         *(p++) = (required_len-8) & 0xff;
584
585         /* only fill in the rest if we have space */
586         if (required_len <= response_len) {
587                 int i;
588                 struct {
589                         int first, count, have_voltags, eltype;
590                         int empty_flags, full_flags;
591                         struct element_state *es;
592                 } page[4] = {
593                         { IE_FIRST, IE_COUNT, 1, 3, 0x38, 0x39, &rs.ie[0] },
594                         { MTE_FIRST, MTE_COUNT, 0, 1, 0x00, 0x01, &rs.mte[0] },
595                         { DTE_FIRST, DTE_COUNT, 1, 4, 0x08, 0x81, &rs.dte[0] },
596                         { STORAGE_FIRST, STORAGE_COUNT, 1, 2, 0x08, 0x09, &rs.storage[0] },
597                 };
598
599                 for (i = 0; i < 4; i++) {
600                         int descr_size = page[i].have_voltags? 84 : 12;
601                         int totalsize = descr_size * page[i].count;
602                         int j;
603
604                         if (page[i].count == 0)
605                                 continue;
606
607                         /* write the page header */
608                         *(p++) = page[i].eltype;
609                         *(p++) = page[i].have_voltags? 0xc0 : 0;
610                         *(p++) = 0;
611                         *(p++) = descr_size;
612                         *(p++) = 0; /* reserved */
613                         *(p++) = totalsize >> 16;
614                         *(p++) = (totalsize >> 8) & 0xff;
615                         *(p++) = totalsize & 0xff;
616
617                         /* and write each descriptor */
618                         for (j = 0; j < page[i].count; j++) {
619                                 int elt_addr = page[i].first + j;
620                                 int src_elt = page[i].es[j].source_element;
621                                 unsigned char byte9 = page[i].es[j].medium_type;
622                                 if (src_elt!= 0)
623                                         byte9 |= 0x80; /* SVALID */
624
625                                 *(p++) = elt_addr >> 8;
626                                 *(p++) = elt_addr & 0xff;
627                                 *(p++) = page[i].es[j].full?
628                                             page[i].full_flags : page[i].empty_flags;
629                                 *(p++) = 0;
630                                 *(p++) = 0;
631                                 *(p++) = 0;
632                                 *(p++) = 0;
633                                 *(p++) = 0;
634                                 *(p++) = 0;
635                                 *(p++) = byte9;
636                                 *(p++) = src_elt >> 8;
637                                 *(p++) = src_elt & 0xff;
638
639                                 if (page[i].have_voltags) {
640                                         int k;
641                                         if (page[i].es[j].full) {
642                                                 for (k = 0; k < 32; k++) {
643                                                         if (!page[i].es[j].pvoltag[k])
644                                                                 break;
645                                                         p[k] = page[i].es[j].pvoltag[k];
646                                                 }
647                                                 for (k = 0; k < 32; k++) {
648                                                         if (!page[i].es[j].avoltag[k])
649                                                                 break;
650                                                         p[k+36] = page[i].es[j].avoltag[k];
651                                                 }
652                                         } else {
653                                                 for (k = 0; k < 32; k++) {
654                                                         p[k] = p[k+36] = ' ';
655                                                 }
656                                         }
657                                         p += 72;
658                                 }
659                         }
660                 }
661         }
662
663         reply->datain.datain_len = response_len;
664         reply->datain.datain_val = response;
665
666         return NDMP9_NO_ERR;
667 }
668
669 static ndmp9_error
670 execute_cdb_move_medium (struct ndm_session *sess,
671   ndmp9_execute_cdb_request *request,
672   ndmp9_execute_cdb_reply *reply)
673 {
674         unsigned char *cdb = (unsigned char *)request->cdb.cdb_val;
675         struct robot_state rs;
676         int mte, src, dest;
677
678         if (request->cdb.cdb_len != 12)
679                 return scsi_fail_with_sense_code(sess, reply,
680                     SCSI_STATUS_CHECK_CONDITION,
681                     SCSI_SENSE_KEY_ILLEGAL_REQUEST,
682                     ASQ_INVALID_FIELD_IN_CDB);
683         mte = (cdb[2] << 8) + cdb[3];
684         src = (cdb[4] << 8) + cdb[5];
685         dest = (cdb[6] << 8) + cdb[7];
686
687         if (!IS_MTE_ADDR(mte))
688                 return scsi_fail_with_sense_code(sess, reply,
689                     SCSI_STATUS_CHECK_CONDITION,
690                     SCSI_SENSE_KEY_ILLEGAL_REQUEST,
691                     ASQ_INVALID_ELEMENT_ADDRESS);
692
693         robot_state_load(sess, &rs);
694         if (robot_state_move(sess, &rs, src, dest) < 0)
695                 return scsi_fail_with_sense_code(sess, reply,
696                     SCSI_STATUS_CHECK_CONDITION,
697                     SCSI_SENSE_KEY_ILLEGAL_REQUEST,
698                     ASQ_INVALID_ELEMENT_ADDRESS);
699         robot_state_save(sess, &rs);
700
701         return NDMP9_NO_ERR;
702 }
703
704 static struct {
705         char cdb_byte;
706         ndmp9_error (* execute_cdb)(
707                   struct ndm_session *sess,
708                   ndmp9_execute_cdb_request *request,
709                   ndmp9_execute_cdb_reply *reply);
710 } cdb_executors[] = {
711         { SCSI_CMD_TEST_UNIT_READY, execute_cdb_test_unit_ready },
712         { SCSI_CMD_INQUIRY, execute_cdb_inquiry },
713         { SCSI_CMD_MODE_SENSE_6, execute_cdb_mode_sense_6 },
714         { SCSI_CMD_READ_ELEMENT_STATUS, execute_cdb_read_element_status },
715         { SCSI_CMD_MOVE_MEDIUM, execute_cdb_move_medium },
716         { 0, 0 },
717 };
718
719 ndmp9_error
720 ndmos_scsi_execute_cdb (struct ndm_session *sess,
721   ndmp9_execute_cdb_request *request,
722   ndmp9_execute_cdb_reply *reply)
723 {
724         struct ndm_robot_agent *        ra = &sess->robot_acb;
725         char cdb_byte;
726         int i;
727
728         if (ra->scsi_state.error != NDMP9_NO_ERR)
729                 return ra->scsi_state.error;
730
731         if (request->cdb.cdb_len < 1)
732                 return NDMP9_ILLEGAL_ARGS_ERR;
733
734         cdb_byte = request->cdb.cdb_val[0];
735         for (i = 0; cdb_executors[i].execute_cdb; i++) {
736                 if (cdb_executors[i].cdb_byte == cdb_byte)
737                         return cdb_executors[i].execute_cdb(sess, request, reply);
738         }
739
740         return NDMP9_ILLEGAL_ARGS_ERR;
741 }
742
743 #endif /* NDMOS_OPTION_ROBOT_SIMULATOR */
744
745 #endif /* !NDMOS_OPTION_NO_ROBOT_AGENT */