t10_osd.c revision f3861e1a2ceec23a5b699c24d814b7775a9e0b52
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* []------------------------------------------------------------------[]
* | Implementation of OSD emulation |
* | |
* | NOTE: At this point in time this file is nothing more than a |
* | place holder for the implementation. As the project evolves we'll |
* | add more and more functionality. |
* []------------------------------------------------------------------[]
*/
#include <sys/types.h>
#include <aio.h>
#include <sys/asynch.h>
#include <sys/mman.h>
#include <stddef.h>
#include <strings.h>
#include <unistd.h>
#include <assert.h>
#include <sys/scsi/generic/sense.h>
#include <sys/scsi/generic/status.h>
#include <sys/scsi/generic/inquiry.h>
#include <sys/scsi/generic/commands.h>
#include <sys/scsi/generic/mode.h>
#include <sys/scsi/generic/dad_mode.h>
#include "t10.h"
#include "t10_spc.h"
#include "t10_osd.h"
#include "utility.h"
/*
* Forward declarations
*/
static void osd_cmd(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len);
static void osd_data(t10_cmd_t *cmd, emul_handle_t e, size_t offset,
char *data, size_t data_len);
static scsi_cmd_table_t osd_table[];
static void osd_list(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len);
/*
* []----
* | osd_init_common -- Initialize LU data which is common to all I_T_Ls
* []----
*/
/*ARGSUSED*/
Boolean_t
osd_common_init(t10_lu_common_t *lu)
{
osd_params_t *o;
char *str;
tgt_node_t *node = lu->l_root;
if ((o = (osd_params_t *)calloc(1, sizeof (*o))) == NULL)
return (False);
if (tgt_find_value_str(node, XML_ELEMENT_SIZE, &str) == True) {
o->o_size = strtoll(str, NULL, 0);
free(str);
} else {
free(o);
return (False);
}
lu->l_dtype_params = (void *)o;
return (True);
}
/*ARGSUSED*/
void
osd_common_fini(t10_lu_common_t *lu)
{
free(lu->l_dtype_params);
}
/*
* []----
* | osd_init_per -- Initialize per I_T_L information
* []----
*/
/*ARGSUSED*/
void
osd_per_init(t10_lu_impl_t *itl)
{
itl->l_cmd = osd_cmd;
itl->l_data = osd_data;
itl->l_cmd_table = osd_table;
/*
* The first time an I_T nexus connects to a LU it is supposed
* to receive an unit attention upon the first command sent.
*/
itl->l_status = KEY_UNIT_ATTENTION;
itl->l_asc = SPC_ASC_PWR_ON;
itl->l_ascq = SPC_ASCQ_PWR_ON;
}
/*ARGSUSED*/
void
osd_per_fini(t10_lu_impl_t *itl)
{
}
/*ARGSUSED*/
void
osd_task_mgmt(t10_lu_common_t *lu, TaskOp_t op)
{
}
/*
* []----
* | osd_cmd -- start a SCSI command
* |
* | This routine is called from within the SAM-3 Task router.
* []----
*/
static void
osd_cmd(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
{
scsi_cmd_table_t *e;
e = &cmd->c_lu->l_cmd_table[cdb[0]];
#ifdef FULL_DEBUG
queue_prt(mgmtq, Q_STE_IO, "SBC%x LUN%d Cmd %s\n",
cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
e->cmd_name == NULL ? "(no name)" : e->cmd_name);
#endif
(*e->cmd_start)(cmd, cdb, cdb_len);
}
/*
* []----
* | osd_data -- Data phase for command.
* |
* | Normally this is only called for the WRITE command. Other commands
* | that have a data in phase will probably be short circuited when
* | we call trans_rqst_dataout() and the data is already available.
* | At least this is true for iSCSI. FC however will need a DataIn phase
* | for commands like MODE SELECT and PGROUT.
* []----
*/
static void
osd_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data,
size_t data_len)
{
scsi_cmd_table_t *e;
e = &cmd->c_lu->l_cmd_table[cmd->c_cdb[0]];
#ifdef FULL_DEBUG
queue_prt(mgmtq, Q_STE_IO, "SBC%x LUN%d Data %s\n",
cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
e->cmd_name);
#endif
(*e->cmd_data)(cmd, id, offset, data, data_len);
}
/*
* []------------------------------------------------------------------[]
* | SCSI Object-Based Storage Device Commands |
* | T10/1355-D |
* | The following functions implement the emulation of OSD type |
* | commands. |
* []------------------------------------------------------------------[]
*/
static void
osd_service_action(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
{
osd_generic_cdb_t *o;
uint16_t service_action;
/*
* debug only -- no need to drop core if someone doesn't play right.
*/
assert(cdb_len == sizeof (*o));
if (cdb_len != sizeof (*o)) {
spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, SPC_ASCQ_INVALID_CDB);
trans_send_complete(cmd, STATUS_CHECK);
return;
}
o = (osd_generic_cdb_t *)cdb;
service_action = o->ocdb_basic.b_service_action[0] << 8 |
o->ocdb_basic.b_service_action[1];
queue_prt(mgmtq, Q_STE_NONIO,
"OSD%x LUN%d service=0x%x, options=0x%x, specific_opts=0x%x,"
" fmt=0x%x", cmd->c_lu->l_targ->s_targ_num,
cmd->c_lu->l_common->l_num, service_action, o->ocdb_options,
o->ocdb_specific_opts, o->ocdb_fmt);
switch (service_action) {
case OSD_APPEND:
case OSD_CREATE:
case OSD_CREATE_AND_WRITE:
case OSD_CREATE_COLLECTION:
case OSD_CREATE_PARTITION:
case OSD_FLUSH:
case OSD_FLUSH_COLLECTION:
case OSD_FLUSH_OSD:
case OSD_FLUSH_PARTITION:
case OSD_FORMAT_OSD:
case OSD_GET_ATTR:
case OSD_LIST:
osd_list(cmd, cdb, cdb_len);
break;
case OSD_LIST_COLLECTION:
case OSD_PERFORM_SCSI:
case OSD_TASK_MGMT:
default:
spc_unsupported(cmd, cdb, cdb_len);
break;
}
}
/*
* []----
* | osd_list -- return a list of objects
* []----
*/
static void
osd_list(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
{
osd_cmd_list_t *o = (osd_cmd_list_t *)cdb;
osd_obj_id_t part;
osd_list_param_t *data;
uint64_t len,
alloc_len;
part = (uint64_t)o->ocdb_partition_id[0] << 56 |
(uint64_t)o->ocdb_partition_id[1] << 48 |
(uint64_t)o->ocdb_partition_id[2] << 40 |
(uint64_t)o->ocdb_partition_id[3] << 32 |
(uint64_t)o->ocdb_partition_id[4] << 24 |
(uint64_t)o->ocdb_partition_id[5] << 16 |
(uint64_t)o->ocdb_partition_id[6] << 8 |
(uint64_t)o->ocdb_partition_id[7];
len = (uint64_t)o->ocdb_length[0] << 56 |
(uint64_t)o->ocdb_length[1] << 48 |
(uint64_t)o->ocdb_length[2] << 40 |
(uint64_t)o->ocdb_length[3] << 32 |
(uint64_t)o->ocdb_length[4] << 24 |
(uint64_t)o->ocdb_length[5] << 16 |
(uint64_t)o->ocdb_length[6] << 8 |
(uint64_t)o->ocdb_length[7];
if (len == 0) {
trans_send_complete(cmd, STATUS_GOOD);
return;
}
queue_prt(mgmtq, Q_STE_NONIO, "part=0x%llx, len=0x%llx", part, len);
alloc_len = MAX(sizeof (*data), len);
if ((data = calloc(1, alloc_len)) == NULL) {
trans_send_complete(cmd, STATUS_BUSY);
return;
}
data->op_length[7] = sizeof (*data) - 8;
if (part == OSD_PARTITION_ROOT)
data->op_root = 1;
trans_send_datain(cmd, (char *)data, sizeof (*data), 0, free, True,
(emul_handle_t)data);
}
/*
* []------------------------------------------------------------------[]
* | Support related functions for OSD |
* []------------------------------------------------------------------[]
*/
/*
* []----
* | Command table for OSD emulation. This is at the end of the file because
* | it's big and ugly. ;-) To make for fast translation to the appropriate
* | emulation routine we just have a big command table with all 256 possible
* | entries. Most will report STATUS_CHECK, unsupport operation. By doing
* | this we can avoid error checking for command range or the use of a switch
* | statement.
* []----
*/
static scsi_cmd_table_t osd_table[] = {
/* 0x00 -- 0x0f */
{ spc_tur, NULL, NULL, "TEST_UNIT_READY" },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_request_sense, NULL, NULL, "REQUEST_SENSE" },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, "READ" },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, "WRITE" },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
/* 0x10 -- 0x1f */
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_inquiry, NULL, NULL, "INQUIRY" },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_mselect, spc_mselect_data, NULL, "MODE_SELECT" },
{ spc_unsupported, NULL, NULL, "RESERVE" },
{ spc_unsupported, NULL, NULL, "RELEASE" },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, "MODE_SENSE" },
{ spc_unsupported, NULL, NULL, "START_STOP" },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_send_diag, NULL, NULL, "SEND_DIAG" },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
/* 0x20 -- 0x2f */
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, "READ_CAPACITY" },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, "READ_G1" },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, "WRITE_G1" },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
/* 0x30 -- 0x3f */
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, "SYNC_CACHE" },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
/* 0x40 -- 0x4f */
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, "LOG_SENSE" },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
/* 0x50 -- 0x5f */
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, "PERSISTENT_IN" },
{ spc_unsupported, NULL, NULL, "PERSISTENT_OUT" },
/* 0x60 -- 0x6f */
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
/* 0x70 -- 0x7f */
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ osd_service_action, NULL, NULL, "SERVICE_ACTION" },
/* 0x80 -- 0x8f */
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, "READ_G4" },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, "WRITE_G4" },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
/* 0x90 -- 0x9f */
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, "SVC_ACTION_G4" },
{ spc_unsupported, NULL, NULL, NULL },
/* 0xa0 - 0xaf */
{ spc_report_luns, NULL, NULL, "REPORT_LUNS" },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_report_tpgs, NULL, NULL, "REPORT_TPGS" },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
/* 0xb0 -- 0xbf */
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
/* 0xc0 -- 0xcf */
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
/* 0xd0 -- 0xdf */
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
/* 0xe0 -- 0xef */
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
/* 0xf0 -- 0xff */
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
{ spc_unsupported, NULL, NULL, NULL },
};