Mercurial > illumos > onarm
comparison usr/src/cmd/iscsi/iscsitgtd/t10_ssc.c @ 0:c9caec207d52 b86
Initial porting based on b86
author | Koji Uno <koji.uno@sun.com> |
---|---|
date | Tue, 02 Jun 2009 18:56:50 +0900 |
parents | |
children | 1a15d5aaf794 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:c9caec207d52 |
---|---|
1 /* | |
2 * CDDL HEADER START | |
3 * | |
4 * The contents of this file are subject to the terms of the | |
5 * Common Development and Distribution License (the "License"). | |
6 * You may not use this file except in compliance with the License. | |
7 * | |
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE | |
9 * or http://www.opensolaris.org/os/licensing. | |
10 * See the License for the specific language governing permissions | |
11 * and limitations under the License. | |
12 * | |
13 * When distributing Covered Code, include this CDDL HEADER in each | |
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. | |
15 * If applicable, add the following below this CDDL HEADER, with the | |
16 * fields enclosed by brackets "[]" replaced with your own identifying | |
17 * information: Portions Copyright [yyyy] [name of copyright owner] | |
18 * | |
19 * CDDL HEADER END | |
20 */ | |
21 | |
22 /* | |
23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. | |
24 * Use is subject to license terms. | |
25 */ | |
26 | |
27 #pragma ident "@(#)t10_ssc.c 1.6 06/12/16 SMI" | |
28 | |
29 /* | |
30 * Implementation of SSC-2 emulation | |
31 */ | |
32 | |
33 #include <strings.h> | |
34 #include <unistd.h> | |
35 #include <sys/types.h> | |
36 #include <aio.h> | |
37 #include <sys/asynch.h> | |
38 #include <sys/scsi/generic/sense.h> | |
39 #include <sys/scsi/generic/status.h> | |
40 #include <sys/scsi/targets/stdef.h> | |
41 #include <netinet/in.h> | |
42 | |
43 #include "target.h" | |
44 #include "utility.h" | |
45 #include "t10.h" | |
46 #include "t10_spc.h" | |
47 #include "t10_ssc.h" | |
48 | |
49 /* | |
50 * []---- | |
51 * | Forward declarations | |
52 * []---- | |
53 */ | |
54 static scsi_cmd_table_t ssc_table[]; | |
55 static void ssc_cmd(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len); | |
56 static void ssc_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, | |
57 char *data, size_t data_len); | |
58 static void ssc_free(emul_handle_t e); | |
59 static void ssc_write_cmplt(emul_handle_t e); | |
60 static void ssc_read_cmplt(emul_handle_t id); | |
61 static void ssc_setup_tape(ssc_params_t *s, t10_lu_common_t *lu); | |
62 static uint32_t find_last_obj_id(char *file_mark, off_t eod); | |
63 static char *sense_dev_config(ssc_params_t *s, char *data); | |
64 static char *sense_compression(ssc_params_t *s, char *data); | |
65 | |
66 static long ssc_page_size; | |
67 | |
68 /* | |
69 * []---- | |
70 * | ssc_init_common -- initialize common information that all ITLs will use | |
71 * []---- | |
72 */ | |
73 Boolean_t | |
74 ssc_common_init(t10_lu_common_t *lu) | |
75 { | |
76 ssc_params_t *s; | |
77 ssc_obj_mark_t mark; | |
78 | |
79 ssc_page_size = sysconf(_SC_PAGESIZE); | |
80 | |
81 if (lu->l_mmap == MAP_FAILED) | |
82 return (False); | |
83 | |
84 if ((s = (ssc_params_t *)calloc(1, sizeof (*s))) == NULL) | |
85 return (False); | |
86 | |
87 s->s_size = lu->l_size; | |
88 s->s_fast_write_ack = lu->l_fast_write_ack; | |
89 | |
90 bcopy(lu->l_mmap, &mark, sizeof (mark)); | |
91 if (mark.som_sig != SSC_OBJ_SIG) { | |
92 ssc_setup_tape(s, lu); | |
93 } | |
94 s->s_cur_fm = 0; | |
95 s->s_cur_rec = sizeof (ssc_obj_mark_t); | |
96 s->s_prev_rec = s->s_cur_rec; | |
97 s->s_state = lu->l_state; | |
98 | |
99 lu->l_dtype_params = (void *)s; | |
100 return (True); | |
101 } | |
102 | |
103 /* | |
104 * []---- | |
105 * | ssc_fini_common -- free any resources | |
106 * []---- | |
107 */ | |
108 void | |
109 ssc_common_fini(t10_lu_common_t *lu) | |
110 { | |
111 free(lu->l_dtype_params); | |
112 } | |
113 | |
114 void | |
115 ssc_task_mgmt(t10_lu_common_t *lu, TaskOp_t op) | |
116 { | |
117 ssc_params_t *s = (ssc_params_t *)lu->l_dtype_params; | |
118 | |
119 switch (op) { | |
120 case CapacityChange: | |
121 s->s_size = lu->l_size; | |
122 break; | |
123 | |
124 case DeviceOnline: | |
125 s->s_state = lu->l_state; | |
126 } | |
127 } | |
128 | |
129 /* | |
130 * []---- | |
131 * | ssc_init_per -- initialize per ITL information | |
132 * []---- | |
133 */ | |
134 void | |
135 ssc_per_init(t10_lu_impl_t *itl) | |
136 { | |
137 ssc_params_t *s = (ssc_params_t *)itl->l_common->l_dtype_params; | |
138 | |
139 if (s->s_state == lu_online) | |
140 itl->l_cmd = ssc_cmd; | |
141 else | |
142 itl->l_cmd = spc_cmd_offline; | |
143 itl->l_data = ssc_data; | |
144 itl->l_cmd_table = ssc_table; | |
145 } | |
146 | |
147 /* | |
148 * []---- | |
149 * | ssc_fini_per -- release or free any ITL resources | |
150 * []---- | |
151 */ | |
152 /*ARGSUSED*/ | |
153 void | |
154 ssc_per_fini(t10_lu_impl_t *itl) | |
155 { | |
156 } | |
157 | |
158 /* | |
159 * []---- | |
160 * | ssc_cmd -- start a SCSI command | |
161 * | | |
162 * | This routine is called from within the SAM-3 Task router. | |
163 * []---- | |
164 */ | |
165 static void | |
166 ssc_cmd(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) | |
167 { | |
168 scsi_cmd_table_t *e; | |
169 | |
170 e = &cmd->c_lu->l_cmd_table[cdb[0]]; | |
171 #ifdef FULL_DEBUG | |
172 queue_prt(mgmtq, Q_STE_IO, "SSC%x LUN%d Cmd %s\n", | |
173 cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, | |
174 e->cmd_name == NULL ? "(no name)" : e->cmd_name); | |
175 #endif | |
176 (*e->cmd_start)(cmd, cdb, cdb_len); | |
177 } | |
178 | |
179 /* | |
180 * []---- | |
181 * | ssc_data -- Data phase for command. | |
182 * | | |
183 * | Normally this is only called for the WRITE command. Other commands | |
184 * | that have a data in phase will probably be short circuited when | |
185 * | we call trans_rqst_dataout() and the data is already available. | |
186 * | At least this is true for iSCSI. FC however will need a DataIn phase | |
187 * | for commands like MODE SELECT and PGROUT. | |
188 * []---- | |
189 */ | |
190 static void | |
191 ssc_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data, | |
192 size_t data_len) | |
193 { | |
194 scsi_cmd_table_t *e; | |
195 | |
196 e = &cmd->c_lu->l_cmd_table[cmd->c_cdb[0]]; | |
197 #ifdef FULL_DEBUG | |
198 queue_prt(mgmtq, Q_STE_IO, "SSC%x LUN%d Data %s\n", | |
199 cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, | |
200 e->cmd_name); | |
201 #endif | |
202 (*e->cmd_data)(cmd, id, offset, data, data_len); | |
203 } | |
204 | |
205 /* | |
206 * []------------------------------------------------------------------[] | |
207 * | SCSI Streaming Commands - 3 | | |
208 * | T10/1611-D Revision 01c | | |
209 * | The following functions implement the emulation of SSC-3 type | | |
210 * | commands. | | |
211 * []------------------------------------------------------------------[] | |
212 */ | |
213 | |
214 /*ARGSUSED*/ | |
215 static void | |
216 ssc_read(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) | |
217 { | |
218 ssc_io_t *io; | |
219 ssc_params_t *s = (ssc_params_t *)T10_PARAMS_AREA(cmd); | |
220 ssc_obj_mark_t fm, | |
221 rm; | |
222 int fixed, | |
223 sili; | |
224 off_t offset = 0; | |
225 size_t xfer, | |
226 req_len; | |
227 void *mmap = cmd->c_lu->l_common->l_mmap; | |
228 t10_cmd_t *c; | |
229 | |
230 fixed = cdb[1] & 0x01; | |
231 sili = cdb[1] & 0x02; | |
232 | |
233 if (s == NULL) | |
234 return; | |
235 | |
236 /* | |
237 * Standard error checking. | |
238 */ | |
239 if ((sili && fixed) || (cdb[1] & 0xfc) || | |
240 SAM_CONTROL_BYTE_RESERVED(cdb[5])) { | |
241 spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); | |
242 spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); | |
243 trans_send_complete(cmd, STATUS_CHECK); | |
244 return; | |
245 } | |
246 | |
247 req_len = (cdb[2] << 16) | (cdb[3] << 8) | cdb[4]; | |
248 req_len *= fixed ? 512 : 1; | |
249 | |
250 if (req_len == 0) { | |
251 trans_send_complete(cmd, STATUS_GOOD); | |
252 return; | |
253 } | |
254 | |
255 #ifdef FULL_DEBUG | |
256 queue_prt(mgmtq, Q_STE_IO, | |
257 "SSC%x LUN%d read 0x%x bytes", | |
258 cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, req_len); | |
259 #endif | |
260 | |
261 bcopy((char *)mmap + s->s_cur_fm, &fm, sizeof (fm)); | |
262 bcopy((char *)mmap + s->s_cur_fm + s->s_cur_rec, &rm, sizeof (rm)); | |
263 | |
264 if (rm.som_sig != SSC_OBJ_SIG) { | |
265 queue_prt(mgmtq, Q_STE_ERRS, | |
266 "SSC%x LUN%d bad RECORD-MARK", | |
267 cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num); | |
268 spc_sense_create(cmd, KEY_MEDIUM_ERROR, 0); | |
269 trans_send_complete(cmd, STATUS_CHECK); | |
270 return; | |
271 } else if (rm.som_type != SSC_OBJ_TYPE_RM) { | |
272 s->s_cur_fm += fm.o_fm.size; | |
273 s->s_cur_rec = sizeof (ssc_obj_mark_t); | |
274 s->s_prev_rec = s->s_cur_rec; | |
275 | |
276 spc_sense_create(cmd, KEY_NO_SENSE, 0); | |
277 spc_sense_ascq(cmd, SPC_ASC_FM_DETECTED, SPC_ASCQ_FM_DETECTED); | |
278 spc_sense_info(cmd, req_len); | |
279 spc_sense_flags(cmd, SPC_SENSE_FM); | |
280 trans_send_complete(cmd, STATUS_CHECK); | |
281 return; | |
282 } else if ((sili == 0) && | |
283 ((rm.o_rm.size - sizeof (ssc_obj_mark_t)) != req_len)) { | |
284 queue_prt(mgmtq, Q_STE_ERRS, | |
285 "SSC%x LUN%d Wrong size read", | |
286 cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num); | |
287 | |
288 s->s_prev_rec = s->s_cur_rec; | |
289 s->s_cur_rec += rm.o_rm.size; | |
290 | |
291 spc_sense_create(cmd, KEY_NO_SENSE, 0); | |
292 spc_sense_flags(cmd, SPC_SENSE_ILI); | |
293 trans_send_complete(cmd, STATUS_CHECK); | |
294 return; | |
295 } | |
296 | |
297 do { | |
298 xfer = MIN((req_len - offset), T10_MAX_OUT(cmd)); | |
299 if ((offset + xfer) < req_len) | |
300 c = trans_cmd_dup(cmd); | |
301 else | |
302 c = cmd; | |
303 if ((io = (ssc_io_t *)calloc(1, sizeof (*io))) == NULL) { | |
304 trans_send_complete(c, STATUS_BUSY); | |
305 return; | |
306 } | |
307 | |
308 io->sio_cmd = c; | |
309 io->sio_offset = offset; | |
310 io->sio_total = req_len; | |
311 io->sio_data_len = xfer; | |
312 io->sio_data = (char *)mmap + s->s_cur_fm + | |
313 s->s_cur_rec + sizeof (ssc_obj_mark_t) + offset; | |
314 io->sio_aio.a_aio.aio_return = xfer; | |
315 | |
316 ssc_read_cmplt((emul_handle_t)io); | |
317 offset += xfer; | |
318 } while (offset < req_len); | |
319 | |
320 s->s_prev_rec = s->s_cur_rec; | |
321 s->s_cur_rec += req_len + sizeof (ssc_obj_mark_t); | |
322 } | |
323 | |
324 static void | |
325 ssc_read_cmplt(emul_handle_t id) | |
326 { | |
327 ssc_io_t *io = (ssc_io_t *)id; | |
328 t10_cmd_t *cmd = io->sio_cmd; | |
329 | |
330 if (io->sio_aio.a_aio.aio_return != io->sio_data_len) { | |
331 spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0); | |
332 trans_send_complete(cmd, STATUS_CHECK); | |
333 return; | |
334 } | |
335 | |
336 if ((io->sio_offset + io->sio_data_len) < io->sio_total) { | |
337 if (trans_send_datain(cmd, io->sio_data, io->sio_data_len, | |
338 io->sio_offset, ssc_free, False, io) == False) { | |
339 trans_send_complete(cmd, STATUS_BUSY); | |
340 } | |
341 } else { | |
342 if (trans_send_datain(cmd, io->sio_data, io->sio_data_len, | |
343 io->sio_offset, ssc_free, True, io) == False) { | |
344 trans_send_complete(cmd, STATUS_BUSY); | |
345 } | |
346 } | |
347 } | |
348 | |
349 /*ARGSUSED*/ | |
350 static void | |
351 ssc_write(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) | |
352 { | |
353 ssc_obj_mark_t mark; | |
354 size_t request_len, | |
355 max_xfer; | |
356 int fixed, | |
357 prev_id; | |
358 ssc_io_t *io; | |
359 ssc_params_t *s = (ssc_params_t *)T10_PARAMS_AREA(cmd); | |
360 | |
361 if (s == NULL) | |
362 return; | |
363 | |
364 if ((cdb[1] & 0xfe) || SAM_CONTROL_BYTE_RESERVED(cdb[5])) { | |
365 spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); | |
366 spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); | |
367 trans_send_complete(cmd, STATUS_CHECK); | |
368 return; | |
369 } | |
370 fixed = cdb[1]; | |
371 request_len = (cdb[2] << 16) | (cdb[3] << 8) | cdb[4]; | |
372 request_len *= fixed ? 512 : 1; | |
373 | |
374 #ifdef FULL_DEBUG | |
375 queue_prt(mgmtq, Q_STE_IO, | |
376 "SSC%x LUN%d write %d, fixed %d", | |
377 cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, | |
378 request_len, fixed); | |
379 #endif | |
380 io = cmd->c_emul_id; | |
381 if (io == NULL) { | |
382 if ((io = calloc(1, sizeof (*io))) == NULL) { | |
383 trans_send_complete(cmd, STATUS_BUSY); | |
384 return; | |
385 } | |
386 io->sio_total = request_len; | |
387 io->sio_cmd = cmd; | |
388 io->sio_offset = 0; | |
389 | |
390 /* | |
391 * Writing looses all information after the current | |
392 * file-mark. So, check to see if the current file-mark | |
393 * size doesn't reflect the end-of-media. If not, update | |
394 * it. | |
395 */ | |
396 bcopy((char *)cmd->c_lu->l_common->l_mmap + s->s_cur_fm, | |
397 &mark, sizeof (mark)); | |
398 if (mark.o_fm.size != | |
399 (s->s_size - sizeof (ssc_obj_mark_t) - s->s_cur_fm)) { | |
400 mark.o_fm.size = s->s_size - sizeof (ssc_obj_mark_t) - | |
401 s->s_cur_fm; | |
402 bcopy(&mark, (char *)cmd->c_lu->l_common->l_mmap + | |
403 s->s_cur_fm, sizeof (mark)); | |
404 } | |
405 | |
406 /* | |
407 * End-of-Partition detection | |
408 */ | |
409 if ((s->s_cur_rec + request_len) > (mark.o_fm.size)) { | |
410 spc_sense_create(cmd, KEY_VOLUME_OVERFLOW, 0); | |
411 spc_sense_ascq(cmd, SPC_ASC_EOP, SPC_ASCQ_EOP); | |
412 spc_sense_flags(cmd, SPC_SENSE_EOM); | |
413 trans_send_complete(cmd, STATUS_CHECK); | |
414 return; | |
415 } | |
416 | |
417 if ((s->s_cur_fm == 0) && | |
418 (s->s_cur_rec == sizeof (ssc_obj_mark_t))) { | |
419 | |
420 /* | |
421 * The current position is a BOM. By setting | |
422 * the prev_id value to -1 the code below will | |
423 * create the first ID with a value of zero | |
424 * Which is what the specification requires. | |
425 */ | |
426 prev_id = -1; | |
427 | |
428 } else if (s->s_cur_rec == sizeof (ssc_obj_mark_t)) { | |
429 | |
430 /* | |
431 * If the current position is at the beginning of | |
432 * this file-mark use the object ID found in | |
433 * the file-mark header. It will have been updated | |
434 * from the last object ID in the previous file-mark. | |
435 * | |
436 * NOTE: We're counting on 'mark' still referring | |
437 * to the current file mark here. | |
438 */ | |
439 prev_id = mark.o_fm.last_obj_id; | |
440 } else { | |
441 bcopy((char *)cmd->c_lu->l_common->l_mmap + | |
442 s->s_cur_fm + s->s_prev_rec, &mark, sizeof (mark)); | |
443 prev_id = mark.o_rm.obj_id; | |
444 } | |
445 | |
446 bzero(&mark, sizeof (mark)); | |
447 mark.som_sig = SSC_OBJ_SIG; | |
448 mark.som_type = SSC_OBJ_TYPE_RM; | |
449 mark.o_rm.size = request_len + | |
450 sizeof (ssc_obj_mark_t); | |
451 mark.o_rm.obj_id = prev_id + 1; | |
452 bcopy(&mark, (char *)cmd->c_lu->l_common->l_mmap + | |
453 s->s_cur_fm + s->s_cur_rec, sizeof (mark)); | |
454 } | |
455 | |
456 max_xfer = min(io->sio_total - io->sio_offset, | |
457 cmd->c_lu->l_targ->s_maxout); | |
458 io->sio_aio.a_aio.aio_return = max_xfer; | |
459 io->sio_data_len = max_xfer; | |
460 io->sio_data = (char *)cmd->c_lu->l_common->l_mmap + | |
461 s->s_cur_fm + s->s_cur_rec + sizeof (mark) + io->sio_offset; | |
462 | |
463 if (trans_rqst_dataout(cmd, io->sio_data, io->sio_data_len, | |
464 io->sio_offset, io, ssc_free) == False) { | |
465 trans_send_complete(cmd, STATUS_BUSY); | |
466 } | |
467 } | |
468 | |
469 /*ARGSUSED*/ | |
470 static void | |
471 ssc_write_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data, | |
472 size_t data_len) | |
473 { | |
474 ssc_io_t *io = (ssc_io_t *)id; | |
475 ssc_params_t *s = (ssc_params_t *)T10_PARAMS_AREA(cmd); | |
476 | |
477 if (s == NULL) | |
478 return; | |
479 | |
480 if (s->s_fast_write_ack == False) { | |
481 uint64_t sa; | |
482 size_t len; | |
483 | |
484 /* | |
485 * msync requires the address to be page aligned. | |
486 * That means we need to account for any alignment | |
487 * loss in the len field and access the full page. | |
488 */ | |
489 sa = (uint64_t)(intptr_t)data & ~(ssc_page_size - 1); | |
490 len = (((size_t)data & (ssc_page_size - 1)) + | |
491 data_len + ssc_page_size - 1) & | |
492 ~(ssc_page_size -1); | |
493 | |
494 /* | |
495 * We only need to worry about sync'ing the blocks | |
496 * in the mmap case because if the fast cache isn't | |
497 * enabled for AIO the file will be opened with F_SYNC | |
498 * which performs the correct action. | |
499 */ | |
500 if (msync((char *)(intptr_t)sa, len, MS_SYNC) == -1) { | |
501 perror("msync"); | |
502 spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0); | |
503 trans_send_complete(cmd, STATUS_CHECK); | |
504 return; | |
505 } | |
506 } | |
507 ssc_write_cmplt((emul_handle_t)io); | |
508 } | |
509 | |
510 static void | |
511 ssc_write_cmplt(emul_handle_t e) | |
512 { | |
513 ssc_io_t *io = (ssc_io_t *)e; | |
514 t10_cmd_t *cmd = io->sio_cmd; | |
515 ssc_params_t *s = (ssc_params_t *)T10_PARAMS_AREA(cmd); | |
516 | |
517 if (s == NULL) | |
518 return; | |
519 | |
520 if ((io->sio_offset + io->sio_data_len) < io->sio_total) { | |
521 io->sio_offset += io->sio_data_len; | |
522 ssc_write(cmd, cmd->c_cdb, cmd->c_cdb_len); | |
523 return; | |
524 } | |
525 | |
526 s->s_prev_rec = s->s_cur_rec; | |
527 s->s_cur_rec += io->sio_total + sizeof (ssc_obj_mark_t); | |
528 free(io); | |
529 trans_send_complete(cmd, STATUS_GOOD); | |
530 } | |
531 | |
532 /*ARGSUSED*/ | |
533 static void | |
534 ssc_rewind(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) | |
535 { | |
536 ssc_params_t *s = (ssc_params_t *)T10_PARAMS_AREA(cmd); | |
537 | |
538 if (s == NULL) | |
539 return; | |
540 | |
541 if ((cdb[1] & ~SSC_REWIND_IMMED) || cdb[2] || cdb[3] || cdb[4] || | |
542 SAM_CONTROL_BYTE_RESERVED(cdb[5])) { | |
543 spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); | |
544 spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); | |
545 trans_send_complete(cmd, STATUS_CHECK); | |
546 return; | |
547 } | |
548 | |
549 s->s_cur_fm = 0; | |
550 s->s_cur_rec = sizeof (ssc_obj_mark_t); | |
551 trans_send_complete(cmd, STATUS_GOOD); | |
552 } | |
553 | |
554 /*ARGSUSED*/ | |
555 static void | |
556 ssc_read_limits(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) | |
557 { | |
558 struct read_blklim *rb; | |
559 int min_size = 512; | |
560 | |
561 if (cdb[1] || cdb[2] || cdb[3] || cdb[4] || | |
562 SAM_CONTROL_BYTE_RESERVED(cdb[5])) { | |
563 spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); | |
564 spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); | |
565 trans_send_complete(cmd, STATUS_CHECK); | |
566 return; | |
567 } | |
568 | |
569 if ((rb = (struct read_blklim *)calloc(1, sizeof (*rb))) == NULL) { | |
570 trans_send_complete(cmd, STATUS_BUSY); | |
571 return; | |
572 } | |
573 | |
574 /* | |
575 * maximum block size is set to zero to indicate no maximum block | |
576 * limit is specified. | |
577 */ | |
578 rb->granularity = 9; /* 512 block sizes */ | |
579 rb->min_hi = hibyte(min_size); | |
580 rb->min_lo = lobyte(min_size); | |
581 | |
582 if (trans_send_datain(cmd, (char *)rb, sizeof (*rb), 0, ssc_free, | |
583 True, (emul_handle_t)rb) == False) { | |
584 trans_send_complete(cmd, STATUS_BUSY); | |
585 } | |
586 } | |
587 | |
588 /*ARGSUSED*/ | |
589 static void | |
590 ssc_space(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) | |
591 { | |
592 int code, | |
593 count; | |
594 ssc_params_t *s = T10_PARAMS_AREA(cmd); | |
595 ssc_obj_mark_t mark; | |
596 t10_lu_common_t *lu = cmd->c_lu->l_common; | |
597 | |
598 if (s == NULL) | |
599 return; | |
600 | |
601 if ((cdb[1] & 0xf0) || SAM_CONTROL_BYTE_RESERVED(cdb[5])) { | |
602 spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); | |
603 spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); | |
604 trans_send_complete(cmd, STATUS_CHECK); | |
605 return; | |
606 } | |
607 | |
608 code = cdb[1] & 0x0f; | |
609 count = (cdb[2] << 16) | (cdb[3] << 8) | cdb[4]; | |
610 | |
611 if ((count == 0) && (code != SSC_SPACE_CODE_END_OF_DATA)) { | |
612 trans_send_complete(cmd, STATUS_GOOD); | |
613 return; | |
614 } | |
615 | |
616 switch (code) { | |
617 case SSC_SPACE_CODE_BLOCKS: | |
618 if (count < 0) { | |
619 bcopy((char *)lu->l_mmap + s->s_cur_fm + s->s_cur_rec, | |
620 &mark, sizeof (mark)); | |
621 if ((mark.som_sig == SSC_OBJ_SIG) && | |
622 (mark.som_type == SSC_OBJ_TYPE_RM)) { | |
623 count = mark.o_rm.obj_id + count; | |
624 | |
625 /* | |
626 * If the count is still negative it means | |
627 * the request is still attempting to go | |
628 * beyond the beginning of the file mark. | |
629 */ | |
630 if (count < 0) { | |
631 count *= -1; | |
632 spc_sense_create(cmd, KEY_NO_SENSE, 0); | |
633 spc_sense_ascq(cmd, | |
634 SPC_ASC_FM_DETECTED, | |
635 SPC_ASCQ_FM_DETECTED); | |
636 spc_sense_info(cmd, count); | |
637 trans_send_complete(cmd, STATUS_CHECK); | |
638 return; | |
639 } | |
640 s->s_cur_rec = s->s_cur_fm + sizeof (mark); | |
641 } else { | |
642 /* | |
643 * Something is not right. We'll let the | |
644 * processing below determine exactly what | |
645 * is wrong. So don't update the record | |
646 * mark and sent the count to 1. | |
647 */ | |
648 count = 1; | |
649 } | |
650 } | |
651 | |
652 while (count) { | |
653 bcopy((char *)lu->l_mmap + s->s_cur_fm + s->s_cur_rec, | |
654 &mark, sizeof (mark)); | |
655 | |
656 /* | |
657 * Something internally bad has happened with | |
658 * the marks in the file. | |
659 */ | |
660 if (mark.som_sig != SSC_OBJ_SIG) { | |
661 queue_prt(mgmtq, Q_STE_ERRS, | |
662 "SSC%x LUN%d, bad sig mark: " | |
663 "expected=0x%x, got=0x%x", | |
664 cmd->c_lu->l_targ->s_targ_num, | |
665 cmd->c_lu->l_common->l_num, | |
666 SSC_OBJ_SIG, mark.som_sig); | |
667 spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0); | |
668 trans_send_complete(cmd, STATUS_CHECK); | |
669 return; | |
670 } | |
671 | |
672 /* | |
673 * Hit a filemark. Update the current record if | |
674 * we're not at the End-Of-Medium. | |
675 */ | |
676 if (mark.som_type == SSC_OBJ_TYPE_FM) { | |
677 if (mark.o_fm.eom == True) { | |
678 spc_sense_create(cmd, KEY_MEDIUM_ERROR, | |
679 0); | |
680 spc_sense_ascq(cmd, SPC_ASC_EOP, | |
681 SPC_ASCQ_EOP); | |
682 spc_sense_info(cmd, count); | |
683 spc_sense_flags(cmd, SPC_SENSE_EOM); | |
684 trans_send_complete(cmd, STATUS_CHECK); | |
685 return; | |
686 } | |
687 s->s_cur_fm += s->s_cur_rec; | |
688 s->s_cur_rec += sizeof (mark); | |
689 spc_sense_create(cmd, KEY_NO_SENSE, 0); | |
690 spc_sense_ascq(cmd, SPC_ASC_FM_DETECTED, | |
691 SPC_ASCQ_FM_DETECTED); | |
692 spc_sense_info(cmd, count); | |
693 trans_send_complete(cmd, STATUS_CHECK); | |
694 return; | |
695 } | |
696 s->s_cur_rec += mark.o_rm.size; | |
697 count--; | |
698 } | |
699 trans_send_complete(cmd, STATUS_CHECK); | |
700 break; | |
701 | |
702 case SSC_SPACE_CODE_FILEMARKS: | |
703 if (count < 0) { | |
704 bcopy((char *)lu->l_mmap + s->s_cur_fm, &mark, | |
705 sizeof (mark)); | |
706 if ((mark.som_sig == SSC_OBJ_SIG) && | |
707 (mark.som_type == SSC_OBJ_TYPE_FM)) { | |
708 count = mark.o_fm.num + count; | |
709 | |
710 /* | |
711 * If the count is still negative it means | |
712 * the request is still attempting to go | |
713 * beyond the beginning of the file mark. | |
714 */ | |
715 if (count < 0) { | |
716 count *= -1; | |
717 spc_sense_create(cmd, KEY_NO_SENSE, 0); | |
718 spc_sense_ascq(cmd, | |
719 SPC_ASC_FM_DETECTED, | |
720 SPC_ASCQ_FM_DETECTED); | |
721 spc_sense_info(cmd, count); | |
722 trans_send_complete(cmd, STATUS_CHECK); | |
723 return; | |
724 } | |
725 s->s_cur_fm = 0; | |
726 s->s_cur_rec = sizeof (ssc_obj_mark_t); | |
727 } else { | |
728 /* | |
729 * Something is not right. We'll let the | |
730 * processing below determine exactly what | |
731 * is wrong. So don't update the record | |
732 * mark and sent the count to 1. | |
733 */ | |
734 count = 1; | |
735 } | |
736 } | |
737 | |
738 while (count--) { | |
739 bcopy((char *)lu->l_mmap + s->s_cur_fm, &mark, | |
740 sizeof (mark)); | |
741 if (mark.som_sig != SSC_OBJ_SIG) { | |
742 queue_prt(mgmtq, Q_STE_ERRS, | |
743 "SSC%x LUN%d, bad sig mark: " | |
744 "expected=0x%x, got=0x%x", | |
745 cmd->c_lu->l_targ->s_targ_num, | |
746 cmd->c_lu->l_common->l_num, | |
747 SSC_OBJ_SIG, mark.som_sig); | |
748 spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0); | |
749 trans_send_complete(cmd, STATUS_CHECK); | |
750 return; | |
751 } | |
752 if (mark.som_type != SSC_OBJ_TYPE_FM) { | |
753 queue_prt(mgmtq, Q_STE_ERRS, | |
754 "SSC%x LUN%d, bad mark type: " | |
755 "expected=0x%x, got=0x%x", | |
756 cmd->c_lu->l_targ->s_targ_num, | |
757 cmd->c_lu->l_common->l_num, | |
758 SSC_OBJ_TYPE_FM, mark.som_type); | |
759 spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0); | |
760 trans_send_complete(cmd, STATUS_CHECK); | |
761 return; | |
762 } | |
763 if (mark.o_fm.eom == True) { | |
764 spc_sense_create(cmd, KEY_MEDIUM_ERROR, 0); | |
765 spc_sense_ascq(cmd, SPC_ASC_EOP, SPC_ASCQ_EOP); | |
766 spc_sense_info(cmd, count); | |
767 spc_sense_flags(cmd, SPC_SENSE_EOM); | |
768 trans_send_complete(cmd, STATUS_CHECK); | |
769 return; | |
770 } | |
771 s->s_cur_fm += mark.o_fm.size; | |
772 } | |
773 trans_send_complete(cmd, STATUS_GOOD); | |
774 break; | |
775 | |
776 default: | |
777 spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); | |
778 spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); | |
779 trans_send_complete(cmd, STATUS_CHECK); | |
780 return; | |
781 } | |
782 } | |
783 | |
784 /* | |
785 * []---- | |
786 * | ssc_msense -- MODE SENSE command | |
787 * | | |
788 * | This command is part of the SPC set, but is device specific enough | |
789 * | that it must be emulated in each device type. | |
790 * []---- | |
791 */ | |
792 /*ARGSUSED*/ | |
793 static void | |
794 ssc_msense(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) | |
795 { | |
796 ssc_params_t *s = T10_PARAMS_AREA(cmd); | |
797 struct mode_header *mode_hdr; | |
798 int request_len, | |
799 alloc_len; | |
800 char *data, | |
801 *np; | |
802 | |
803 /* | |
804 * SPC-3 Revision 21c section 6.8 | |
805 * Reserve bit checks | |
806 */ | |
807 if ((cdb[1] & ~8) || SAM_CONTROL_BYTE_RESERVED(cdb[5])) { | |
808 spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); | |
809 spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); | |
810 trans_send_complete(cmd, STATUS_CHECK); | |
811 return; | |
812 } | |
813 | |
814 /* | |
815 * Zero length causes a simple ack to occur. | |
816 */ | |
817 if (cdb[4] == 0) { | |
818 trans_send_complete(cmd, STATUS_GOOD); | |
819 return; | |
820 } else { | |
821 request_len = cdb[4]; | |
822 alloc_len = max(request_len, | |
823 sizeof (*mode_hdr) + MODE_BLK_DESC_LENGTH + | |
824 sizeof (ssc_data_compression_t) + sizeof (*mode_hdr) + | |
825 MODE_BLK_DESC_LENGTH + sizeof (ssc_device_config_t)); | |
826 } | |
827 | |
828 queue_prt(mgmtq, Q_STE_NONIO, "SSC%x LUN%d: MODE_SENSE(0x%x)", | |
829 cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, cdb[2]); | |
830 | |
831 if ((data = memalign(sizeof (void *), alloc_len)) == NULL) { | |
832 trans_send_complete(cmd, STATUS_BUSY); | |
833 return; | |
834 } | |
835 | |
836 mode_hdr = (struct mode_header *)data; | |
837 | |
838 switch (cdb[2]) { | |
839 case MODE_SENSE_COMPRESSION: | |
840 mode_hdr->length = sizeof (ssc_data_compression_t); | |
841 mode_hdr->bdesc_length = MODE_BLK_DESC_LENGTH; | |
842 (void) sense_compression(s, data + sizeof (*mode_hdr) + | |
843 mode_hdr->bdesc_length); | |
844 break; | |
845 | |
846 case MODE_SENSE_DEV_CONFIG: | |
847 mode_hdr->length = sizeof (ssc_device_config_t); | |
848 mode_hdr->bdesc_length = MODE_BLK_DESC_LENGTH; | |
849 (void) sense_dev_config(s, data + sizeof (*mode_hdr) + | |
850 mode_hdr->bdesc_length); | |
851 break; | |
852 | |
853 case MODE_SENSE_SEND_ALL: | |
854 np = sense_compression(s, data); | |
855 (void) sense_dev_config(s, np); | |
856 break; | |
857 | |
858 case 0x00: | |
859 bzero(data, alloc_len); | |
860 break; | |
861 | |
862 default: | |
863 queue_prt(mgmtq, Q_STE_ERRS, | |
864 "SSC%x LUN%d: MODE SENSE(0x%x) not handled", | |
865 cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, | |
866 cdb[2]); | |
867 spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); | |
868 spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); | |
869 trans_send_complete(cmd, STATUS_CHECK); | |
870 return; | |
871 } | |
872 | |
873 if (trans_send_datain(cmd, (char *)data, request_len, 0, ssc_free, | |
874 True, data) == False) { | |
875 trans_send_complete(cmd, STATUS_BUSY); | |
876 } | |
877 } | |
878 | |
879 /*ARGSUSED*/ | |
880 static void | |
881 ssc_read_pos(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) | |
882 { | |
883 int service_action, | |
884 request_len, | |
885 alloc_len; | |
886 pos_short_form_t *sf; | |
887 void *data; | |
888 ssc_params_t *s = (ssc_params_t *)T10_PARAMS_AREA(cmd); | |
889 ssc_obj_mark_t mark; | |
890 | |
891 if (s == NULL) | |
892 return; | |
893 | |
894 /* | |
895 * Standard reserve bit check | |
896 */ | |
897 if ((cdb[1] & 0xc0) || cdb[2] || cdb[3] || cdb[4] || cdb[5] || | |
898 cdb[6] || SAM_CONTROL_BYTE_RESERVED(cdb[9])) { | |
899 spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); | |
900 spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); | |
901 trans_send_complete(cmd, STATUS_CHECK); | |
902 return; | |
903 } | |
904 | |
905 service_action = cdb[1] & 0x1f; | |
906 request_len = (cdb[7] << 8) | cdb[8]; | |
907 switch (service_action) { | |
908 case SSC_READ_POS_SHORT_FORM: | |
909 alloc_len = max(request_len, sizeof (*sf)); | |
910 if ((data = memalign(sizeof (void *), alloc_len)) == NULL) { | |
911 trans_send_complete(cmd, STATUS_BUSY); | |
912 return; | |
913 } | |
914 sf = (pos_short_form_t *)data; | |
915 bcopy((char *)cmd->c_lu->l_common->l_mmap + s->s_cur_fm, | |
916 &mark, sizeof (mark)); | |
917 if ((mark.o_fm.bom == True) && | |
918 (s->s_cur_rec == sizeof (ssc_obj_mark_t))) | |
919 sf->bop = 1; | |
920 bcopy((char *)cmd->c_lu->l_common->l_mmap + s->s_cur_fm + | |
921 s->s_cur_rec, &mark, sizeof (mark)); | |
922 sf->first_obj[0] = hibyte(hiword(mark.o_rm.obj_id)); | |
923 sf->first_obj[1] = lobyte(hiword(mark.o_rm.obj_id)); | |
924 sf->first_obj[2] = hibyte(loword(mark.o_rm.obj_id)); | |
925 sf->first_obj[3] = lobyte(loword(mark.o_rm.obj_id)); | |
926 | |
927 /* | |
928 * We mark the last object to be the same as the first | |
929 * object which indicates that nothing is currently | |
930 * buffered. | |
931 */ | |
932 sf->last_obj[0] = sf->first_obj[0]; | |
933 sf->last_obj[1] = sf->first_obj[1]; | |
934 sf->last_obj[2] = sf->first_obj[2]; | |
935 sf->last_obj[3] = sf->first_obj[3]; | |
936 | |
937 break; | |
938 | |
939 case SSC_READ_POS_LONG_FORM: | |
940 spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); | |
941 spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); | |
942 trans_send_complete(cmd, STATUS_CHECK); | |
943 return; | |
944 | |
945 default: | |
946 spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); | |
947 spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); | |
948 trans_send_complete(cmd, STATUS_CHECK); | |
949 return; | |
950 } | |
951 | |
952 if (trans_send_datain(cmd, (char *)data, request_len, 0, ssc_free, | |
953 True, data) == False) { | |
954 trans_send_complete(cmd, STATUS_BUSY); | |
955 } | |
956 } | |
957 | |
958 /*ARGSUSED*/ | |
959 static void | |
960 ssc_rpt_density(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) | |
961 { | |
962 ssc_density_t *d; | |
963 ssc_density_media_t *dm; | |
964 ssc_params_t *s = (ssc_params_t *)T10_PARAMS_AREA(cmd); | |
965 size_t cap = s->s_size / (1024 * 1024); | |
966 int medium_type, | |
967 request_len, | |
968 alloc_len; | |
969 void *data; | |
970 | |
971 if (s == NULL) | |
972 return; | |
973 | |
974 if ((cdb[1] & 0xfc) || cdb[2] || cdb[3] || cdb[4] || cdb[5] || | |
975 cdb[6] || SAM_CONTROL_BYTE_RESERVED(cdb[9])) { | |
976 spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); | |
977 spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); | |
978 trans_send_complete(cmd, STATUS_CHECK); | |
979 return; | |
980 } | |
981 | |
982 medium_type = cdb[1] & 0x2; | |
983 request_len = (cdb[7] << 8) | cdb[8]; | |
984 alloc_len = max(request_len, max(sizeof (*d), sizeof (*dm))); | |
985 if ((data = memalign(sizeof (void *), alloc_len)) == NULL) { | |
986 trans_send_complete(cmd, STATUS_BUSY); | |
987 return; | |
988 } | |
989 if (medium_type == 0) { | |
990 d = (ssc_density_t *)data; | |
991 d->d_hdr.len = htons(sizeof (*d) - | |
992 sizeof (ssc_density_header_t)); | |
993 d->d_prim_code = 1; | |
994 d->d_wrtok = 1; | |
995 d->d_deflt = 1; | |
996 d->d_tracks[1] = 1; | |
997 d->d_capacity[0] = hibyte(hiword(cap)); | |
998 d->d_capacity[1] = lobyte(hiword(cap)); | |
999 d->d_capacity[2] = hibyte(loword(cap)); | |
1000 d->d_capacity[3] = lobyte(loword(cap)); | |
1001 bcopy(cmd->c_lu->l_common->l_vid, d->d_organization, | |
1002 min(sizeof (d->d_organization), | |
1003 strlen(cmd->c_lu->l_common->l_vid))); | |
1004 bcopy(cmd->c_lu->l_common->l_pid, d->d_description, | |
1005 min(sizeof (d->d_description), | |
1006 strlen(cmd->c_lu->l_common->l_pid))); | |
1007 } else { | |
1008 dm = (ssc_density_media_t *)data; | |
1009 dm->d_hdr.len = htons(sizeof (*d) - | |
1010 sizeof (ssc_density_header_t)); | |
1011 bcopy(cmd->c_lu->l_common->l_vid, d->d_organization, | |
1012 min(sizeof (d->d_organization), | |
1013 strlen(cmd->c_lu->l_common->l_vid))); | |
1014 bcopy(cmd->c_lu->l_common->l_pid, dm->d_description, | |
1015 min(sizeof (dm->d_description), | |
1016 strlen(cmd->c_lu->l_common->l_pid))); | |
1017 } | |
1018 | |
1019 if (trans_send_datain(cmd, (char *)data, request_len, 0, ssc_free, | |
1020 True, (emul_handle_t)data) == False) { | |
1021 trans_send_complete(cmd, STATUS_BUSY); | |
1022 } | |
1023 } | |
1024 | |
1025 /*ARGSUSED*/ | |
1026 static void | |
1027 ssc_write_fm(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) | |
1028 { | |
1029 int marks_requested; | |
1030 off_t next_size; | |
1031 ssc_params_t *s = (ssc_params_t *)T10_PARAMS_AREA(cmd); | |
1032 ssc_obj_mark_t mark_fm; | |
1033 | |
1034 if (s == NULL) | |
1035 return; | |
1036 | |
1037 if ((cdb[1] & 0xfc) || SAM_CONTROL_BYTE_RESERVED(cdb[5])) { | |
1038 spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); | |
1039 spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); | |
1040 trans_send_complete(cmd, STATUS_CHECK); | |
1041 return; | |
1042 } | |
1043 | |
1044 marks_requested = (cdb[2] << 16) | (cdb[3] << 8) | cdb[4]; | |
1045 while (marks_requested--) { | |
1046 /* | |
1047 * Get the last file-mark and update it's size. | |
1048 */ | |
1049 bcopy((char *)cmd->c_lu->l_common->l_mmap + s->s_cur_fm, | |
1050 &mark_fm, sizeof (mark_fm)); | |
1051 if (mark_fm.som_sig != SSC_OBJ_SIG) { | |
1052 spc_sense_create(cmd, KEY_HARDWARE_ERROR, 0); | |
1053 trans_send_complete(cmd, STATUS_CHECK); | |
1054 return; | |
1055 } | |
1056 next_size = mark_fm.o_fm.size - s->s_cur_rec; | |
1057 mark_fm.o_fm.size = s->s_cur_rec; | |
1058 bcopy(&mark_fm, (char *)cmd->c_lu->l_common->l_mmap + | |
1059 s->s_cur_fm, sizeof (mark_fm)); | |
1060 | |
1061 /* | |
1062 * Write new mark and update internal location of mark. | |
1063 */ | |
1064 mark_fm.o_fm.last_obj_id = | |
1065 find_last_obj_id((char *)cmd->c_lu->l_common->l_mmap + | |
1066 s->s_cur_fm, s->s_cur_rec); | |
1067 mark_fm.o_fm.bom = False; | |
1068 mark_fm.o_fm.size = next_size; | |
1069 s->s_cur_fm += s->s_cur_rec; | |
1070 s->s_cur_rec = sizeof (ssc_obj_mark_t); | |
1071 s->s_prev_rec = s->s_cur_rec; | |
1072 bcopy(&mark_fm, (char *)cmd->c_lu->l_common->l_mmap + | |
1073 s->s_cur_fm, sizeof (mark_fm)); | |
1074 mark_fm.o_fm.size = 0; | |
1075 bcopy(&mark_fm, (char *)cmd->c_lu->l_common->l_mmap + | |
1076 s->s_cur_fm + s->s_cur_rec, sizeof (mark_fm)); | |
1077 } | |
1078 trans_send_complete(cmd, STATUS_GOOD); | |
1079 } | |
1080 | |
1081 /*ARGSUSED*/ | |
1082 static void | |
1083 ssc_locate(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) | |
1084 { | |
1085 } | |
1086 | |
1087 /*ARGSUSED*/ | |
1088 static void | |
1089 ssc_erase(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) | |
1090 { | |
1091 ssc_obj_mark_t mark; | |
1092 ssc_params_t *s = (ssc_params_t *)T10_PARAMS_AREA(cmd); | |
1093 | |
1094 if (s == NULL) | |
1095 return; | |
1096 | |
1097 bzero(&mark, sizeof (mark)); | |
1098 mark.som_sig = SSC_OBJ_SIG; | |
1099 mark.som_type = SSC_OBJ_TYPE_FM; | |
1100 mark.o_fm.bom = True; | |
1101 mark.o_fm.eom = False; | |
1102 mark.o_fm.size = s->s_size - sizeof (mark); | |
1103 | |
1104 bcopy(&mark, (char *)cmd->c_lu->l_common->l_mmap, sizeof (mark)); | |
1105 } | |
1106 | |
1107 /*ARGSUSED*/ | |
1108 static void | |
1109 ssc_load(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) | |
1110 { | |
1111 ssc_params_t *s = T10_PARAMS_AREA(cmd); | |
1112 t10_lu_common_t *lu = cmd->c_lu->l_common; | |
1113 ssc_obj_mark_t mark; | |
1114 | |
1115 /* | |
1116 * SSC-3, revision 02, section 7.2 LOAD/UNLOAD command | |
1117 * Check for various reserve bits. | |
1118 */ | |
1119 if ((cdb[1] & ~SSC_LOAD_CMD_IMMED) || cdb[2] || cdb[3] || | |
1120 (cdb[4] & ~(SSC_LOAD_CMD_LOAD | SSC_LOAD_CMD_RETEN | | |
1121 SSC_LOAD_CMD_EOT | SSC_LOAD_CMD_HOLD)) || | |
1122 SAM_CONTROL_BYTE_RESERVED(cdb[5])) { | |
1123 spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); | |
1124 spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0); | |
1125 trans_send_complete(cmd, STATUS_CHECK); | |
1126 return; | |
1127 } | |
1128 | |
1129 queue_prt(mgmtq, Q_STE_NONIO, "SSC%x LUN%d load bits 0x%x", | |
1130 cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, cdb[4]); | |
1131 | |
1132 /* | |
1133 * There are four possible actions based on the LOAD and HOLD | |
1134 * bits. | |
1135 */ | |
1136 switch (cdb[4] & (SSC_LOAD_CMD_LOAD|SSC_LOAD_CMD_HOLD)) { | |
1137 case SSC_LOAD_CMD_LOAD|SSC_LOAD_CMD_HOLD: | |
1138 /* | |
1139 * Load the media into the system if not already done | |
1140 * so, but do not position tape. The EOT and RETEN should | |
1141 * be zero. Since this emulation currently is always available | |
1142 * we're good to go. | |
1143 */ | |
1144 break; | |
1145 | |
1146 case SSC_LOAD_CMD_LOAD: | |
1147 /* | |
1148 * Without the HOLD bit the tape should be positioned at | |
1149 * the beginning of partition 0. | |
1150 */ | |
1151 s->s_cur_fm = 0; | |
1152 s->s_cur_rec = sizeof (ssc_obj_mark_t); | |
1153 break; | |
1154 | |
1155 case SSC_LOAD_CMD_HOLD: | |
1156 /* | |
1157 * Without the LOAD bit we leave the tape online, but look | |
1158 * at the RETEN and EOT bits. The RETEN doesn't mean anything | |
1159 * for this virtual tape, but we can reposition to EOT. | |
1160 */ | |
1161 if (cdb[4] & SSC_LOAD_CMD_EOT) { | |
1162 /*CONSTANTCONDITION*/ | |
1163 while (1) { | |
1164 bcopy((char *)lu->l_mmap + s->s_cur_fm, &mark, | |
1165 sizeof (mark)); | |
1166 if (mark.som_sig != SSC_OBJ_SIG) { | |
1167 queue_prt(mgmtq, Q_STE_ERRS, | |
1168 "SSC%x LUN%d, bad sig mark: " | |
1169 "expected=0x%x, got=0x%x", | |
1170 cmd->c_lu->l_targ->s_targ_num, | |
1171 cmd->c_lu->l_common->l_num, | |
1172 SSC_OBJ_SIG, mark.som_sig); | |
1173 spc_sense_create(cmd, | |
1174 KEY_MEDIUM_ERROR, 0); | |
1175 trans_send_complete(cmd, STATUS_CHECK); | |
1176 return; | |
1177 } | |
1178 if (mark.som_type != SSC_OBJ_TYPE_FM) { | |
1179 queue_prt(mgmtq, Q_STE_ERRS, | |
1180 "SSC%x LUN%d, bad mark type: " | |
1181 "expected=0x%x, got=0x%x", | |
1182 cmd->c_lu->l_targ->s_targ_num, | |
1183 cmd->c_lu->l_common->l_num, | |
1184 SSC_OBJ_TYPE_FM, mark.som_type); | |
1185 spc_sense_create(cmd, | |
1186 KEY_MEDIUM_ERROR, 0); | |
1187 trans_send_complete(cmd, STATUS_CHECK); | |
1188 return; | |
1189 } | |
1190 if (mark.o_fm.eom == True) | |
1191 break; | |
1192 s->s_cur_fm += mark.o_fm.size; | |
1193 } | |
1194 } | |
1195 break; | |
1196 | |
1197 case 0: | |
1198 /* | |
1199 * Unload the current tape. | |
1200 */ | |
1201 | |
1202 break; | |
1203 } | |
1204 trans_send_complete(cmd, STATUS_GOOD); | |
1205 } | |
1206 | |
1207 /*ARGSUSED*/ | |
1208 static void | |
1209 ssc_logsense(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) | |
1210 { | |
1211 spc_log_supported_pages_t p; | |
1212 void *v; | |
1213 | |
1214 /* | |
1215 * Reserve bit checks | |
1216 */ | |
1217 if ((cdb[1] & ~(SSC_LOG_SP|SSC_LOG_PPC)) || cdb[3] || cdb[4] || | |
1218 SAM_CONTROL_BYTE_RESERVED(cdb[9])) { | |
1219 spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); | |
1220 spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0); | |
1221 trans_send_complete(cmd, STATUS_CHECK); | |
1222 return; | |
1223 } | |
1224 | |
1225 queue_prt(mgmtq, Q_STE_ERRS, "SSC%x LUN%d page code 0x%x", | |
1226 cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, cdb[2]); | |
1227 | |
1228 switch (cdb[2] & SPC_LOG_PAGE_MASK) { | |
1229 case 0: | |
1230 if ((v = malloc(sizeof (p))) == NULL) { | |
1231 trans_send_complete(cmd, STATUS_BUSY); | |
1232 return; | |
1233 } | |
1234 bzero(&p, sizeof (p)); | |
1235 p.length[0] = 1; | |
1236 bcopy(&p, v, sizeof (p)); | |
1237 if (trans_send_datain(cmd, (char *)v, sizeof (p), 0, free, | |
1238 True, (emul_handle_t)v) == False) { | |
1239 trans_send_complete(cmd, STATUS_BUSY); | |
1240 } | |
1241 break; | |
1242 | |
1243 default: | |
1244 spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); | |
1245 spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0); | |
1246 trans_send_complete(cmd, STATUS_CHECK); | |
1247 break; | |
1248 } | |
1249 } | |
1250 | |
1251 /* | |
1252 * []------------------------------------------------------------------[] | |
1253 * | Support functions for the SSC command set | | |
1254 * []------------------------------------------------------------------[] | |
1255 */ | |
1256 static uint32_t | |
1257 find_last_obj_id(char *file_mark, off_t eod) | |
1258 { | |
1259 ssc_obj_mark_t rm; | |
1260 off_t offset = sizeof (ssc_obj_mark_t); | |
1261 uint32_t obj_id = 0xffffffff; | |
1262 | |
1263 bcopy(file_mark + offset, &rm, sizeof (rm)); | |
1264 while (rm.som_type == SSC_OBJ_TYPE_RM) { | |
1265 obj_id = rm.o_rm.obj_id; | |
1266 offset += rm.o_rm.size; | |
1267 if (offset >= eod) | |
1268 break; | |
1269 bcopy(file_mark + offset, &rm, sizeof (rm)); | |
1270 } | |
1271 return (obj_id); | |
1272 } | |
1273 | |
1274 static void | |
1275 ssc_setup_tape(ssc_params_t *s, t10_lu_common_t *lu) | |
1276 { | |
1277 ssc_obj_mark_t mark; | |
1278 | |
1279 /* | |
1280 * Add Begin-of-Partition marker | |
1281 */ | |
1282 bzero(&mark, sizeof (mark)); | |
1283 mark.som_sig = SSC_OBJ_SIG; | |
1284 mark.som_type = SSC_OBJ_TYPE_FM; | |
1285 mark.o_fm.bom = True; | |
1286 mark.o_fm.eom = False; | |
1287 mark.o_fm.size = s->s_size - sizeof (mark); | |
1288 bcopy(&mark, lu->l_mmap, sizeof (mark)); | |
1289 | |
1290 /* | |
1291 * Add first file-record with a zero size. | |
1292 */ | |
1293 bzero(&mark, sizeof (mark)); | |
1294 mark.som_sig = SSC_OBJ_SIG; | |
1295 mark.som_type = SSC_OBJ_TYPE_RM; | |
1296 mark.o_rm.size = 0; | |
1297 mark.o_rm.obj_id = 0xffffffff; | |
1298 bcopy(&mark, (char *)lu->l_mmap + sizeof (ssc_obj_mark_t), | |
1299 sizeof (mark)); | |
1300 | |
1301 /* | |
1302 * Add End-of-Partiton marker | |
1303 */ | |
1304 bzero(&mark, sizeof (mark)); | |
1305 mark.som_sig = SSC_OBJ_SIG; | |
1306 mark.som_type = SSC_OBJ_TYPE_FM; | |
1307 mark.o_fm.bom = False; | |
1308 mark.o_fm.eom = True; | |
1309 mark.o_fm.size = 0; | |
1310 bcopy(&mark, (char *)lu->l_mmap + s->s_size - sizeof (mark), | |
1311 sizeof (mark)); | |
1312 } | |
1313 | |
1314 static char * | |
1315 sense_compression(ssc_params_t *s, char *data) | |
1316 { | |
1317 ssc_data_compression_t d; | |
1318 | |
1319 bzero(&d, sizeof (d)); | |
1320 d.mode_page.code = MODE_SENSE_COMPRESSION; | |
1321 d.mode_page.length = sizeof (d) - sizeof (struct mode_page); | |
1322 bcopy(&d, data, sizeof (d)); | |
1323 | |
1324 return (data + sizeof (d)); | |
1325 } | |
1326 | |
1327 static char * | |
1328 sense_dev_config(ssc_params_t *s, char *data) | |
1329 { | |
1330 ssc_device_config_t d; | |
1331 | |
1332 bzero(&d, sizeof (d)); | |
1333 d.mode_page.code = MODE_SENSE_DEV_CONFIG; | |
1334 d.mode_page.length = sizeof (d) - sizeof (struct mode_page); | |
1335 d.lois = 1; | |
1336 d.socf = 1; | |
1337 d.rewind_on_reset = 1; | |
1338 bcopy(&d, data, sizeof (d)); | |
1339 | |
1340 return (data + sizeof (d)); | |
1341 } | |
1342 | |
1343 static void | |
1344 ssc_free(emul_handle_t e) | |
1345 { | |
1346 free(e); | |
1347 } | |
1348 | |
1349 /* | |
1350 * []---- | |
1351 * | Command table for SSC emulation. This is at the end of the file because | |
1352 * | it's big and ugly. ;-) To make for fast translation to the appropriate | |
1353 * | emulation routine we just have a big command table with all 256 possible | |
1354 * | entries. Most will report STATUS_CHECK, unsupport operation. By doing | |
1355 * | this we can avoid error checking for command range. | |
1356 * []---- | |
1357 */ | |
1358 static scsi_cmd_table_t ssc_table[] = { | |
1359 /* 0x00 -- 0x0f */ | |
1360 { spc_tur, NULL, NULL, "TEST_UNIT_READY" }, | |
1361 { ssc_rewind, NULL, NULL, "REWIND"}, | |
1362 { spc_unsupported, NULL, NULL, NULL }, | |
1363 { spc_request_sense, NULL, NULL, "REQUEST_SENSE" }, | |
1364 { spc_unsupported, NULL, NULL, NULL }, | |
1365 { ssc_read_limits, NULL, NULL, "READ BLOCK LIMITS"}, | |
1366 { spc_unsupported, NULL, NULL, NULL }, | |
1367 { spc_unsupported, NULL, NULL, NULL }, | |
1368 { ssc_read, NULL, ssc_read_cmplt, "READ(6)" }, | |
1369 { spc_unsupported, NULL, NULL, NULL }, | |
1370 { ssc_write, ssc_write_data, ssc_write_cmplt, "WRITE(6)" }, | |
1371 { spc_unsupported, NULL, NULL, NULL }, | |
1372 { spc_unsupported, NULL, NULL, NULL }, | |
1373 { spc_unsupported, NULL, NULL, NULL }, | |
1374 { spc_unsupported, NULL, NULL, NULL }, | |
1375 { spc_unsupported, NULL, NULL, NULL }, | |
1376 | |
1377 /* 0x10 -- 0x1f */ | |
1378 { ssc_write_fm, NULL, NULL, "WRITE_FILEMARKS(6)"}, | |
1379 { ssc_space, NULL, NULL, "SPACE(6)"}, | |
1380 { spc_inquiry, NULL, NULL, "INQUIRY" }, | |
1381 { spc_unsupported, NULL, NULL, NULL }, | |
1382 { spc_unsupported, NULL, NULL, NULL }, | |
1383 { spc_mselect, spc_mselect_data, NULL, "MODE_SELECT(6)" }, | |
1384 { spc_request_sense, NULL, NULL, "RESERVE" }, | |
1385 { spc_request_sense, NULL, NULL, "RELEASE" }, | |
1386 { spc_unsupported, NULL, NULL, NULL }, | |
1387 { ssc_erase, NULL, NULL, "ERASE(6)"}, | |
1388 { ssc_msense, NULL, NULL, "MODE_SENSE(6)" }, | |
1389 { ssc_load, NULL, NULL, "LOAD_UNLOAD" }, | |
1390 { spc_unsupported, NULL, NULL, NULL }, | |
1391 { spc_send_diag, NULL, NULL, "SEND_DIAG" }, | |
1392 { spc_unsupported, NULL, NULL, NULL }, | |
1393 { spc_unsupported, NULL, NULL, NULL }, | |
1394 | |
1395 /* 0x20 -- 0x2f */ | |
1396 { spc_unsupported, NULL, NULL, NULL }, | |
1397 { spc_unsupported, NULL, NULL, NULL }, | |
1398 { spc_unsupported, NULL, NULL, NULL }, | |
1399 { spc_unsupported, NULL, NULL, NULL }, | |
1400 { spc_unsupported, NULL, NULL, NULL }, | |
1401 { spc_unsupported, NULL, NULL, "READ_CAPACITY" }, | |
1402 { spc_unsupported, NULL, NULL, NULL }, | |
1403 { spc_unsupported, NULL, NULL, NULL }, | |
1404 { ssc_read, NULL, ssc_read_cmplt, "READ_G1" }, | |
1405 { spc_unsupported, NULL, NULL, NULL }, | |
1406 { ssc_write, ssc_write_data, ssc_write_cmplt, "WRITE_G1" }, | |
1407 { ssc_locate, NULL, NULL, "LOCATE(10)"}, | |
1408 { spc_unsupported, NULL, NULL, NULL }, | |
1409 { spc_unsupported, NULL, NULL, NULL }, | |
1410 { spc_unsupported, NULL, NULL, NULL }, | |
1411 { spc_unsupported, NULL, NULL, NULL }, | |
1412 | |
1413 /* 0x30 -- 0x3f */ | |
1414 { spc_unsupported, NULL, NULL, NULL }, | |
1415 { spc_unsupported, NULL, NULL, NULL }, | |
1416 { spc_unsupported, NULL, NULL, NULL }, | |
1417 { spc_unsupported, NULL, NULL, NULL }, | |
1418 { ssc_read_pos, NULL, NULL, "READ POSITION"}, | |
1419 { spc_unsupported, NULL, NULL, "SYNC_CACHE" }, | |
1420 { spc_unsupported, NULL, NULL, NULL }, | |
1421 { spc_unsupported, NULL, NULL, NULL }, | |
1422 { spc_unsupported, NULL, NULL, NULL }, | |
1423 { spc_unsupported, NULL, NULL, NULL }, | |
1424 { spc_unsupported, NULL, NULL, NULL }, | |
1425 { spc_unsupported, NULL, NULL, NULL }, | |
1426 { spc_unsupported, NULL, NULL, NULL }, | |
1427 { spc_unsupported, NULL, NULL, NULL }, | |
1428 { spc_unsupported, NULL, NULL, NULL }, | |
1429 { spc_unsupported, NULL, NULL, NULL }, | |
1430 | |
1431 /* 0x40 -- 0x4f */ | |
1432 { spc_unsupported, NULL, NULL, NULL }, | |
1433 { spc_unsupported, NULL, NULL, NULL }, | |
1434 { spc_unsupported, NULL, NULL, NULL }, | |
1435 { spc_unsupported, NULL, NULL, NULL }, | |
1436 { ssc_rpt_density, NULL, NULL, "REPORT DENSITY SUPPORT"}, | |
1437 { spc_unsupported, NULL, NULL, NULL }, | |
1438 { spc_unsupported, NULL, NULL, NULL }, | |
1439 { spc_unsupported, NULL, NULL, NULL }, | |
1440 { spc_unsupported, NULL, NULL, NULL }, | |
1441 { spc_unsupported, NULL, NULL, NULL }, | |
1442 { spc_unsupported, NULL, NULL, NULL }, | |
1443 { spc_unsupported, NULL, NULL, NULL }, | |
1444 { spc_unsupported, NULL, NULL, NULL }, | |
1445 { ssc_logsense, NULL, NULL, "LOG SENSE" }, | |
1446 { spc_unsupported, NULL, NULL, NULL }, | |
1447 { spc_unsupported, NULL, NULL, NULL }, | |
1448 | |
1449 /* 0x50 -- 0x5f */ | |
1450 { spc_unsupported, NULL, NULL, NULL }, | |
1451 { spc_unsupported, NULL, NULL, NULL }, | |
1452 { spc_unsupported, NULL, NULL, NULL }, | |
1453 { spc_unsupported, NULL, NULL, NULL }, | |
1454 { spc_unsupported, NULL, NULL, NULL }, | |
1455 { spc_unsupported, NULL, NULL, NULL }, | |
1456 { spc_unsupported, NULL, NULL, NULL }, | |
1457 { spc_unsupported, NULL, NULL, NULL }, | |
1458 { spc_unsupported, NULL, NULL, NULL }, | |
1459 { spc_unsupported, NULL, NULL, NULL }, | |
1460 { spc_unsupported, NULL, NULL, NULL }, | |
1461 { spc_unsupported, NULL, NULL, NULL }, | |
1462 { spc_unsupported, NULL, NULL, NULL }, | |
1463 { spc_unsupported, NULL, NULL, NULL }, | |
1464 { spc_unsupported, NULL, NULL, NULL }, | |
1465 { spc_unsupported, NULL, NULL, NULL }, | |
1466 | |
1467 /* 0x60 -- 0x6f */ | |
1468 { spc_unsupported, NULL, NULL, NULL }, | |
1469 { spc_unsupported, NULL, NULL, NULL }, | |
1470 { spc_unsupported, NULL, NULL, NULL }, | |
1471 { spc_unsupported, NULL, NULL, NULL }, | |
1472 { spc_unsupported, NULL, NULL, NULL }, | |
1473 { spc_unsupported, NULL, NULL, NULL }, | |
1474 { spc_unsupported, NULL, NULL, NULL }, | |
1475 { spc_unsupported, NULL, NULL, NULL }, | |
1476 { spc_unsupported, NULL, NULL, NULL }, | |
1477 { spc_unsupported, NULL, NULL, NULL }, | |
1478 { spc_unsupported, NULL, NULL, NULL }, | |
1479 { spc_unsupported, NULL, NULL, NULL }, | |
1480 { spc_unsupported, NULL, NULL, NULL }, | |
1481 { spc_unsupported, NULL, NULL, NULL }, | |
1482 { spc_unsupported, NULL, NULL, NULL }, | |
1483 { spc_unsupported, NULL, NULL, NULL }, | |
1484 | |
1485 /* 0x70 -- 0x7f */ | |
1486 { spc_unsupported, NULL, NULL, NULL }, | |
1487 { spc_unsupported, NULL, NULL, NULL }, | |
1488 { spc_unsupported, NULL, NULL, NULL }, | |
1489 { spc_unsupported, NULL, NULL, NULL }, | |
1490 { spc_unsupported, NULL, NULL, NULL }, | |
1491 { spc_unsupported, NULL, NULL, NULL }, | |
1492 { spc_unsupported, NULL, NULL, NULL }, | |
1493 { spc_unsupported, NULL, NULL, NULL }, | |
1494 { spc_unsupported, NULL, NULL, NULL }, | |
1495 { spc_unsupported, NULL, NULL, NULL }, | |
1496 { spc_unsupported, NULL, NULL, NULL }, | |
1497 { spc_unsupported, NULL, NULL, NULL }, | |
1498 { spc_unsupported, NULL, NULL, NULL }, | |
1499 { spc_unsupported, NULL, NULL, NULL }, | |
1500 { spc_unsupported, NULL, NULL, NULL }, | |
1501 { spc_unsupported, NULL, NULL, NULL }, | |
1502 | |
1503 /* 0x80 -- 0x8f */ | |
1504 { ssc_write_fm, NULL, NULL, "WRITE_FILEMARKS(16)"}, | |
1505 { spc_unsupported, NULL, NULL, NULL }, | |
1506 { spc_unsupported, NULL, NULL, NULL }, | |
1507 { spc_unsupported, NULL, NULL, NULL }, | |
1508 { spc_unsupported, NULL, NULL, NULL }, | |
1509 { spc_unsupported, NULL, NULL, NULL }, | |
1510 { spc_unsupported, NULL, NULL, NULL }, | |
1511 { spc_unsupported, NULL, NULL, NULL }, | |
1512 { ssc_read, NULL, ssc_read_cmplt, "READ_G4" }, | |
1513 { spc_unsupported, NULL, NULL, NULL }, | |
1514 { ssc_write, ssc_write_data, ssc_write_cmplt, "WRITE_G4" }, | |
1515 { spc_unsupported, NULL, NULL, NULL }, | |
1516 { spc_unsupported, NULL, NULL, NULL }, | |
1517 { spc_unsupported, NULL, NULL, NULL }, | |
1518 { spc_unsupported, NULL, NULL, NULL }, | |
1519 { spc_unsupported, NULL, NULL, NULL }, | |
1520 | |
1521 /* 0x90 -- 0x9f */ | |
1522 { spc_unsupported, NULL, NULL, NULL }, | |
1523 { spc_unsupported, NULL, NULL, NULL }, | |
1524 { ssc_locate, NULL, NULL, "LOCATE(16)"}, | |
1525 { ssc_erase, NULL, NULL, "ERASE" }, | |
1526 { spc_unsupported, NULL, NULL, NULL }, | |
1527 { spc_unsupported, NULL, NULL, NULL }, | |
1528 { spc_unsupported, NULL, NULL, NULL }, | |
1529 { spc_unsupported, NULL, NULL, NULL }, | |
1530 { spc_unsupported, NULL, NULL, NULL }, | |
1531 { spc_unsupported, NULL, NULL, NULL }, | |
1532 { spc_unsupported, NULL, NULL, NULL }, | |
1533 { spc_unsupported, NULL, NULL, NULL }, | |
1534 { spc_unsupported, NULL, NULL, NULL }, | |
1535 { spc_unsupported, NULL, NULL, NULL }, | |
1536 { spc_unsupported, NULL, NULL, "SVC_ACTION_G4" }, | |
1537 { spc_unsupported, NULL, NULL, NULL }, | |
1538 | |
1539 /* 0xa0 - 0xaf */ | |
1540 { spc_report_luns, NULL, NULL, "REPORT_LUNS" }, | |
1541 { spc_unsupported, NULL, NULL, NULL }, | |
1542 { spc_unsupported, NULL, NULL, NULL }, | |
1543 { spc_report_tpgs, NULL, NULL, "REPORT_TPGS" }, | |
1544 { spc_unsupported, NULL, NULL, NULL }, | |
1545 { spc_unsupported, NULL, NULL, NULL }, | |
1546 { spc_unsupported, NULL, NULL, NULL }, | |
1547 { spc_unsupported, NULL, NULL, NULL }, | |
1548 { spc_unsupported, NULL, NULL, NULL }, | |
1549 { spc_unsupported, NULL, NULL, NULL }, | |
1550 { spc_unsupported, NULL, NULL, NULL }, | |
1551 { spc_unsupported, NULL, NULL, NULL }, | |
1552 { spc_unsupported, NULL, NULL, NULL }, | |
1553 { spc_unsupported, NULL, NULL, NULL }, | |
1554 { spc_unsupported, NULL, NULL, NULL }, | |
1555 { spc_unsupported, NULL, NULL, NULL }, | |
1556 | |
1557 /* 0xb0 -- 0xbf */ | |
1558 { spc_unsupported, NULL, NULL, NULL }, | |
1559 { spc_unsupported, NULL, NULL, NULL }, | |
1560 { spc_unsupported, NULL, NULL, NULL }, | |
1561 { spc_unsupported, NULL, NULL, NULL }, | |
1562 { spc_unsupported, NULL, NULL, NULL }, | |
1563 { spc_unsupported, NULL, NULL, NULL }, | |
1564 { spc_unsupported, NULL, NULL, NULL }, | |
1565 { spc_unsupported, NULL, NULL, NULL }, | |
1566 { spc_unsupported, NULL, NULL, NULL }, | |
1567 { spc_unsupported, NULL, NULL, NULL }, | |
1568 { spc_unsupported, NULL, NULL, NULL }, | |
1569 { spc_unsupported, NULL, NULL, NULL }, | |
1570 { spc_unsupported, NULL, NULL, NULL }, | |
1571 { spc_unsupported, NULL, NULL, NULL }, | |
1572 { spc_unsupported, NULL, NULL, NULL }, | |
1573 { spc_unsupported, NULL, NULL, NULL }, | |
1574 | |
1575 /* 0xc0 -- 0xcf */ | |
1576 { spc_unsupported, NULL, NULL, NULL }, | |
1577 { spc_unsupported, NULL, NULL, NULL }, | |
1578 { spc_unsupported, NULL, NULL, NULL }, | |
1579 { spc_unsupported, NULL, NULL, NULL }, | |
1580 { spc_unsupported, NULL, NULL, NULL }, | |
1581 { spc_unsupported, NULL, NULL, NULL }, | |
1582 { spc_unsupported, NULL, NULL, NULL }, | |
1583 { spc_unsupported, NULL, NULL, NULL }, | |
1584 { spc_unsupported, NULL, NULL, NULL }, | |
1585 { spc_unsupported, NULL, NULL, NULL }, | |
1586 { spc_unsupported, NULL, NULL, NULL }, | |
1587 { spc_unsupported, NULL, NULL, NULL }, | |
1588 { spc_unsupported, NULL, NULL, NULL }, | |
1589 { spc_unsupported, NULL, NULL, NULL }, | |
1590 { spc_unsupported, NULL, NULL, NULL }, | |
1591 { spc_unsupported, NULL, NULL, NULL }, | |
1592 | |
1593 /* 0xd0 -- 0xdf */ | |
1594 { spc_unsupported, NULL, NULL, NULL }, | |
1595 { spc_unsupported, NULL, NULL, NULL }, | |
1596 { spc_unsupported, NULL, NULL, NULL }, | |
1597 { spc_unsupported, NULL, NULL, NULL }, | |
1598 { spc_unsupported, NULL, NULL, NULL }, | |
1599 { spc_unsupported, NULL, NULL, NULL }, | |
1600 { spc_unsupported, NULL, NULL, NULL }, | |
1601 { spc_unsupported, NULL, NULL, NULL }, | |
1602 { spc_unsupported, NULL, NULL, NULL }, | |
1603 { spc_unsupported, NULL, NULL, NULL }, | |
1604 { spc_unsupported, NULL, NULL, NULL }, | |
1605 { spc_unsupported, NULL, NULL, NULL }, | |
1606 { spc_unsupported, NULL, NULL, NULL }, | |
1607 { spc_unsupported, NULL, NULL, NULL }, | |
1608 { spc_unsupported, NULL, NULL, NULL }, | |
1609 { spc_unsupported, NULL, NULL, NULL }, | |
1610 | |
1611 /* 0xe0 -- 0xef */ | |
1612 { spc_unsupported, NULL, NULL, NULL }, | |
1613 { spc_unsupported, NULL, NULL, NULL }, | |
1614 { spc_unsupported, NULL, NULL, NULL }, | |
1615 { spc_unsupported, NULL, NULL, NULL }, | |
1616 { spc_unsupported, NULL, NULL, NULL }, | |
1617 { spc_unsupported, NULL, NULL, NULL }, | |
1618 { spc_unsupported, NULL, NULL, NULL }, | |
1619 { spc_unsupported, NULL, NULL, NULL }, | |
1620 { spc_unsupported, NULL, NULL, NULL }, | |
1621 { spc_unsupported, NULL, NULL, NULL }, | |
1622 { spc_unsupported, NULL, NULL, NULL }, | |
1623 { spc_unsupported, NULL, NULL, NULL }, | |
1624 { spc_unsupported, NULL, NULL, NULL }, | |
1625 { spc_unsupported, NULL, NULL, NULL }, | |
1626 { spc_unsupported, NULL, NULL, NULL }, | |
1627 { spc_unsupported, NULL, NULL, NULL }, | |
1628 | |
1629 /* 0xf0 -- 0xff */ | |
1630 { spc_unsupported, NULL, NULL, NULL }, | |
1631 { spc_unsupported, NULL, NULL, NULL }, | |
1632 { spc_unsupported, NULL, NULL, NULL }, | |
1633 { spc_unsupported, NULL, NULL, NULL }, | |
1634 { spc_unsupported, NULL, NULL, NULL }, | |
1635 { spc_unsupported, NULL, NULL, NULL }, | |
1636 { spc_unsupported, NULL, NULL, NULL }, | |
1637 { spc_unsupported, NULL, NULL, NULL }, | |
1638 { spc_unsupported, NULL, NULL, NULL }, | |
1639 { spc_unsupported, NULL, NULL, NULL }, | |
1640 { spc_unsupported, NULL, NULL, NULL }, | |
1641 { spc_unsupported, NULL, NULL, NULL }, | |
1642 { spc_unsupported, NULL, NULL, NULL }, | |
1643 { spc_unsupported, NULL, NULL, NULL }, | |
1644 { spc_unsupported, NULL, NULL, NULL }, | |
1645 { spc_unsupported, NULL, NULL, NULL }, | |
1646 }; |