|  | /* | 
|  | * Copyright (c) 2007 Vladimir Nadvornik <nadvornik@suse.cz> | 
|  | * Copyright (c) 2007-2017 Dmitry V. Levin <ldv@altlinux.org> | 
|  | * All rights reserved. | 
|  | * | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions | 
|  | * are met: | 
|  | * 1. Redistributions of source code must retain the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer. | 
|  | * 2. Redistributions in binary form must reproduce the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer in the | 
|  | *    documentation and/or other materials provided with the distribution. | 
|  | * 3. The name of the author may not be used to endorse or promote products | 
|  | *    derived from this software without specific prior written permission. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | 
|  | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | 
|  | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | 
|  | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | 
|  | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | 
|  | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
|  | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
|  | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 
|  | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | */ | 
|  |  | 
|  | #include "defs.h" | 
|  |  | 
|  | #ifdef HAVE_SCSI_SG_H | 
|  | # include <scsi/sg.h> | 
|  | #endif | 
|  |  | 
|  | #include "xlat/scsi_sg_commands.h" | 
|  | #include "xlat/sg_scsi_reset.h" | 
|  |  | 
|  | static int | 
|  | decode_sg_io(struct tcb *const tcp, const uint32_t iid, | 
|  | const kernel_ulong_t arg) | 
|  | { | 
|  | switch (iid) { | 
|  | case 'S': | 
|  | return decode_sg_io_v3(tcp, arg); | 
|  | case 'Q': | 
|  | return decode_sg_io_v4(tcp, arg); | 
|  | default: | 
|  | tprintf("[%u]", iid); | 
|  | return RVAL_IOCTL_DECODED; | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | #ifdef HAVE_SCSI_SG_H | 
|  |  | 
|  | static int | 
|  | decode_sg_scsi_id(struct tcb *const tcp, const kernel_ulong_t arg) | 
|  | { | 
|  | struct sg_scsi_id id; | 
|  |  | 
|  | if (entering(tcp)) | 
|  | return 0; | 
|  |  | 
|  | tprints(", "); | 
|  | if (!umove_or_printaddr(tcp, arg, &id)) { | 
|  | tprintf("{host_no=%d" | 
|  | ", channel=%d" | 
|  | ", scsi_id=%#x" | 
|  | ", lun=%d" | 
|  | ", scsi_type=%#x" | 
|  | ", h_cmd_per_lun=%hd" | 
|  | ", d_queue_depth=%hd}", | 
|  | id.host_no, | 
|  | id.channel, | 
|  | id.scsi_id, | 
|  | id.lun, | 
|  | id.scsi_type, | 
|  | id.h_cmd_per_lun, | 
|  | id.d_queue_depth); | 
|  | } | 
|  | return RVAL_IOCTL_DECODED; | 
|  | } | 
|  |  | 
|  | #endif /* HAVE_SCSI_SG_H */ | 
|  |  | 
|  | int | 
|  | scsi_ioctl(struct tcb *const tcp, const unsigned int code, | 
|  | const kernel_ulong_t arg) | 
|  | { | 
|  | switch (code) { | 
|  | case SG_IO: | 
|  | if (entering(tcp)) { | 
|  | uint32_t iid; | 
|  |  | 
|  | tprints(", "); | 
|  | if (umove_or_printaddr(tcp, arg, &iid)) { | 
|  | break; | 
|  | } else { | 
|  | return decode_sg_io(tcp, iid, arg); | 
|  | } | 
|  | } else { | 
|  | uint32_t *piid = get_tcb_priv_data(tcp); | 
|  | if (piid) | 
|  | decode_sg_io(tcp, *piid, arg); | 
|  | tprints("}"); | 
|  | break; | 
|  | } | 
|  |  | 
|  | #ifdef HAVE_SCSI_SG_H | 
|  | /* returns struct sg_scsi_id */ | 
|  | case SG_GET_SCSI_ID: | 
|  | return decode_sg_scsi_id(tcp, arg); | 
|  | /* returns struct sg_req_info */ | 
|  | case SG_GET_REQUEST_TABLE: | 
|  | return decode_sg_req_info(tcp, arg); | 
|  | #endif /* HAVE_SCSI_SG_H */ | 
|  |  | 
|  | /* takes a value by pointer */ | 
|  | case SG_SCSI_RESET: { | 
|  | unsigned int val; | 
|  | tprints(", "); | 
|  | if (!umove_or_printaddr(tcp, arg, &val)) { | 
|  | tprints("["); | 
|  | if (val & SG_SCSI_RESET_NO_ESCALATE) { | 
|  | printxval(sg_scsi_reset, | 
|  | SG_SCSI_RESET_NO_ESCALATE, 0); | 
|  | tprints("|"); | 
|  | } | 
|  | printxval(sg_scsi_reset, | 
|  | val & ~SG_SCSI_RESET_NO_ESCALATE, | 
|  | "SG_SCSI_RESET_???"); | 
|  | tprints("]"); | 
|  |  | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* takes a signed int by pointer */ | 
|  | case SG_NEXT_CMD_LEN: | 
|  | case SG_SET_COMMAND_Q: | 
|  | case SG_SET_DEBUG: | 
|  | case SG_SET_FORCE_LOW_DMA: | 
|  | case SG_SET_FORCE_PACK_ID: | 
|  | case SG_SET_KEEP_ORPHAN: | 
|  | case SG_SET_RESERVED_SIZE: | 
|  | case SG_SET_TIMEOUT: | 
|  | tprints(", "); | 
|  | printnum_int(tcp, arg, "%d"); | 
|  | break; | 
|  |  | 
|  | /* returns a signed int by pointer */ | 
|  | case SG_EMULATED_HOST: | 
|  | case SG_GET_ACCESS_COUNT: | 
|  | case SG_GET_COMMAND_Q: | 
|  | case SG_GET_KEEP_ORPHAN: | 
|  | case SG_GET_LOW_DMA: | 
|  | case SG_GET_NUM_WAITING: | 
|  | case SG_GET_PACK_ID: | 
|  | case SG_GET_RESERVED_SIZE: | 
|  | case SG_GET_SG_TABLESIZE: | 
|  | case SG_GET_TRANSFORM: | 
|  | case SG_GET_VERSION_NUM: | 
|  | if (entering(tcp)) | 
|  | return 0; | 
|  | tprints(", "); | 
|  | printnum_int(tcp, arg, "%d"); | 
|  | break; | 
|  |  | 
|  | /* takes an integer by value */ | 
|  | case SG_SET_TRANSFORM: | 
|  | tprintf(", %#x", (unsigned int) arg); | 
|  | break; | 
|  |  | 
|  | /* no arguments */ | 
|  | case SG_GET_TIMEOUT: | 
|  | break; | 
|  |  | 
|  | default: | 
|  | return RVAL_DECODED; | 
|  | } | 
|  |  | 
|  | return RVAL_IOCTL_DECODED; | 
|  | } |