├── .gitignore ├── LICENSE ├── include ├── utils.h ├── usb.h ├── scsi.h ├── gsm.h ├── scsisim.h └── device.h ├── Makefile ├── src ├── scsi.c ├── usb.c ├── utils.c ├── demo.c ├── gsm.c └── sim.c └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.swp 3 | *.[oa] 4 | *.so 5 | demo 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, Chris Coffey 2 | 3 | Permission to use, copy, modify, and/or distribute this software 4 | for any purpose with or without fee is hereby granted, provided 5 | that the above copyright notice and this permission notice appear 6 | in all copies. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 9 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 10 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 11 | AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 12 | DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA 13 | OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 14 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 | PERFORMANCE OF THIS SOFTWARE. 16 | 17 | -------------------------------------------------------------------------------- /include/utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * utils.h 3 | * Definitions for various utility functions in the scsisim library. 4 | * This is an internal interface file for the scsisim library. 5 | * 6 | * Copyright (c) 2017, Chris Coffey 7 | * 8 | * Permission to use, copy, modify, and/or distribute this software 9 | * for any purpose with or without fee is hereby granted, provided 10 | * that the above copyright notice and this permission notice appear 11 | * in all copies. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 14 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 16 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 17 | * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA 18 | * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 19 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 20 | * PERFORMANCE OF THIS SOFTWARE. 21 | */ 22 | 23 | #ifndef __SCSISIM_UTILS_H__ 24 | #define __SCSISIM_UTILS_H__ 25 | 26 | #include 27 | #include 28 | 29 | #define MIN(x,y) (((x) < (y)) ? (x) : (y)) 30 | #define MAX(x,y) (((x) > (y)) ? (x) : (y)) 31 | 32 | void print_binary_buffer(const uint8_t *buf, const unsigned int len); 33 | 34 | bool is_digit_string(const char *str); 35 | 36 | #endif /* __SCSISIM_UTILS_H__ */ 37 | 38 | /* EOF */ 39 | 40 | -------------------------------------------------------------------------------- /include/usb.h: -------------------------------------------------------------------------------- 1 | /* 2 | * usb.h 3 | * USB-related definitions for the scsisim library. 4 | * This is an internal interface file for the scsisim library. 5 | * 6 | * Copyright (c) 2017, Chris Coffey 7 | * 8 | * Permission to use, copy, modify, and/or distribute this software 9 | * for any purpose with or without fee is hereby granted, provided 10 | * that the above copyright notice and this permission notice appear 11 | * in all copies. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 14 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 16 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 17 | * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA 18 | * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 19 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 20 | * PERFORMANCE OF THIS SOFTWARE. 21 | */ 22 | 23 | #ifndef __SCSISIM_USB_H__ 24 | #define __SCSISIM_USB_H__ 25 | 26 | #include 27 | 28 | #define SYSFS_SG_BASE_PATH "/sys/class/scsi_generic" 29 | 30 | int usb_get_vendor_product(const struct scsisim_dev *device, 31 | unsigned int *vendor, 32 | unsigned int *product); 33 | 34 | bool usb_is_device_supported(struct scsisim_dev *device, 35 | unsigned int vendor, 36 | unsigned int product, 37 | const unsigned int supported_devices[][3]); 38 | 39 | #endif /* __SCSISIM_USB_H__ */ 40 | 41 | /* EOF */ 42 | 43 | -------------------------------------------------------------------------------- /include/scsi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * scsi.h 3 | * SCSI-related definitions for the scsisim library. 4 | * This is an internal interface file for the scsisim library. 5 | * 6 | * Copyright (c) 2017, Chris Coffey 7 | * 8 | * Permission to use, copy, modify, and/or distribute this software 9 | * for any purpose with or without fee is hereby granted, provided 10 | * that the above copyright notice and this permission notice appear 11 | * in all copies. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 14 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 16 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 17 | * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA 18 | * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 19 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 20 | * PERFORMANCE OF THIS SOFTWARE. 21 | */ 22 | 23 | #ifndef __SCSISIM_SCSI_H__ 24 | #define __SCSISIM_SCSI_H__ 25 | 26 | #include 27 | 28 | /* This struct contains everything needed to 29 | * send a READ or WRITE command to the device */ 30 | struct scsi_cmd { 31 | /* Input values */ 32 | int direction; 33 | uint8_t cdb_len; 34 | uint8_t *cdb; 35 | unsigned int data_len; 36 | uint8_t *data; 37 | uint8_t sense_len; 38 | uint8_t *sense; 39 | /* Output values */ 40 | unsigned int data_xfered; 41 | uint8_t sense_xfered; 42 | }; 43 | 44 | int scsi_send_cdb(const struct scsisim_dev *device, struct scsi_cmd *my_cmd); 45 | 46 | #endif /* __SCSISIM_SCSI_H__ */ 47 | 48 | /* EOF */ 49 | 50 | -------------------------------------------------------------------------------- /include/gsm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * gsm.h 3 | * GSM-related definitions for the scsisim library. 4 | * This is an internal interface file for the scsisim library. 5 | * 6 | * Copyright (c) 2017, Chris Coffey 7 | * 8 | * Permission to use, copy, modify, and/or distribute this software 9 | * for any purpose with or without fee is hereby granted, provided 10 | * that the above copyright notice and this permission notice appear 11 | * in all copies. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 14 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 16 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 17 | * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA 18 | * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 19 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 20 | * PERFORMANCE OF THIS SOFTWARE. 21 | */ 22 | 23 | #ifndef __SCSISIM_GSM_H__ 24 | #define __SCSISIM_GSM_H__ 25 | 26 | #include 27 | #include 28 | 29 | /* GSM command codes: these are only the commands the API currently supports, not 30 | * every single GSM command code. If you need to use a command code not listed here, 31 | * use scsisim_send_raw_command() to build your own command buffer. 32 | */ 33 | #define GSM_CLASS 0xa0 34 | #define GSM_CMD_SELECT 0xa4 35 | #define GSM_CMD_GET_RESPONSE 0xc0 36 | #define GSM_CMD_READ_BINARY 0xb0 37 | #define GSM_CMD_READ_RECORD 0xb2 38 | #define GSM_CMD_UPDATE_BINARY 0xd6 39 | #define GSM_CMD_UPDATE_RECORD 0xdc 40 | #define GSM_CMD_VERIFY_CHV 0x20 41 | 42 | /* Other GSM-related constants */ 43 | #define GSM_CMD_SELECT_DATA_LEN 0x02 /* Two bytes of data for a 44 | SELECT command */ 45 | #define GSM_CMD_VERIFY_CHV_DATA_LEN 0x08 /* Eight bytes of data for a 46 | VERIFY CHV command */ 47 | 48 | #define GSM_ESCAPE_CHAR 0x1b /* Escape character in GSM charset */ 49 | 50 | #define GSM_SMS_RECORD_LEN 176 /* Length of SMS record, in bytes */ 51 | #define GSM_MAX_SMSC_LEN 10 /* Maximum SM Service Center length (TON/NPI 52 | disregarded); see GSM 04.11, section 8.2.5.1 */ 53 | #define GSM_MIN_ADDRESS_LEN 2 /* Minimum length of TP-DA, TP-OA, or TP-RA */ 54 | #define GSM_MAX_ADDRESS_LEN 12 /* Maximum length of TP-DA, TP-OA, or TP-RA */ 55 | #define GSM_ADN_NUMBER_BUFFER_LEN 14 /* Length of ADN (contact) number data buffer; 56 | see GSM spec section 10.4.1 for EF-ADN file */ 57 | #define GSM_MAX_ADN_NUMBER_LEN 10 /* Maximum length of actual ADN (contact) 58 | number (TON/NPI disregarded); see GSM spec 59 | for EF-ADN file */ 60 | #define GSM_MIN_EF_RESPONSE_LEN 15 /* Minimum length of GET RESPONSE data 61 | after selecting an EF */ 62 | #define GSM_MIN_MF_DF_RESPONSE_LEN 22 /* Minimum length of GET RESPONSE data 63 | after selecting an MF or DF */ 64 | 65 | 66 | extern const char *GSM_basic_charset[]; 67 | extern const char *GSM_basic_charset_extension[]; 68 | extern const char *GSM_sms_status[]; 69 | extern const char *GSM_file_type[]; 70 | extern const char *GSM_ef_structure[]; 71 | 72 | int gsm_parse_response(const uint8_t *response, 73 | unsigned int response_len, 74 | struct GSM_response *resp); 75 | 76 | #endif /* __SCSISIM_GSM_H__ */ 77 | 78 | /* EOF */ 79 | 80 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile for SCSI-generic SIM card driver libraries 3 | # and demonstration executable. 4 | # 5 | # Copyright (c) 2017, Chris Coffey 6 | # 7 | # Permission to use, copy, modify, and/or distribute this software 8 | # for any purpose with or without fee is hereby granted, provided 9 | # that the above copyright notice and this permission notice appear 10 | # in all copies. 11 | # 12 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | # WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 14 | # WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 15 | # AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 16 | # DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA 17 | # OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 18 | # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 19 | # PERFORMANCE OF THIS SOFTWARE. 20 | # 21 | 22 | 23 | CC = gcc 24 | CFLAGS = -g -Wall -std=gnu99 25 | LDFLAGS = -g 26 | 27 | SRC_DIR = src 28 | INCLUDE_DIR = include 29 | BUILD_DIR = build 30 | 31 | COMPILE_OBJS = $(CC) $(CFLAGS) -I $(INCLUDE_DIR) -c $(addprefix $(SRC_DIR)/, $*.c) -o $@ 32 | 33 | # Libraries: 34 | LIB_SRC = usb.c scsi.c sim.c gsm.c utils.c 35 | LIB_OBJS = $(LIB_SRC:%.c=%.o) 36 | BASE_LIB_NAME = scsisim 37 | 38 | SHARED_LIB_NAME = lib$(BASE_LIB_NAME).so 39 | SHARED_OBJS_DIR = $(BUILD_DIR)/shared-objs 40 | SHARED_OBJS = $(addprefix $(SHARED_OBJS_DIR)/, $(LIB_OBJS)) 41 | 42 | STATIC_LIB_NAME = lib$(BASE_LIB_NAME).a 43 | STATIC_OBJS_DIR = $(BUILD_DIR)/static-objs 44 | STATIC_OBJS = $(addprefix $(STATIC_OBJS_DIR)/, $(LIB_OBJS)) 45 | 46 | $(SHARED_OBJS_DIR)/%.o: CFLAGS += -fpic 47 | $(SHARED_OBJS_DIR)/%.o: $(addprefix $(SRC_DIR)/, $(LIB_SRC)) | $(SHARED_OBJS_DIR) 48 | $(COMPILE_OBJS) 49 | 50 | $(STATIC_OBJS_DIR)/%.o: $(addprefix $(SRC_DIR)/, $(LIB_SRC)) | $(STATIC_OBJS_DIR) 51 | $(COMPILE_OBJS) 52 | 53 | # Demonstration executable: 54 | DEMO_NAME = demo 55 | DEMO_SRC = demo.c 56 | DEMO_OBJS_DIR = $(BUILD_DIR)/demo-objs 57 | DEMO_OBJS = $(addprefix $(DEMO_OBJS_DIR)/, $(DEMO_SRC:%.c=%.o)) 58 | 59 | $(DEMO_OBJS_DIR)/%.o: $(addprefix $(SRC_DIR)/, $(DEMO_SRC)) | $(DEMO_OBJS_DIR) 60 | $(COMPILE_OBJS) 61 | 62 | # Targets: 63 | .PHONY: all clean .FORCE 64 | 65 | all: shared_lib static_lib demo 66 | 67 | $(SHARED_OBJS_DIR) $(STATIC_OBJS_DIR) $(DEMO_OBJS_DIR): 68 | @mkdir -p $@ 69 | 70 | shared_lib: $(SHARED_OBJS) 71 | $(CC) $(LDFLAGS) -shared -o $(BUILD_DIR)/$(SHARED_LIB_NAME) $(SHARED_OBJS) 72 | @echo "*******************************" 73 | @echo "* Shared library complete *" 74 | @echo "*******************************" 75 | 76 | static_lib: $(STATIC_OBJS) 77 | $(AR) rcs $(BUILD_DIR)/$(STATIC_LIB_NAME) $(STATIC_OBJS) 78 | @echo "*******************************" 79 | @echo "* Static library complete *" 80 | @echo "*******************************" 81 | 82 | demo: $(DEMO_OBJS) .FORCE 83 | $(CC) $(LDFLAGS) -o $(BUILD_DIR)/$(DEMO_NAME) $(DEMO_OBJS) $(BUILD_DIR)/$(STATIC_LIB_NAME) 84 | # To link the demo with the shared library instead, comment out the previous line, 85 | # uncomment the following line, and then run 'cd build && LD_LIBRARY_PATH=$(pwd) ./demo' 86 | # $(CC) $(LDFLAGS) $(DEMO_OBJS) -o $(BUILD_DIR)/$(DEMO_NAME) -L./$(BUILD_DIR) -l$(BASE_LIB_NAME) 87 | @echo "*******************************" 88 | @echo "* Demo complete *" 89 | @echo "*******************************" 90 | 91 | clean: 92 | $(RM) -r $(SHARED_OBJS_DIR) $(STATIC_OBJS_DIR) $(DEMO_OBJS_DIR) 93 | $(RM) $(BUILD_DIR)/$(SHARED_LIB_NAME) $(BUILD_DIR)/$(STATIC_LIB_NAME) $(BUILD_DIR)/$(DEMO_NAME) 94 | @echo "*******************************" 95 | @echo "* Cleanup complete *" 96 | @echo "*******************************" 97 | 98 | # End makefile 99 | 100 | -------------------------------------------------------------------------------- /src/scsi.c: -------------------------------------------------------------------------------- 1 | /* 2 | * scsi.c 3 | * SCSI-related functions for the scsisim library. 4 | * 5 | * Copyright (c) 2017, Chris Coffey 6 | * 7 | * Permission to use, copy, modify, and/or distribute this software 8 | * for any purpose with or without fee is hereby granted, provided 9 | * that the above copyright notice and this permission notice appear 10 | * in all copies. 11 | * 12 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 14 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 15 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 16 | * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA 17 | * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 18 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 19 | * PERFORMANCE OF THIS SOFTWARE. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "scsisim.h" 28 | #include "scsi.h" 29 | #include "utils.h" 30 | 31 | static inline void scsi_init_io_hdr(struct sg_io_hdr *io_hdr); 32 | 33 | 34 | /** 35 | * Function: scsi_send_cdb 36 | * 37 | * Parameters: 38 | * device: Pointer to scsisim_dev struct. 39 | * my_cmd: Pointer to scsi_cmd struct. 40 | * 41 | * Description: 42 | * Given a pointer to an scsisim_dev struct, set up the SCSI generic 43 | * sg_io_hdr struct with the settings passed in the scsi_cmd struct. 44 | * Then do the actual ioctl() on the device to send the CDB (command 45 | * data block). 46 | * 47 | * Return values: 48 | * SCSISIM_SCSI_SEND_ERROR 49 | * SCSISIM_SUCCESS 50 | */ 51 | int scsi_send_cdb(const struct scsisim_dev *device, struct scsi_cmd *my_cmd) 52 | { 53 | int ret = SCSISIM_SCSI_SEND_ERROR; 54 | struct sg_io_hdr io_hdr; 55 | 56 | /* Initialize the sg_io_hdr struct: */ 57 | scsi_init_io_hdr(&io_hdr); 58 | 59 | /* Set the transfer direction: */ 60 | io_hdr.dxfer_direction = 61 | (my_cmd->direction == SIM_WRITE) ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV; 62 | 63 | /* Set the SCSI command buffer: */ 64 | io_hdr.cmdp = my_cmd->cdb; 65 | io_hdr.cmd_len = my_cmd->cdb_len; 66 | 67 | /* Set the SCSI data buffer: */ 68 | io_hdr.dxferp = my_cmd->data; 69 | io_hdr.dxfer_len = my_cmd->data_len; 70 | 71 | /* Set the SCSI sense buffer: */ 72 | io_hdr.sbp = my_cmd->sense; 73 | io_hdr.mx_sb_len = my_cmd->sense_len; 74 | 75 | /* Print some debug info if requested: */ 76 | if (scsisim_verbose()) 77 | { 78 | scsisim_pinfo("%s: >>> SENDING COMMAND >>>", __func__); 79 | print_binary_buffer(io_hdr.cmdp, io_hdr.cmd_len); 80 | 81 | if (io_hdr.dxfer_direction == SG_DXFER_TO_DEV) 82 | { 83 | scsisim_pinfo("%s: >>> SENDING DATA >>>", 84 | __func__, io_hdr.dxfer_len); 85 | print_binary_buffer(io_hdr.dxferp, io_hdr.dxfer_len); 86 | } 87 | } 88 | 89 | /* We're ready -- send the command to the SCSI generic kernel driver: */ 90 | if (ioctl(device->fd, SG_IO, &io_hdr) == 0) 91 | { 92 | my_cmd->data_xfered = io_hdr.dxfer_len - io_hdr.resid; 93 | my_cmd->sense_xfered = io_hdr.sb_len_wr; 94 | ret = SCSISIM_SUCCESS; 95 | } 96 | 97 | /* Print a whole bunch more debug info if requested: */ 98 | if (scsisim_verbose()) 99 | { 100 | scsisim_pinfo("%s: io_hdr.status = %d", 101 | __func__, io_hdr.status); 102 | scsisim_pinfo("%s: %d data bytes transferred", 103 | __func__, my_cmd->data_xfered); 104 | 105 | if (io_hdr.dxfer_len > 0 && io_hdr.resid > 0) 106 | { 107 | scsisim_pinfo("%s: data transfer underrun by %d bytes", 108 | __func__, io_hdr.resid); 109 | } 110 | 111 | if (io_hdr.dxfer_direction == SG_DXFER_FROM_DEV && my_cmd->data_xfered) 112 | { 113 | scsisim_pinfo("%s: <<< RECEIVED DATA <<<", __func__); 114 | print_binary_buffer(my_cmd->data, my_cmd->data_xfered); 115 | } 116 | 117 | if (my_cmd->sense_xfered) 118 | { 119 | scsisim_pinfo("%s: received %d bytes of sense data", 120 | __func__, my_cmd->sense_xfered); 121 | print_binary_buffer(my_cmd->sense, my_cmd->sense_xfered); 122 | } 123 | 124 | scsisim_pinfo("%s: returning %d (%s)", 125 | __func__, ret, scsisim_strerror(ret)); 126 | } 127 | 128 | return ret; 129 | } 130 | 131 | /** 132 | * Function: scsi_init_io_hdr 133 | * 134 | * Parameters: 135 | * io_hdr: Pointer to SCSI generic sg_io_hdr struct. 136 | * 137 | * Description: 138 | * Initialize the specified sg_io_hdr struct with settings 139 | * common to all SCSI commands sent to the device. 140 | * 141 | * Return value: 142 | * None 143 | */ 144 | static inline void scsi_init_io_hdr(struct sg_io_hdr *io_hdr) 145 | { 146 | memset(io_hdr, 0, sizeof(struct sg_io_hdr)); 147 | 148 | io_hdr->interface_id = 'S'; /* Per scsi/sg.h, this must always be set to 'S' */ 149 | io_hdr->timeout = 1000; /* In milliseconds */ 150 | } 151 | 152 | 153 | /* EOF */ 154 | 155 | -------------------------------------------------------------------------------- /src/usb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * usb.c 3 | * USB-related functions for the scsisim library. 4 | * 5 | * Copyright (c) 2017, Chris Coffey 6 | * 7 | * Permission to use, copy, modify, and/or distribute this software 8 | * for any purpose with or without fee is hereby granted, provided 9 | * that the above copyright notice and this permission notice appear 10 | * in all copies. 11 | * 12 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 14 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 15 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 16 | * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA 17 | * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 18 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 19 | * PERFORMANCE OF THIS SOFTWARE. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "scsisim.h" 28 | #include "usb.h" 29 | 30 | #define VENDOR_FILE "idVendor" 31 | #define PRODUCT_FILE "idProduct" 32 | 33 | #define VENDOR_INDEX 0 34 | #define PRODUCT_INDEX 1 35 | #define DEVICE_INDEX 2 36 | 37 | 38 | /** 39 | * Function: usb_get_vendor_product 40 | * 41 | * Parameters: 42 | * device: Pointer to scsisim_dev struct. 43 | * vendor: (Output) USB vendor number. 44 | * product: (Output) USB product number. 45 | * 46 | * Description: 47 | * Given a pointer to an scsisim_dev struct, traverse the sysfs directory 48 | * structure for the associated device name to obtain the USB vendor 49 | * and product numbers. 50 | * 51 | * Return values: 52 | * SCSISIM_SYSFS_CHDIR_FAILED 53 | * SCSISIM_USB_VENDOR_OPEN_FAILED 54 | * SCSISIM_USB_PRODUCT_OPEN_FAILED 55 | * SCSISIM_SUCCESS 56 | */ 57 | int usb_get_vendor_product(const struct scsisim_dev *device, 58 | unsigned int *vendor, 59 | unsigned int *product) 60 | { 61 | int ret; 62 | char cwd[PATH_MAX], sysfs_sg_full_path[PATH_MAX]; 63 | FILE *fpVendor = NULL, *fpProduct = NULL; 64 | 65 | *vendor = 0; 66 | *product = 0; 67 | 68 | snprintf(sysfs_sg_full_path, 69 | PATH_MAX, 70 | "%s/%s", 71 | SYSFS_SG_BASE_PATH, 72 | device->name); 73 | 74 | if (scsisim_verbose()) 75 | scsisim_pinfo("%s: ready to change directory to %s", 76 | __func__, sysfs_sg_full_path); 77 | 78 | /* Change to the physical device directory in sysfs -- equivalent to 79 | * `cd -P /sys/class/scsi_generic/sg[X]` */ 80 | ret = chdir(sysfs_sg_full_path); 81 | 82 | if (ret) 83 | { 84 | if (scsisim_verbose()) 85 | scsisim_pinfo("%s: changing to %s failed", 86 | __func__, sysfs_sg_full_path); 87 | 88 | return(SCSISIM_SYSFS_CHDIR_FAILED); 89 | } 90 | 91 | if (scsisim_verbose()) 92 | scsisim_pinfo("%s: current directory is %s", 93 | __func__, getcwd(cwd, PATH_MAX)); 94 | 95 | /* Back out to the directory that contains the idProduct and idVendor files. 96 | * This is usually something like /sys/devices/pci0000:00/0000:00:14.0/usb1/1-3 */ 97 | ret = chdir("../../../../../.."); 98 | 99 | if (ret) 100 | { 101 | return(SCSISIM_SYSFS_CHDIR_FAILED); 102 | } 103 | 104 | if (scsisim_verbose()) 105 | scsisim_pinfo("%s: current directory is %s", 106 | __func__, getcwd(cwd, PATH_MAX)); 107 | 108 | /* Get the USB vendor ID */ 109 | if ((fpVendor = fopen(VENDOR_FILE, "r")) == NULL) 110 | { 111 | return(SCSISIM_USB_VENDOR_OPEN_FAILED); 112 | } 113 | 114 | fscanf(fpVendor, "%x", vendor); 115 | fclose(fpVendor); 116 | 117 | if (scsisim_verbose()) 118 | scsisim_pinfo("%s: device vendor is %x", __func__, *vendor); 119 | 120 | /* Get the USB product ID */ 121 | if ((fpProduct = fopen(PRODUCT_FILE, "r")) == NULL) 122 | { 123 | return(SCSISIM_USB_PRODUCT_OPEN_FAILED); 124 | } 125 | 126 | fscanf(fpProduct, "%x", product); 127 | fclose(fpProduct); 128 | 129 | if (scsisim_verbose()) 130 | scsisim_pinfo("%s: device product is %x", __func__, *product); 131 | 132 | return SCSISIM_SUCCESS; 133 | } 134 | 135 | /** 136 | * Function: usb_is_device_supported 137 | * 138 | * Parameters: 139 | * device: Pointer to scsisim_dev struct. 140 | * vendor: (Output) USB vendor number. 141 | * product: (Output) USB product number. 142 | * supported_devices: Array of supported USB devices organized by 143 | * vendor and product number. See device.h 144 | * 145 | * Description: 146 | * Given a USB vendor and product number, determine if it is in the 147 | * list of supported USB devices. If so, set the device index in 148 | * scsisim_dev struct so it knows which definitions in device.h to reference. 149 | * 150 | * Return values: 151 | * true - The specified device is supported. 152 | * false - The specified device is not supported. 153 | */ 154 | bool usb_is_device_supported(struct scsisim_dev *device, 155 | unsigned int vendor, 156 | unsigned int product, 157 | const unsigned int supported_devices[][3]) 158 | { 159 | int i; 160 | 161 | for (i = 0; supported_devices[i][VENDOR_INDEX] != 0; i++) 162 | { 163 | if (supported_devices[i][VENDOR_INDEX] == vendor && 164 | supported_devices[i][PRODUCT_INDEX] == product) 165 | { 166 | /* We have a match */ 167 | device->index = supported_devices[i][DEVICE_INDEX]; 168 | 169 | if (scsisim_verbose()) 170 | scsisim_pinfo("%s: device vendor/product is supported", __func__); 171 | 172 | return true; 173 | } 174 | } 175 | 176 | return false; 177 | } 178 | 179 | /* EOF */ 180 | 181 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # scsisim 2 | 3 | The **scsisim** (SCSI SIM) library provides an API for accessing USB SIM card readers that use the SCSI protocol. It is a user-space "driver" that calls into the Linux [SCSI generic](http://sg.danny.cz/sg/) kernel driver. 4 | 5 | ## Motivation 6 | 7 | There are many serial-based and PC/SC-based USB SIM card readers available, and Linux support for them generally isn't an issue. However, I purchased an inexpensive "APM UR-200" USB SIM card reader in France awhile back. It includes a basic "SimCardRead.exe" Windows application that allows you to view and edit SIM card contacts and SMS messages, but nothing else. Linux and Mac users are completely out of luck, with no end-user application available. 8 | 9 | I did some USB packet sniffing on Windows to see what was going back and forth between the application and the USB device. It turns out that the device uses [standard GSM commands](http://www.etsi.org/deliver/etsi_gts/11/1111/05.03.00_60/gsmts_1111v050300p.pdf) embedded in SCSI commands to read from and write to the SIM card. I reverse-engineered the device's initialization packets and SIM commands, and the result is the **scsisim** library. Now I can read and edit SIM cards using the device on Linux, as well as view and edit additional "hidden" SIM files and data that the Windows application does not. 10 | 11 | I have yet to see another SIM card reader that uses SCSI, so the **scsisim** library is necessarily based on a sample size of one device (and over a dozen different SIM cards). If you come across another SCSI-based SIM device, you should be able to add the device-specific code to **device.h** and get things working relatively quickly. Pull requests welcome. 12 | 13 | For the record, here is what `lsusb` reports for the "APM UR-200" USB SIM card reader: 14 | 15 | `ID 0420:1307 Chips and Technologies Celly SIM Card Reader` 16 | 17 | ## Sample output 18 | 19 | Here is some output from the demonstration program (with sanitized numbers). This was run on an Orange France SIM card: 20 | 21 |
Sample output 22 |

23 | 24 | ``` 25 | chris@deb:~/scsisim$ build/demo sg3 -p 1234 26 | PIN enabled on card; 3 attempts remaining 27 | Do you want to send PIN 1234 to the card? [y/n] y 28 | [INFO: PIN verification successful.] 29 | ICCID = 8933015XXXXXXXXXXXXX 30 | SPN = Orange F 31 | ==================== 32 | ADN record #1 33 | Contact name: Maison 34 | Contact number: 0172XXXXXX 35 | ==================== 36 | ADN record #2 37 | Contact name: Mike 38 | Contact number: 0620XXXXXX 39 | ==================== 40 | ADN record #3 41 | Contact name: Balance 42 | Contact number: #123# 43 | ==================== 44 | 45 | [Output truncated for brevity] 46 | 47 | ==================== 48 | ADN record #249 49 | ADN record unused 50 | ==================== 51 | ADN record #250 52 | ADN record unused 53 | ==================== 54 | SMS record #1 55 | Status: Message received and read 56 | SMSC: 31654XXXXXXX 57 | Sender: 3364XXXXXXX 58 | Date: 05/02/2011 59 | Time: 21:31:18 60 | Timezone: 00 61 | Message: Mostly. Gonna stay a bit longer 62 | ==================== 63 | SMS record #2 64 | Status: Message received and read 65 | SMSC: 33689004000 66 | Sender: 20220 67 | Date: 11/08/2010 68 | Time: 08:14:31 69 | Timezone: 64 70 | Message: Orange Info : voici les mots clés disponibles INFO, METEO, SPORT, FOOT, RUGBY, TV, LOTO, QUINTE, BLAGUE, BOURSE, HOROSCOPE 71 | ==================== 72 | 73 | [Output truncated for brevity] 74 | 75 | ==================== 76 | SMS record #49 77 | Status: Unused space 78 | [ERROR: SMS record parse failed: Invalid SMS Center number] 79 | ==================== 80 | SMS record #50 81 | Status: Unused space 82 | [ERROR: SMS record parse failed: Invalid SMS Center number] 83 | chris@deb:~/scsisim$ 84 | ``` 85 | 86 |

87 |
88 | 89 | ## How to build 90 | 91 | Assuming `gcc` and `make` are installed on your system, building everything is as easy as running `make` from the project root directory. The makefile builds the following in the **build** subdirectory: 92 | 93 | * **libscsisim.so** (shared library) 94 | * **libscsisim.a** (static library) 95 | * **demo** (demo application linked to the static library) 96 | 97 | To run the **demo** application, type `./demo [DEVICE]` at the command line, where [DEVICE] is the SCSI generic name (for example, sg3). You can determine the SCSI generic name for a device by running `dmesg | grep "scsi generic sg"`. Or if you have the `lsscsi` program installed, just run `lsscsi -g`. 98 | 99 | Additionally, you can use the following command-line options with the **demo** application: 100 | 101 | `-p [PIN]`: If the SIM card has a PIN (CHV) enabled, you must specify a PIN to access some of the card's contents. 102 | 103 | `-v`: Display verbose information, including raw SCSI commands and responses, diagnostic information, etc. All verbose information goes to stderr, so you can redirect output as needed. 104 | 105 | **NOTE:** On some Linux distros (Debian and Ubuntu; maybe others), you must add the current user to the **disk** group. This ensures that the user has sufficient privileges to access the device directly using SCSI. Otherwise, you will have to run the demo app as the root user. 106 | 107 | For example, on Debian type the following command to add a user to the **disk** group (replace [USERNAME] with the current username): 108 | 109 | $ usermod -a -G disk [USERNAME] 110 | 111 | ## API usage 112 | 113 | To use the **scsisim** library in your own applications, all you need to do is include **scsisim.h** in your source code (and link the static or shared library, of course). For detailed information about the API functions, see the documentation in **scsisim.h**. See also **demo.c** for examples. 114 | 115 | Briefly, here is how an application uses the **scsisim** library to access a SIM card: 116 | 117 | 1. Call the *scsisim_open_device()* function to open the device. 118 | 2. Call the *scsisim_init_device()* function to send any required initialization commands to the device. Device-specific initialization data is defined in **device.h**. 119 | 3. Call functions that interact directly with the SIM card: 120 | 121 | * *scsisim_select_file()* 122 | * *scsisim_get_response()* 123 | * *scsisim_select_file_and_get_response()* 124 | * *scsisim_read_record()* 125 | * *scsisim_read_binary()* 126 | * *scsisim_update_record()* 127 | * *scsisim_update_binary()* 128 | * *scsisim_verify_chv()* 129 | * *scsisim_send_raw_command()* 130 | 131 | There are also functions that parse and process data returned from the SIM card. Although these functions are technically not necessary for the "driver", they do make it easier to work with SIM card data: 132 | 133 | * *scsisim_packed_bcd_to_ascii()* 134 | * *scsisim_unpack_septets()* 135 | * *scsisim_parse_sms()* 136 | * *scsisim_parse_adn()* 137 | * *scsisim_map_gsm_chars()* 138 | * *scsisim_get_gsm_text()* 139 | 140 | 4. When done, call the *scsisim_close_device()* function to close the device. 141 | 142 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * utils.c 3 | * Utility functions for the scsisim library. 4 | * 5 | * Copyright (c) 2017, Chris Coffey 6 | * 7 | * Permission to use, copy, modify, and/or distribute this software 8 | * for any purpose with or without fee is hereby granted, provided 9 | * that the above copyright notice and this permission notice appear 10 | * in all copies. 11 | * 12 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 14 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 15 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 16 | * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA 17 | * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 18 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 19 | * PERFORMANCE OF THIS SOFTWARE. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "scsisim.h" 31 | #include "utils.h" 32 | 33 | #define ROW_SIZE 16 34 | 35 | #define MAX_STRERROR 128 36 | 37 | static bool verbose_output = false; 38 | 39 | static const char BCD_basic_digits[]="0123456789abcdef"; 40 | static const char BCD_telecom_digits[]="0123456789*#,--f"; 41 | 42 | static char error_buf[MAX_STRERROR]; 43 | 44 | static const char *error_list[] = { 45 | "Operation succeeded", /* 0 - SCSISIM_SUCCESS */ 46 | "Device open failed", /* 1 - SCSISIM_DEVICE_OPEN_FAILED */ 47 | "Device close failed", /* 2 - SCSISIM_DEVICE_CLOSE_FAILED */ 48 | "Device not supported", /* 3 - SCSISIM_DEVICE_NOT_SUPPORTED */ 49 | "Invalid file descriptor", /* 4 - SCSISIM_INVALID_FILE_DESCRIPTOR */ 50 | "sysfs directory traversal failed", /* 5 - SCSISIM_SYSFS_CHDIR_FAILED */ 51 | "USB vendor file open failed", /* 6 - SCSISIM_USB_VENDOR_OPEN_FAILED */ 52 | "USB product file open failed", /* 7 - SCSISIM_USB_PRODUCT_OPEN_FAILED */ 53 | "ioctl() for SCSI send failed", /* 8 - SCSISIM_SCSI_SEND_ERROR */ 54 | "No SCSI sense data", /* 9 - SCSISIM_SCSI_NO_SENSE_DATA */ 55 | "Unknown SCSI sense data", /* 10 - SCSISIM_SCSI_UNKNOWN_SENSE_DATA */ 56 | "Invalid PIN", /* 11 - SCSISIM_INVALID_PIN */ 57 | "Memory allocation error", /* 12 - SCSISIM_MEMORY_ALLOCATION_ERROR */ 58 | "Invalid parameter", /* 13 - SCSISIM_INVALID_PARAM */ 59 | "Invalid GSM response", /* 14 - SCSISIM_INVALID_GSM_RESPONSE */ 60 | "Invalid device name", /* 15 - SCSISIM_INVALID_DEVICE_NAME */ 61 | "Invalid SMS status", /* 16 - SCSISIM_SMS_INVALID_STATUS */ 62 | "Invalid SMS Center number", /* 17 - SCSISIM_SMS_INVALID_SMSC */ 63 | "Invalid SMS address", /* 18 - SCSISIM_SMS_INVALID_ADDRESS */ 64 | NULL, /* 19 - Reserved for future use */ 65 | "GSM: Incorrect parameter P3", /* 20 - SCSISIM_GSM_ERROR_PARAM_3 */ 66 | "GSM: Incorrect parameter P1 or P2", /* 21 - SCSISIM_GSM_ERROR_PARAM_1_OR_2 */ 67 | "GSM: Unknown instruction code in command", /* 22 - SCSISIM_GSM_UNKNOWN_INSTRUCTION */ 68 | "GSM: Wrong instruction class in command", /* 23 - SCSISIM_GSM_WRONG_INSTRUCTION_CLASS */ 69 | "GSM: Technical problem with no diagnostic given", /* 24 - SCSISIM_GSM_TECHNICAL_PROBLEM */ 70 | "GSM: Memory problem", /* 25 - SCSISIM_GSM_MEMORY_ERROR */ 71 | "GSM: SIM Application Toolkit busy", /* 26 - SCSISIM_GSM_BUSY */ 72 | "GSM: No EF selected", /* 27 - SCSISIM_GSM_NO_EF_SELECTED */ 73 | "GSM: Out of range (invalid address)", /* 28 - SCSISIM_GSM_INVALID_ADDRESS */ 74 | "GSM: File ID or pattern not found", /* 29 - SCSISIM_GSM_FILE_NOT_FOUND */ 75 | "GSM: File inconsistent with command", /* 30 - SCSISIM_GSM_FILE_INCONSISTENT_WITH_COMMAND */ 76 | "GSM: Unknown status word SW1", /* 31 - SCSISIM_GSM_UNKNOWN_SW1 */ 77 | "GSM: Unknown status word SW2", /* 32 - SCSISIM_GSM_UNKNOWN_SW2 */ 78 | "GSM: No CHV initialized", /* 33 - SCSISIM_GSM_NO_CHV_INITIALIZED */ 79 | "GSM: CHV verification failed", /* 34 - SCSISIM_GSM_CHV_VERIFICATION_FAILED */ 80 | "GSM: CHV status contradiction", /* 35 - SCSISIM_GSM_CHV_STATUS_CONTRADICTION */ 81 | "GSM: Invalidation status contradiction", /* 36 - SCSISIM_GSM_INVALIDATION_STATUS_CONTRADICTION */ 82 | "GSM: CHV blocked", /* 37 - SCSISIM_GSM_CHV_BLOCKED */ 83 | "GSM: Increase cannot be performed (max value reached)", /* 38 - SCSISIM_GSM_INCREASE_FAILED */ 84 | "GSM: Security error", /* 39 - SCSISIM_GSM_SECURITY_ERROR */ 85 | "GSM: Invalid ADN record", /* 40 - SCSISIM_GSM_INVALID_ADN_RECORD */ 86 | }; 87 | 88 | #define MAXERR (sizeof(error_list) / sizeof(error_list[0])) 89 | 90 | /** 91 | * Function: print_binary_buffer 92 | * 93 | * Parameters: 94 | * buf: Pointer to binary buffer. 95 | * len: Length of binary buffer. 96 | * 97 | * Description: 98 | * Print out a nicely formatted hex dump of a binary buffer, similar 99 | * to what `hexdump -C` does. 100 | * 101 | * Return values: 102 | * None 103 | */ 104 | void print_binary_buffer(const uint8_t *buf, const unsigned int len) 105 | { 106 | unsigned int i; 107 | char ascii[ROW_SIZE + 1] = ""; 108 | 109 | if (buf == NULL || len <= 0 ) 110 | return; 111 | 112 | for (i = 0; i < len; i++) 113 | { 114 | /* Print ASCII string when at end of row */ 115 | if (i % ROW_SIZE == 0 && i != 0) 116 | { 117 | fprintf(stderr, "\t%s\n", ascii); 118 | } 119 | 120 | /* Print hex value of current byte */ 121 | fprintf(stderr, "%02x ", buf[i]); 122 | 123 | /* Build up ASCII string for current row */ 124 | ascii[i % ROW_SIZE] = isprint(buf[i]) ? buf[i] : '.'; 125 | ascii[i % ROW_SIZE + 1] = '\0'; 126 | } 127 | 128 | /* Pad any leftover bytes from the last row */ 129 | while (i % ROW_SIZE != 0) 130 | { 131 | fprintf(stderr, " "); 132 | i++; 133 | } 134 | 135 | fprintf(stderr, "\t%s\n", ascii); 136 | } 137 | 138 | /** 139 | * For information about this function, see scsisim.h 140 | */ 141 | char *scsisim_packed_bcd_to_ascii(const uint8_t *bcd, 142 | const unsigned int len, 143 | bool little_endian, 144 | bool strip_sign_flag, 145 | bool use_telecom_digits) 146 | { 147 | unsigned int i; 148 | uint8_t lo_nibble, hi_nibble; 149 | char lo_char, hi_char, *ascii = NULL, *tmp = NULL; 150 | 151 | if (bcd == NULL || 152 | len <= 0 || 153 | (ascii = malloc((size_t)len * 2 + 1)) == NULL) 154 | return NULL; 155 | 156 | tmp = ascii; 157 | 158 | for (i = 0; i < len; i++) 159 | { 160 | lo_nibble = bcd[i] & 0xf; 161 | hi_nibble = bcd[i] >> 4; 162 | 163 | lo_char = use_telecom_digits ? BCD_telecom_digits[lo_nibble] : BCD_basic_digits[lo_nibble]; 164 | hi_char = use_telecom_digits ? BCD_telecom_digits[hi_nibble] : BCD_basic_digits[hi_nibble]; 165 | 166 | if (little_endian) 167 | { 168 | *tmp++ = lo_char; 169 | *tmp++ = hi_char; 170 | } 171 | else 172 | { 173 | *tmp++ = hi_char; 174 | *tmp++ = lo_char; 175 | } 176 | } 177 | 178 | /* Strip off any trailing 'f' characters from the string that are 179 | * functioning as the sign flag */ 180 | if ( strip_sign_flag && *(tmp - 1) == 'f' ) 181 | *(tmp - 1) = '\0'; 182 | else 183 | *tmp = '\0'; 184 | 185 | return ascii; 186 | } 187 | 188 | /** 189 | * For information about this function, see scsisim.h 190 | */ 191 | void scsisim_unpack_septets(const unsigned int num_septets, 192 | const uint8_t *packed, 193 | const unsigned int packed_len, 194 | uint8_t **unpacked, 195 | unsigned int *unpacked_len) 196 | { 197 | unsigned int i, cur_pos; 198 | uint8_t tmp, *ptr = NULL; 199 | 200 | if (num_septets <= 0 || packed == NULL || packed_len <= 0) 201 | return; 202 | 203 | /* Calculate the length of the unpacked buffer */ 204 | *unpacked_len = packed_len * 8 / 7; 205 | 206 | if ((*unpacked = malloc((size_t)*unpacked_len)) == NULL) 207 | return; 208 | 209 | ptr = *unpacked; 210 | 211 | if (scsisim_verbose()) 212 | scsisim_pinfo("%s: unpacked_len = %d septets", 213 | __func__, *unpacked_len); 214 | 215 | for (i = 0; i < packed_len; i++) 216 | { 217 | cur_pos = i % 7; 218 | tmp = packed[i]; 219 | 220 | /* The first septet in a 7-byte group doesn't require any bit 221 | * shifting, but subsequent septets do: */ 222 | if (cur_pos > 0) 223 | { 224 | /* Shift the high bits into position: */ 225 | tmp <<= cur_pos; 226 | 227 | /* Get the low bits from the previous byte: */ 228 | tmp |= packed[i-1] >> (8 - cur_pos); 229 | } 230 | 231 | tmp &= 0x7f; 232 | 233 | *ptr++ = tmp; 234 | 235 | /* If we're at the end of a 7-byte group, we need to grab the 236 | * last (eighth) septet: */ 237 | if (cur_pos == 6) 238 | { 239 | tmp = packed[i] >> 1; 240 | *ptr++ = tmp; 241 | } 242 | 243 | } 244 | 245 | /* By rounding up packed_len earlier to ensure a whole byte, there 246 | * may now be an extra 'unpacked' character -- if so, remove it: */ 247 | if (*unpacked_len > num_septets) 248 | { 249 | if (scsisim_verbose()) 250 | scsisim_pinfo("%s: fixing mismatch between unpacked_len (%d) and num_septets (%d)", 251 | __func__, *unpacked_len, num_septets); 252 | 253 | *unpacked_len = num_septets; 254 | } 255 | } 256 | 257 | /** 258 | * Function: is_digit_string 259 | * 260 | * Parameters: 261 | * str: String to examine. 262 | * 263 | * Description: 264 | * Determine if the specified string contains only digits (0-9). 265 | * 266 | * Return values: 267 | * true - string contains only digits 268 | * false - string contains at least one non-digit 269 | */ 270 | bool is_digit_string(const char *str) 271 | { 272 | int i; 273 | 274 | if (str == NULL) 275 | return false; 276 | 277 | for (i = 0; str[i] != '\0'; i++) 278 | { 279 | if (!isdigit(str[i])) 280 | return false; 281 | } 282 | 283 | return true; 284 | } 285 | 286 | /** 287 | * For information about this function, see scsisim.h 288 | */ 289 | char *scsisim_strerror(int err) 290 | { 291 | /* Remove negative value */ 292 | err = abs(err); 293 | 294 | if ((unsigned)err >= MAXERR || error_list[err] == NULL) 295 | { 296 | snprintf(error_buf, MAX_STRERROR, "Unknown error %d", err); 297 | } 298 | else 299 | { 300 | strncpy(error_buf, error_list[err], MAX_STRERROR - 1); 301 | error_buf[MAX_STRERROR - 1] = '\0'; 302 | } 303 | 304 | return error_buf; 305 | } 306 | 307 | /** 308 | * For information about this function, see scsisim.h 309 | */ 310 | void scsisim_perror(const char *str, int err) 311 | { 312 | if (str == NULL || str[0] == '\0') 313 | fprintf(stderr, "[ERROR: %s]\n", scsisim_strerror(err)); 314 | else 315 | fprintf(stderr, "[ERROR: %s: %s]\n", str, scsisim_strerror(err)); 316 | } 317 | 318 | /** 319 | * For information about this function, see scsisim.h 320 | */ 321 | void scsisim_pinfo(const char* format, ...) 322 | { 323 | va_list args; 324 | 325 | fprintf(stderr, "[INFO: "); 326 | va_start(args, format); 327 | vfprintf(stderr, format, args); 328 | va_end(args); 329 | fprintf(stderr, "]\n"); 330 | } 331 | 332 | /** 333 | * For information about this function, see scsisim.h 334 | */ 335 | void scsisim_printf(const char* format, ...) 336 | { 337 | va_list args; 338 | 339 | va_start(args, format); 340 | vfprintf(stdout, format, args); 341 | va_end(args); 342 | } 343 | 344 | /** 345 | * For information about this function, see scsisim.h 346 | */ 347 | bool scsisim_verbose(void) 348 | { 349 | return verbose_output; 350 | } 351 | 352 | /** 353 | * For information about this function, see scsisim.h 354 | */ 355 | void scsisim_verbose_enable(void) 356 | { 357 | verbose_output = true; 358 | } 359 | 360 | /** 361 | * For information about this function, see scsisim.h 362 | */ 363 | void scsisim_verbose_disable(void) 364 | { 365 | verbose_output = false; 366 | } 367 | 368 | /* EOF */ 369 | 370 | -------------------------------------------------------------------------------- /src/demo.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demo.c 3 | * Demonstrate how to use the scsisim library to access a SIM card's 4 | * contents. 5 | * 6 | * Copyright (c) 2017, Chris Coffey 7 | * 8 | * Permission to use, copy, modify, and/or distribute this software 9 | * for any purpose with or without fee is hereby granted, provided 10 | * that the above copyright notice and this permission notice appear 11 | * in all copies. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 14 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 16 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 17 | * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA 18 | * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 19 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 20 | * PERFORMANCE OF THIS SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | /* Include scsisim.h for API access */ 31 | #include "scsisim.h" 32 | 33 | /* Command-line options */ 34 | static char *opt_pin; 35 | static char *opt_device; 36 | 37 | /* Internal functions */ 38 | static void parse_cmd_opts (int argc, char *argv[]); 39 | static void print_usage_and_exit(void); 40 | 41 | 42 | /** 43 | * Function: main 44 | * 45 | * Description: Demonstrate how to use the scsisim library. This program does 46 | * the following: 47 | * 48 | * 1. Open the device with scsisim_open_device() using the SCSI generic device 49 | * name provided as a command-line argument (for example, "sg3"). 50 | * 51 | * 2. Initialize the device with scsisim_init_device(). 52 | * 53 | * 3. Select various files and directories on the SIM card, and then run 54 | * appropriate commands to read records, etc. This includes contacts and 55 | * SMS messages. 56 | * 57 | * 4. Close the device with scsisim_close_device(). 58 | * 59 | * See the print_usage_and_exit() function below for additional information 60 | * about available command-line options. 61 | * 62 | */ 63 | int main(int argc, char *argv[]) 64 | { 65 | int ret, i, num_records; 66 | uint8_t *tmp_buf, bin_buf[128] = { 0 }; 67 | char *tmp_str; 68 | struct scsisim_dev device; /* defined in scsisim.h */ 69 | struct GSM_response resp; /* defined in scsisim.h */ 70 | 71 | /* Process command-line arguments. */ 72 | parse_cmd_opts(argc, argv); 73 | 74 | /* Open the device with the specified SCSI generic name. This function 75 | * fills in the 'device' struct with data needed by other scsisim API 76 | * functions that interact directly with the SIM card. */ 77 | if ((ret = scsisim_open_device(opt_device, &device)) != SCSISIM_SUCCESS) 78 | { 79 | scsisim_perror(__func__, ret); 80 | goto exit; 81 | } 82 | 83 | /* Initialize the device. This function sends the 'magic' sequence of 84 | * device-specific initialization commands defined in device.h. */ 85 | if ((ret = scsisim_init_device(&device)) != SCSISIM_SUCCESS) 86 | { 87 | scsisim_perror(__func__, ret); 88 | goto close_device; 89 | } 90 | 91 | /* Device is open and initialized. Try to select the Master File (root) 92 | * directory on the SIM card, and get the response data. */ 93 | if ((ret = scsisim_select_file_and_get_response(&device, 94 | GSM_FILE_MF, 95 | bin_buf, 96 | sizeof(bin_buf), 97 | SIM_SELECT_MF_DF, 98 | &resp)) != SCSISIM_SUCCESS) 99 | { 100 | scsisim_perror("Select MF failed", ret); 101 | goto close_device; 102 | } 103 | 104 | /* We successfully selected the Master File. Is there a PIN (CHV) enabled 105 | * on the card? If so, try to use the PIN provided on the command line. */ 106 | if (resp.type.mf_df.CHV1_enabled == true) 107 | { 108 | if (opt_pin == NULL) 109 | { 110 | scsisim_pinfo("PIN enabled on card, but no PIN specified"); 111 | goto close_device; 112 | } 113 | 114 | /* Is the SIM card's PIN already blocked? */ 115 | if (resp.type.mf_df.CHV1_attempts_remaining == 0) 116 | { 117 | scsisim_pinfo("PIN blocked; %d PIN unblock attempts remaining", 118 | resp.type.mf_df.CHV1_unblock_attempts_remaining); 119 | goto close_device; 120 | } 121 | 122 | scsisim_printf("PIN enabled on card; %d attempts remaining\n", 123 | resp.type.mf_df.CHV1_attempts_remaining); 124 | 125 | scsisim_printf("Do you want to send PIN %s to the card? [y/n] ", 126 | opt_pin); 127 | 128 | i = getchar(); 129 | 130 | if (i == 'y' || i == 'Y') 131 | { 132 | /* Send the PIN to the card */ 133 | if ((ret = scsisim_verify_chv(&device, 1, opt_pin)) == SCSISIM_SUCCESS) 134 | { 135 | scsisim_pinfo("PIN verification successful."); 136 | } 137 | else 138 | { 139 | scsisim_perror(__func__, ret); 140 | goto close_device; 141 | } 142 | } 143 | else 144 | scsisim_pinfo("Accessing SIM card without PIN authentication -- some files will be unreadable."); 145 | } 146 | 147 | /* Select the ICCID (ICC Identification) file */ 148 | if ((ret = scsisim_select_file_and_get_response(&device, 149 | GSM_FILE_EF_ICCID, 150 | bin_buf, 151 | sizeof(bin_buf), 152 | SIM_SELECT_EF, 153 | &resp)) == SCSISIM_SUCCESS) 154 | { 155 | /* Read the raw binary data in the ICCID file */ 156 | if ((ret = scsisim_read_binary(&device, 157 | bin_buf, 158 | 0, 159 | resp.type.ef.file_size)) == SCSISIM_SUCCESS) 160 | { 161 | /* Unpack the raw BCD data to an ASCII string */ 162 | tmp_str = scsisim_packed_bcd_to_ascii(bin_buf, 163 | resp.type.ef.file_size, 164 | true, 165 | true, 166 | false); 167 | 168 | scsisim_printf("ICCID = %s\n", tmp_str); 169 | 170 | /* Caller's responsibility to free memory from a 171 | * scsisim_packed_bcd_to_ascii() return value */ 172 | free(tmp_str); 173 | } 174 | else 175 | scsisim_perror("Read EF-ICCID failed", ret); 176 | } 177 | else 178 | scsisim_perror("Select EF-ICCID failed", ret); 179 | 180 | /* Select the GSM directory */ 181 | if ((ret = scsisim_select_file_and_get_response(&device, 182 | GSM_FILE_DF_GSM, 183 | bin_buf, 184 | sizeof(bin_buf), 185 | SIM_SELECT_MF_DF, 186 | &resp)) != SCSISIM_SUCCESS) 187 | { 188 | scsisim_perror("Select DF-GSM failed", ret); 189 | goto close_device; 190 | } 191 | 192 | /* Select the SPN (Service Provider Name) file */ 193 | if ((ret = scsisim_select_file_and_get_response(&device, 194 | GSM_FILE_EF_SPN, 195 | bin_buf, 196 | sizeof(bin_buf), 197 | SIM_SELECT_EF, 198 | &resp)) == SCSISIM_SUCCESS) 199 | { 200 | /* Read the raw binary data in the SPN file */ 201 | if ((ret = scsisim_read_binary(&device, 202 | bin_buf, 203 | 0, 204 | resp.type.ef.file_size)) == SCSISIM_SUCCESS) 205 | { 206 | /* Map the character codes to their printable GSM 207 | * alphabet counterparts */ 208 | tmp_str = scsisim_map_gsm_chars(bin_buf+1, resp.type.ef.file_size-1); 209 | 210 | scsisim_printf("SPN = %s\n", tmp_str); 211 | 212 | /* Caller's responsibility to free memory from a 213 | * scsisim_map_gsm_chars() return value */ 214 | free(tmp_str); 215 | } 216 | else 217 | scsisim_perror("Read EF-SPN failed", ret); 218 | } 219 | else 220 | scsisim_perror("Select EF-SPN failed", ret); 221 | 222 | /* Select the Master File (root) directory */ 223 | if ((ret = scsisim_select_file_and_get_response(&device, 224 | GSM_FILE_MF, 225 | bin_buf, 226 | sizeof(bin_buf), 227 | SIM_SELECT_MF_DF, 228 | &resp)) != SCSISIM_SUCCESS) 229 | { 230 | scsisim_perror("Select MF failed", ret); 231 | goto close_device; 232 | } 233 | 234 | /* Select the TELECOM directory */ 235 | if ((ret = scsisim_select_file_and_get_response(&device, 236 | GSM_FILE_DF_TELECOM, 237 | bin_buf, 238 | sizeof(bin_buf), 239 | SIM_SELECT_MF_DF, 240 | &resp)) != SCSISIM_SUCCESS) 241 | { 242 | scsisim_perror("Select DF-TELECOM failed", ret); 243 | goto close_device; 244 | } 245 | 246 | /* Select the ADN (Abbreviated Dialing Numbers - AKA the "contacts") file */ 247 | if ((ret = scsisim_select_file_and_get_response(&device, 248 | GSM_FILE_EF_ADN, 249 | bin_buf, 250 | sizeof(bin_buf), 251 | SIM_SELECT_EF, 252 | &resp)) == SCSISIM_SUCCESS) 253 | { 254 | /* Allocate space for an ADN record based on its size on the 255 | * card */ 256 | if ((tmp_buf = malloc((size_t)resp.type.ef.record_len)) == NULL) 257 | goto close_device; 258 | 259 | /* Calculate how many records the ADN file contains, which 260 | * varies by SIM card manufacturer */ 261 | num_records = resp.type.ef.file_size / resp.type.ef.record_len; 262 | 263 | /* Go through all of the records in the ADN file */ 264 | for (i = 1; i <= num_records; i++) 265 | { 266 | scsisim_printf("====================\nADN record #%i\n", i); 267 | 268 | /* Read the current record */ 269 | if (scsisim_read_record(&device, 270 | i, 271 | tmp_buf, 272 | resp.type.ef.record_len) == SCSISIM_SUCCESS) 273 | { 274 | /* Parse the current ADN record */ 275 | if ((ret = scsisim_parse_adn(tmp_buf, 276 | resp.type.ef.record_len)) != SCSISIM_SUCCESS) 277 | scsisim_perror("ADN record parse failed", ret); 278 | } 279 | } 280 | 281 | free(tmp_buf); 282 | } 283 | else 284 | scsisim_perror("Select EF-ADN failed", ret); 285 | 286 | /* Select the SMS (Short Message Service) file */ 287 | if ((ret = scsisim_select_file_and_get_response(&device, 288 | GSM_FILE_EF_SMS, 289 | bin_buf, 290 | sizeof(bin_buf), 291 | SIM_SELECT_EF, 292 | &resp)) == SCSISIM_SUCCESS) 293 | { 294 | /* Allocate space for an SMS record based on its size on the 295 | * card */ 296 | if ((tmp_buf = malloc((size_t)resp.type.ef.record_len)) == NULL) 297 | goto close_device; 298 | 299 | /* Calculate how many records the SMS file contains -- this 300 | * varies by SIM card manufacturer */ 301 | num_records = resp.type.ef.file_size / resp.type.ef.record_len; 302 | 303 | /* Go through all of the records in the SMS file */ 304 | for (i = 1; i <= num_records; i++) 305 | { 306 | scsisim_printf("====================\nSMS record #%i\n", i); 307 | 308 | /* Read the current record */ 309 | if (scsisim_read_record(&device, 310 | i, 311 | tmp_buf, 312 | resp.type.ef.record_len) == SCSISIM_SUCCESS) 313 | { 314 | /* Parse the current SMS record */ 315 | if ((ret = scsisim_parse_sms(tmp_buf, 316 | resp.type.ef.record_len)) != SCSISIM_SUCCESS) 317 | scsisim_perror("SMS record parse failed", ret); 318 | } 319 | } 320 | free(tmp_buf); 321 | } 322 | else 323 | scsisim_perror("Select EF-SMS failed", ret); 324 | 325 | close_device: 326 | /* Close the device */ 327 | ret = scsisim_close_device(&device); 328 | 329 | exit: 330 | return ret; 331 | } 332 | 333 | 334 | /** 335 | * Function: parse_cmd_opts 336 | * 337 | * Parameters: 338 | * argc, argv: Passed straight from main(). 339 | * 340 | * Description: 341 | * Use the getopt() function to parse command-line arguments. 342 | * 343 | * Return values: 344 | * None 345 | */ 346 | static void parse_cmd_opts (int argc, char *argv[]) 347 | { 348 | int cmdopt; 349 | 350 | while ((cmdopt = getopt(argc, argv, "p:v")) != -1) 351 | { 352 | switch (cmdopt) 353 | { 354 | case 'p': 355 | opt_pin = optarg; 356 | break; 357 | 358 | case 'v': 359 | scsisim_verbose_enable(); 360 | break; 361 | 362 | case '?': 363 | if (optopt == 'p') 364 | fprintf (stderr, "Option -%c requires an argument.\n", optopt); 365 | else if (isprint(optopt)) 366 | fprintf (stderr, "Unknown option `-%c'.\n", optopt); 367 | else 368 | fprintf (stderr, "Unknown option character `\\x%x'.\n", optopt); 369 | 370 | default: 371 | print_usage_and_exit(); 372 | } 373 | } 374 | 375 | /* Use first non-option argument as device name, ignore anything else */ 376 | if (optind < argc) 377 | opt_device = argv[optind]; 378 | else 379 | print_usage_and_exit(); 380 | } 381 | 382 | 383 | /** 384 | * Function: print_usage_and_exit 385 | * 386 | * Parameters: 387 | * None 388 | * 389 | * Description: 390 | * Print available command-line arguments and exit. 391 | * 392 | * Return values: 393 | * None 394 | */ 395 | static void print_usage_and_exit(void) 396 | { 397 | fprintf(stderr, "\nUsage: ./demo [DEVICE] [OPTIONS]..."); 398 | fprintf(stderr, "\nDemonstrates access to a SIM card reader using the Linux SCSI generic driver.\n\n"); 399 | fprintf(stderr, "Options:\n\n"); 400 | fprintf(stderr, " [DEVICE]\tSCSI generic device name (for example, 'sg1')\n"); 401 | fprintf(stderr, " -p [PIN]\tSpecify PIN number to access card\n"); 402 | fprintf(stderr, " -v\t\tDisplay verbose information\n"); 403 | fprintf(stderr, "\n"); 404 | fprintf(stderr, "Example:\n\n"); 405 | fprintf(stderr, " ./demo sg2 -p 1234 -v\n"); 406 | fprintf(stderr, " (Open SCSI generic device sg2, use PIN 1234, and display verbose information)\n"); 407 | fprintf(stderr, "\n"); 408 | exit(EXIT_FAILURE); 409 | } 410 | 411 | /* EOF */ 412 | 413 | -------------------------------------------------------------------------------- /include/scsisim.h: -------------------------------------------------------------------------------- 1 | /* 2 | * scsisim.h 3 | * API interface file for the scsisim library. 4 | * 5 | * Copyright (c) 2017, Chris Coffey 6 | * 7 | * Permission to use, copy, modify, and/or distribute this software 8 | * for any purpose with or without fee is hereby granted, provided 9 | * that the above copyright notice and this permission notice appear 10 | * in all copies. 11 | * 12 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 14 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 15 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 16 | * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA 17 | * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 18 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 19 | * PERFORMANCE OF THIS SOFTWARE. 20 | */ 21 | 22 | #ifndef __SCSISIM_H__ 23 | #define __SCSISIM_H__ 24 | 25 | #include 26 | #include 27 | 28 | /* API return values -- general */ 29 | #define SCSISIM_SUCCESS 0 30 | #define SCSISIM_DEVICE_OPEN_FAILED -1 31 | #define SCSISIM_DEVICE_CLOSE_FAILED -2 32 | #define SCSISIM_DEVICE_NOT_SUPPORTED -3 33 | #define SCSISIM_INVALID_FILE_DESCRIPTOR -4 34 | #define SCSISIM_SYSFS_CHDIR_FAILED -5 35 | #define SCSISIM_USB_VENDOR_OPEN_FAILED -6 36 | #define SCSISIM_USB_PRODUCT_OPEN_FAILED -7 37 | #define SCSISIM_SCSI_SEND_ERROR -8 38 | #define SCSISIM_SCSI_NO_SENSE_DATA -9 39 | #define SCSISIM_SCSI_UNKNOWN_SENSE_DATA -10 40 | #define SCSISIM_INVALID_PIN -11 41 | #define SCSISIM_MEMORY_ALLOCATION_ERROR -12 42 | #define SCSISIM_INVALID_PARAM -13 43 | #define SCSISIM_INVALID_GSM_RESPONSE -14 44 | #define SCSISIM_INVALID_DEVICE_NAME -15 45 | #define SCSISIM_SMS_INVALID_STATUS -16 46 | #define SCSISIM_SMS_INVALID_SMSC -17 47 | #define SCSISIM_SMS_INVALID_ADDRESS -18 48 | 49 | /* API return values -- GSM error codes */ 50 | #define SCSISIM_GSM_ERROR_PARAM_3 -20 51 | #define SCSISIM_GSM_ERROR_PARAM_1_OR_2 -21 52 | #define SCSISIM_GSM_UNKNOWN_INSTRUCTION -22 53 | #define SCSISIM_GSM_WRONG_INSTRUCTION_CLASS -23 54 | #define SCSISIM_GSM_TECHNICAL_PROBLEM -24 55 | #define SCSISIM_GSM_MEMORY_ERROR -25 56 | #define SCSISIM_GSM_BUSY -26 57 | #define SCSISIM_GSM_NO_EF_SELECTED -27 58 | #define SCSISIM_GSM_INVALID_ADDRESS -28 59 | #define SCSISIM_GSM_FILE_NOT_FOUND -29 60 | #define SCSISIM_GSM_FILE_INCONSISTENT_WITH_COMMAND -30 61 | #define SCSISIM_GSM_UNKNOWN_SW1 -31 62 | #define SCSISIM_GSM_UNKNOWN_SW2 -32 63 | #define SCSISIM_GSM_NO_CHV_INITIALIZED -33 64 | #define SCSISIM_GSM_CHV_VERIFICATION_FAILED -34 65 | #define SCSISIM_GSM_CHV_STATUS_CONTRADICTION -35 66 | #define SCSISIM_GSM_INVALIDATION_STATUS_CONTRADICTION -36 67 | #define SCSISIM_GSM_CHV_BLOCKED -37 68 | #define SCSISIM_GSM_INCREASE_FAILED -38 69 | #define SCSISIM_GSM_SECURITY_ERROR -39 70 | #define SCSISIM_GSM_INVALID_ADN_RECORD -40 71 | 72 | /* Master file and 'root' file IDs: use these 73 | * in scsisim_select_file() calls */ 74 | #define GSM_FILE_MF 0x3f00 75 | #define GSM_FILE_EF_ELP 0x2f05 76 | #define GSM_FILE_EF_ICCID 0x2fe2 77 | 78 | /* Telecom file IDs: use these in scsisim_select_file() calls */ 79 | #define GSM_FILE_DF_TELECOM 0x7f10 80 | #define GSM_FILE_EF_ADN 0x6f3a 81 | #define GSM_FILE_EF_FDN 0x6f3b 82 | #define GSM_FILE_EF_SMS 0x6f3c 83 | #define GSM_FILE_EF_CCP 0x6f3d 84 | #define GSM_FILE_EF_MSISDN 0x6f40 85 | #define GSM_FILE_EF_SMSP 0x6f42 86 | #define GSM_FILE_EF_SMSS 0x6f43 87 | #define GSM_FILE_EF_LND 0x6f44 88 | #define GSM_FILE_EF_SMSR 0x6f47 89 | #define GSM_FILE_EF_SDN 0x6f49 90 | #define GSM_FILE_EF_EXT1 0x6f4a 91 | #define GSM_FILE_EF_EXT2 0x6f4b 92 | #define GSM_FILE_EF_EXT3 0x6f4c 93 | #define GSM_FILE_EF_BDN 0x6f4d 94 | #define GSM_FILE_EF_EXT4 0x6f4e 95 | 96 | /* GSM file IDs: use these in scsisim_select_file() calls */ 97 | #define GSM_FILE_DF_GSM 0x7f20 98 | #define GSM_FILE_EF_LP 0x6f05 99 | #define GSM_FILE_EF_IMSI 0x6f07 100 | #define GSM_FILE_EF_KC 0x6f20 101 | #define GSM_FILE_EF_DCK 0x6f2c 102 | #define GSM_FILE_EF_PLMNSEL 0x6f30 103 | #define GSM_FILE_EF_HPLMN 0x6f31 104 | #define GSM_FILE_EF_CNL 0x6f32 105 | #define GSM_FILE_EF_ACMMAX 0x6f37 106 | #define GSM_FILE_EF_SST 0x6f38 107 | #define GSM_FILE_EF_ACM 0x6f39 108 | #define GSM_FILE_EF_GID1 0x6f3e 109 | #define GSM_FILE_EF_GID2 0x6f3f 110 | #define GSM_FILE_EF_PUCT 0x6f41 111 | #define GSM_FILE_EF_CBMI 0x6f45 112 | #define GSM_FILE_EF_SPN 0x6f46 113 | #define GSM_FILE_EF_CBMID 0x6f48 114 | #define GSM_FILE_EF_CBMIR 0x6f50 115 | #define GSM_FILE_EF_NIA 0x6f51 116 | #define GSM_FILE_EF_KCGPRS 0x6f52 117 | #define GSM_FILE_EF_LOCIGPRS 0x6f53 118 | #define GSM_FILE_EF_BCCH 0x6f74 119 | #define GSM_FILE_EF_ACC 0x6f78 120 | #define GSM_FILE_EF_FPLMN 0x6f7b 121 | #define GSM_FILE_EF_LOCI 0x6f7e 122 | #define GSM_FILE_EF_AD 0x6fad 123 | #define GSM_FILE_EF_PHASE 0x6fae 124 | #define GSM_FILE_EF_VGCS 0x6fb1 125 | #define GSM_FILE_EF_VGCSS 0x6fb2 126 | #define GSM_FILE_EF_VBS 0x6fb3 127 | #define GSM_FILE_EF_VBSS 0x6fb4 128 | #define GSM_FILE_EF_EMLPP 0x6fb5 129 | #define GSM_FILE_EF_AAEM 0x6fb6 130 | #define GSM_FILE_EF_ECC 0x6fb7 131 | 132 | 133 | /* Struct to hold SCSI generic device */ 134 | struct scsisim_dev { 135 | int fd; /* File descriptor */ 136 | unsigned int index; /* Index into sim_devices[] in device.h */ 137 | char *name; /* Name, such as "sg3" */ 138 | }; 139 | 140 | /* Struct to hold fields for master file and directory files: 141 | * See GSM spec, 9.2.1 SELECT command*/ 142 | struct GSM_MF_DF { 143 | uint16_t file_memory; 144 | uint16_t file_id; 145 | uint8_t file_type; 146 | uint8_t characteristics; 147 | bool CHV1_enabled; 148 | uint8_t df_children; 149 | uint8_t ef_children; 150 | uint8_t num_chvs; 151 | bool CHV1_initialized; 152 | uint8_t CHV1_attempts_remaining; 153 | uint8_t CHV1_unblock_attempts_remaining; 154 | bool CHV2_initialized; 155 | uint8_t CHV2_attempts_remaining; 156 | uint8_t CHV2_unblock_attempts_remaining; 157 | }; 158 | 159 | /* Struct to hold fields for elementary files: 160 | * See GSM spec, 9.2.1 SELECT command */ 161 | struct GSM_EF { 162 | uint16_t file_size; 163 | uint16_t file_id; 164 | uint8_t file_type; 165 | /*int access_conditions;*/ /* $TODO */ 166 | uint8_t status; 167 | uint8_t structure; 168 | uint8_t record_len; 169 | }; 170 | 171 | /* Struct to hold everything from a GSM GET RESPONSE 172 | * command */ 173 | struct GSM_response { 174 | int command; 175 | union { 176 | struct GSM_MF_DF mf_df; 177 | struct GSM_EF ef; 178 | } type; 179 | }; 180 | 181 | 182 | /* GET RESPONSE command constants */ 183 | enum { 184 | SIM_SELECT_EF = 1, 185 | SIM_SELECT_MF_DF, 186 | SIM_RUN_GSM_ALGORITHM, /* $TODO */ 187 | SIM_SEEK, /* $TODO */ 188 | SIM_INCREASE, /* $TODO */ 189 | SIM_ENVELOPE /* $TODO */ 190 | }; 191 | 192 | /* SCSI direction constants */ 193 | enum { 194 | SIM_NO_XFER = 0, 195 | SIM_WRITE, 196 | SIM_READ 197 | }; 198 | 199 | 200 | /** 201 | * Function: scsisim_open_device 202 | * 203 | * Parameters: 204 | * dev_name: Name of SCSI generic device to open, e.g., 'sg1'. 205 | * device: Pointer to scsisim_dev struct. 206 | * 207 | * Description: 208 | * Given the name of a SCSI generic device (e.g., 'sg3'), this function 209 | * obtains a file descriptor to the associated device file (e.g., '/dev/sg1'). 210 | * 211 | * NOTE: Depending on your Linux distribution, you may need to add the user 212 | * to the 'disk' (Debian 8) or 'fuse' (Debian 7) group to allow direct access 213 | * to the device. 214 | * 215 | * Return values: 216 | * SCSISIM_SUCCESS 217 | * SCSISIM_INVALID_DEVICE_NAME 218 | * SCSISIM_MEMORY_ALLOCATION_ERROR 219 | * SCSISIM_DEVICE_OPEN_FAILED 220 | */ 221 | int scsisim_open_device(const char *dev_name, struct scsisim_dev *device); 222 | 223 | 224 | /** 225 | * Function: scsisim_close_device 226 | * 227 | * Parameters: 228 | * device: Pointer to scsisim_dev struct. 229 | * 230 | * Description: 231 | * Given a pointer to an scsisim_dev struct, this function closes the file 232 | * descriptor to the associated device file and does other cleanup. 233 | * 234 | * Return values: 235 | * SCSISIM_SUCCESS 236 | * SCSISIM_INVALID_PARAM 237 | * SCSISIM_DEVICE_CLOSE_FAILED 238 | * SCSISIM_INVALID_FILE_DESCRIPTOR 239 | */ 240 | int scsisim_close_device(struct scsisim_dev *device); 241 | 242 | 243 | /** 244 | * Function: scsisim_init_device 245 | * 246 | * Parameters: 247 | * device: Pointer to scsisim_dev struct. 248 | * 249 | * Description: 250 | * Given a pointer to an scsisim_dev struct, this function makes sure the 251 | * attached USB device is supported, and if so, sends SCSI 252 | * initialization commands to the device. 253 | * 254 | * Return values: 255 | * SCSISIM_SUCCESS 256 | * SCSISIM_INVALID_PARAM 257 | * SCSISIM_DEVICE_NOT_SUPPORTED 258 | * Return value from usb_get_vendor_product 259 | * Return value from scsi_send_cdb 260 | */ 261 | int scsisim_init_device(struct scsisim_dev *device); 262 | 263 | 264 | /** 265 | * Function: scsisim_select_file 266 | * 267 | * Parameters: 268 | * device: Pointer to scsisim_dev struct. 269 | * file: ID number of file to select (2 bytes, per GSM 270 | * standard -- you can use the GSM_FILE_* constants 271 | * defined above). 272 | * 273 | * Description: 274 | * Run the GSM SELECT command on the given file ID. 275 | * See GSM TS 100 977, sections 8.1 and 9.2.1 276 | * 277 | * Return values: 278 | * SCSISIM_SUCCESS 279 | * SCSISIM_INVALID_PARAM 280 | * Return value from scsi_send_cdb 281 | * Return value from sim_process_scsi_sense: number of bytes waiting in GET RESPONSE 282 | * SCSISIM_SCSI_NO_SENSE_DATA 283 | */ 284 | int scsisim_select_file(const struct scsisim_dev *device, uint16_t file); 285 | 286 | 287 | /** 288 | * Function: scsisim_get_response 289 | * 290 | * Parameters: 291 | * device: Pointer to scsisim_dev struct. 292 | * data: Buffer for response data. 293 | * len: Length of data buffer. 294 | * command: The GSM command to get a response for: see GET RESPONSE command constants. 295 | * resp: Pointer to GSM_response struct. 296 | * 297 | * Description: 298 | * Run the GSM GET RESPONSE command. 299 | * See GSM TS 100 977, section 9.2.18 300 | * 301 | * Return values: 302 | * SCSISIM_SUCCESS 303 | * SCSISIM_INVALID_PARAM 304 | * Return value from scsi_send_cdb 305 | * Return value from sim_process_scsi_sense 306 | */ 307 | int scsisim_get_response(const struct scsisim_dev *device, 308 | uint8_t *data, 309 | uint8_t len, 310 | int command, 311 | struct GSM_response *resp); 312 | 313 | 314 | /** 315 | * Function: scsisim_select_file_and_get_response 316 | * 317 | * Parameters: 318 | * device: Pointer to scsisim_dev struct. 319 | * file: ID number of file to select (2 bytes, per GSM 320 | * standard -- you can use the GSM_FILE_* constants 321 | * defined above). 322 | * data: Buffer for response data. 323 | * len: Length of data buffer. 324 | * command: The GSM command to get a response for: see GET RESPONSE command constants. 325 | * resp: Pointer to GSM_response struct. 326 | * 327 | * Description: 328 | * Wrapper function -- combines scsisim_select_file() and 329 | * scsisim_get_response() into one function. 330 | * 331 | * Return values: 332 | * SCSISIM_SUCCESS 333 | * Return value from scsisim_select_file 334 | * Return value from scsisim_get_response 335 | */ 336 | int scsisim_select_file_and_get_response(const struct scsisim_dev *device, 337 | uint16_t file, 338 | uint8_t *data, 339 | uint8_t len, 340 | int command, 341 | struct GSM_response *resp); 342 | 343 | 344 | /** 345 | * Function: scsisim_read_record 346 | * 347 | * Parameters: 348 | * device: Pointer to scsisim_dev struct. 349 | * recno: Record number to read in currently selected file (records 350 | * start at 1, NOT zero!). 351 | * data: Buffer for record data. 352 | * len: Length of data buffer. 353 | * 354 | * Description: 355 | * Run the GSM READ RECORD command. 356 | * See GSM TS 100 977, sections 8.5 and 9.2.5 357 | * 358 | * Return values: 359 | * SCSISIM_SUCCESS 360 | * SCSISIM_INVALID_PARAM 361 | * Return value from scsi_send_cdb 362 | * Return value from sim_process_scsi_sense 363 | */ 364 | int scsisim_read_record(const struct scsisim_dev *device, 365 | uint8_t recno, 366 | uint8_t *data, 367 | uint8_t len); 368 | 369 | 370 | /** 371 | * Function: scsisim_read_binary 372 | * 373 | * Parameters: 374 | * device: Pointer to scsisim_dev struct. 375 | * data: Buffer for binary data. 376 | * offset: Offset from which to begin read (zero-based). 377 | * len: Length of data buffer. 378 | * 379 | * Description: 380 | * Run the GSM READ BINARY command. 381 | * See GSM TS 100 977, sections 8.3 and 9.2.3 382 | * 383 | * Return values: 384 | * SCSISIM_SUCCESS 385 | * SCSISIM_INVALID_PARAM 386 | * Return value from scsi_send_cdb 387 | * Return value from sim_process_scsi_sense 388 | */ 389 | int scsisim_read_binary(const struct scsisim_dev *device, 390 | uint8_t *data, 391 | uint16_t offset, 392 | uint8_t len); 393 | 394 | 395 | /** 396 | * Function: scsisim_update_record 397 | * 398 | * Parameters: 399 | * device: Pointer to scsisim_dev struct. 400 | * recno: Record number to update in currently selected file (records 401 | * start at 1, NOT zero!). 402 | * data: Buffer for record data. 403 | * len: Length of data buffer. 404 | * 405 | * Description: 406 | * Run the GSM UPDATE RECORD command. 407 | * See GSM TS 100 977, sections 8.6 and 9.2.6 408 | * 409 | * Return values: 410 | * SCSISIM_SUCCESS 411 | * SCSISIM_INVALID_PARAM 412 | * Return value from scsi_send_cdb 413 | * Return value from sim_process_scsi_sense 414 | */ 415 | int scsisim_update_record(const struct scsisim_dev *device, 416 | uint8_t recno, 417 | uint8_t *data, 418 | uint8_t len); 419 | 420 | 421 | /** 422 | * Function: scsisim_update_binary 423 | * 424 | * Parameters: 425 | * device: Pointer to scsisim_dev struct. 426 | * data: Buffer for binary data. 427 | * offset: Offset from which to begin write (zero-based). 428 | * len: Length of data buffer. 429 | * 430 | * Description: 431 | * Run the GSM UPDATE BINARY command. 432 | * See GSM TS 100 977, sections 8.4 and 9.2.4 433 | * 434 | * Return values: 435 | * SCSISIM_SUCCESS 436 | * SCSISIM_INVALID_PARAM 437 | * Return value from scsi_send_cdb 438 | * Return value from sim_process_scsi_sense 439 | */ 440 | int scsisim_update_binary(const struct scsisim_dev *device, 441 | uint8_t *data, 442 | uint16_t offset, 443 | uint8_t len); 444 | 445 | 446 | /** 447 | * Function: scsisim_verify_chv 448 | * 449 | * Parameters: 450 | * device: Pointer to scsisim_dev struct. 451 | * chv: Number of CHV to check (i.e., CHV1, CHV2). 452 | * pin: PIN to use for verification. 453 | * 454 | * Description: 455 | * Run the GSM VERIFY CHV command. If you have a SIM card with 456 | * a PIN enabled, you must run this command to access certain 457 | * files and directories. 458 | * See GSM TS 100 977, sections 8.9 and 9.2.9 459 | * 460 | * Return values: 461 | * SCSISIM_SUCCESS 462 | * SCSISIM_INVALID_PARAM 463 | * SCSISIM_INVALID_PIN 464 | * SCSISIM_GSM_ERROR_PARAM_3 465 | * Return value from scsi_send_cdb 466 | * Return value from sim_process_scsi_sense 467 | */ 468 | int scsisim_verify_chv(const struct scsisim_dev *device, 469 | uint8_t chv, 470 | const char *pin); 471 | 472 | 473 | /** 474 | * Function: scsisim_send_raw_command 475 | * 476 | * Parameters: 477 | * device: Pointer to scsisim_dev struct. 478 | * direction: SIM_READ or SIM_WRITE. 479 | * command: GSM command code to run. 480 | * P1: Parameter 1 for the GSM command. 481 | * P2: Parameter 2 for the GSM command. 482 | * P3: Parameter 3 for the GSM command. 483 | * data: Data buffer for command (can be NULL). 484 | * len: Length of data buffer. 485 | * 486 | * Description: 487 | * Run an arbitrary GSM command not handled by any of the 488 | * previous scsisim_* functions. This function assumes you know 489 | * what you are doing, as you can mess up a SIM card pretty 490 | * good by sending arbitrary commands. 491 | * 492 | * Return values: 493 | * SCSISIM_SUCCESS 494 | * SCSISIM_INVALID_PARAM 495 | * Return value from scsi_send_cdb 496 | * Return value from sim_process_scsi_sense 497 | */ 498 | int scsisim_send_raw_command(const struct scsisim_dev *device, 499 | uint8_t direction, 500 | uint8_t command, 501 | uint8_t P1, 502 | uint8_t P2, 503 | uint8_t P3, 504 | uint8_t *data, 505 | unsigned int len); 506 | 507 | 508 | /** 509 | * Function: scsisim_parse_sms 510 | * 511 | * Parameters: 512 | * record: Pointer to raw SMS record. 513 | * record_len: Length of SMS record. 514 | * 515 | * Description: 516 | * Given a pointer to an SMS record, parse out and print the 517 | * following information: 518 | * SMS status (unused space, message read, etc.) 519 | * SMS Center number 520 | * Sender / recipient number 521 | * Message timestamp 522 | * Message text 523 | * 524 | * Return values: 525 | * SCSISIM_SUCCESS 526 | * SCSISIM_INVALID_PARAM 527 | * SCSISIM_SMS_INVALID_STATUS 528 | * SCSISIM_SMS_INVALID_SMSC 529 | * SCSISIM_MEMORY_ALLOCATION_ERROR 530 | * SCSISIM_SMS_INVALID_ADDRESS 531 | */ 532 | int scsisim_parse_sms(const uint8_t *record, uint8_t record_len); 533 | 534 | 535 | /** 536 | * Function: scsisim_parse_adn 537 | * 538 | * Parameters: 539 | * record: Pointer to raw ADN record. 540 | * record_len: Length of ADN record. 541 | * 542 | * Description: 543 | * Given a pointer to an ADN (abbreviated dialling number) record, 544 | * parse out and print the number and name. "ADN" is just a fancy term 545 | * for a contact. 546 | * 547 | * Return value: 548 | * SCSISIM_SUCCESS 549 | * SCSISIM_GSM_INVALID_ADN_RECORD 550 | * SCSISIM_MEMORY_ALLOCATION_ERROR 551 | */ 552 | int scsisim_parse_adn(const uint8_t *record, uint8_t record_len); 553 | 554 | 555 | /** 556 | * Function: scsisim_map_gsm_chars 557 | * 558 | * Parameters: 559 | * src: Pointer to unpacked buffer. 560 | * src_len: Length of buffer. 561 | * 562 | * Description: 563 | * Given an unpacked source buffer of GSM character codes, map them to 564 | * their corresponding characters. Handles basic and extended 565 | * characters in the GSM 7-bit alphabet. 566 | * 567 | * Return value: 568 | * Pointer to null-terminated string. Caller's responsibility to free buffer when done. 569 | */ 570 | char *scsisim_map_gsm_chars(const uint8_t *src, unsigned int src_len); 571 | 572 | 573 | /** 574 | * Function: scsisim_get_gsm_text 575 | * 576 | * Parameters: 577 | * packed: Pointer to buffer of packed septets. 578 | * packed_len: Length of packed buffer. 579 | * num_septets: Number of septets in packed buffer. 580 | * 581 | * Description: 582 | * Given a binary array of packed septets, unpack the septets into octets (bytes), 583 | * and then map those values to their corresponding characters in the GSM alphabet. 584 | * 585 | * Return value: 586 | * Pointer to unpacked, null-terminated string. Caller's responsibility to free buffer when done. 587 | */ 588 | char *scsisim_get_gsm_text(const uint8_t *packed, 589 | unsigned int packed_len, 590 | unsigned int num_septets); 591 | 592 | 593 | /** 594 | * Function: scsisim_packed_bcd_to_ascii 595 | * 596 | * Parameters: 597 | * bcd: Pointer to packed BCD buffer. 598 | * len: Length of packed BCD buffer. 599 | * little_endian: Whether the nibbles of each byte are little endian (true) or big endian (false). 600 | * strip_sign_flag: Strip terminating sign flag (usually 0xf) from unpacked ASCII string. 601 | * use_telecom_digits: Use telecom digits (*#, etc) in translation to ASCII chars. 602 | * 603 | * Description: 604 | * Convert a packed BCD buffer to an ASCII string. 605 | * 606 | * Return value: 607 | * Pointer to ASCII buffer. Caller's responsibility to free buffer when done. 608 | */ 609 | char *scsisim_packed_bcd_to_ascii(const uint8_t *bcd, 610 | const unsigned int len, 611 | bool little_endian, 612 | bool strip_sign_flag, 613 | bool use_telecom_digits); 614 | 615 | 616 | /** 617 | * Function: scsisim_unpack_septets 618 | * 619 | * Parameters: 620 | * num_septets: Number of septets in the packed buffer. 621 | * packed: Pointer to buffer of packed septets. 622 | * packed_len: Length of packed buffer in bytes. 623 | * unpacked: (Output) Pointer to unpacked buffer of octets. 624 | * Caller's responsibility to free buffer when done. 625 | * unpacked_len: (Output) Length of unpacked buffer. 626 | * 627 | * Description: 628 | * Unpack a buffer of packed septets into a buffer of octets (bytes). 629 | * 630 | * Return values: 631 | * None 632 | */ 633 | void scsisim_unpack_septets(const unsigned int num_septets, 634 | const uint8_t *packed, 635 | const unsigned int packed_len, 636 | uint8_t **unpacked, 637 | unsigned int *unpacked_len); 638 | 639 | 640 | /** 641 | * Function: scsisim_strerror 642 | * 643 | * Parameters: 644 | * err: Error code number. 645 | * 646 | * Description: 647 | * Given an error code, return a corresponding human-readable string. 648 | * 649 | * Based in part on sample code from 'The Linux Programming Interface' 650 | * by Michael Kerrisk; see: 651 | * http://man7.org/tlpi/code/online/dist/threads/strerror.c.html 652 | * 653 | * Return value: 654 | * Pointer to string message. 655 | */ 656 | char *scsisim_strerror(int err); 657 | 658 | 659 | /** 660 | * Function: scsisim_perror 661 | * 662 | * Parameters: 663 | * str: Optional user-defined string. 664 | * err: Error code number. 665 | * 666 | * Description: 667 | * Given an error code and an optional user-defined string, print 668 | * a formatted error message to stderr (includes newline). 669 | * 670 | * Return values: 671 | * None 672 | */ 673 | void scsisim_perror(const char *str, int err); 674 | 675 | 676 | /** 677 | * Function: scsisim_pinfo 678 | * 679 | * Parameters: 680 | * format: Message format. 681 | * ...: Optional arguments. 682 | * 683 | * Description: 684 | * Print a formatted diagnostic message to stderr (includes newline). 685 | * 686 | * Return values: 687 | * None 688 | */ 689 | void scsisim_pinfo(const char* format, ...); 690 | 691 | 692 | /** 693 | * Function: scsisim_printf 694 | * 695 | * Parameters: 696 | * format: Message format. 697 | * ...: Optional arguments. 698 | * 699 | * Description: 700 | * Print a message to stdout (does not include newline). 701 | * 702 | * Return values: 703 | * None 704 | */ 705 | void scsisim_printf(const char* format, ...); 706 | 707 | /** 708 | * Function: scsisim_verbose 709 | * 710 | * Parameters: 711 | * None 712 | * 713 | * Description: 714 | * Determine if verbose output is enabled. 715 | * 716 | * Return values: 717 | * true if verbose output is enabled, false otherwise. 718 | */ 719 | bool scsisim_verbose(void); 720 | 721 | /** 722 | * Function: scsisim_verbose_enable 723 | * 724 | * Parameters: 725 | * None 726 | * 727 | * Description: 728 | * Enable verbose output. Note that this results in A LOT of program output. 729 | * 730 | * Return values: 731 | * None 732 | */ 733 | void scsisim_verbose_enable(void); 734 | 735 | /** 736 | * Function: scsisim_verbose_disable 737 | * 738 | * Parameters: 739 | * None 740 | * 741 | * Description: 742 | * Disable verbose output. 743 | * 744 | * Return values: 745 | * None 746 | */ 747 | void scsisim_verbose_disable(void); 748 | 749 | 750 | #endif /* __SCSISIM_H__ */ 751 | 752 | /* EOF */ 753 | 754 | -------------------------------------------------------------------------------- /src/gsm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * gsm.c 3 | * GSM-related functions for the scsisim library. 4 | * 5 | * Copyright (c) 2017, Chris Coffey 6 | * 7 | * Permission to use, copy, modify, and/or distribute this software 8 | * for any purpose with or without fee is hereby granted, provided 9 | * that the above copyright notice and this permission notice appear 10 | * in all copies. 11 | * 12 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 14 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 15 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 16 | * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA 17 | * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 18 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 19 | * PERFORMANCE OF THIS SOFTWARE. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "scsisim.h" 29 | #include "gsm.h" 30 | #include "utils.h" 31 | 32 | static void dump_gsm_response(const struct GSM_response *resp); 33 | 34 | const char *GSM_basic_charset[] = { 35 | /* 0x00 to 0x07: */ 36 | "@", "\u00a3", "$", "\u00a5", "\u00e8", "\u00e9", "\u00f9", "\u00ec", 37 | /* 0x08 to 0x0f: */ 38 | "\u00f2", "\u00c7", "\n", "\u00d8", "\u00f8", "\r", "\u00c5", "\u00e5", 39 | /* 0x10 to 0x17: */ 40 | "\u0394", "_", "\u03a6", "\u0393", "\u039b", "\u03a9", "\u03a0", "\u03a8", 41 | /* 0x18 to 0x1f: */ 42 | "\u03a3", "\u0398", "\u039e", "\uffff", "\u00c6", "\u00e6", "\u00df", "\u00c9", 43 | /* 0x20 to 0x27: */ 44 | " ", "!", "\"", "#", "\u00a4", "%", "&", "'", 45 | /* 0x28 to 0x2f: */ 46 | "(", ")", "*", "+", ",", "-", ".", "/", 47 | /* 0x30 to 0x37: */ 48 | "0", "1", "2", "3", "4", "5", "6", "7", 49 | /* 0x38 to 0x3f: */ 50 | "8", "9", ":", ";", "<", "=", ">", "?", 51 | /* 0x40 to 0x47: */ 52 | "\u00a1", "A", "B", "C", "D", "E", "F", "G", 53 | /* 0x48 to 0x4f: */ 54 | "H", "I", "J", "K", "L", "M", "N", "O", 55 | /* 0x50 to 0x57: */ 56 | "P", "Q", "R", "S", "T", "U", "V", "W", 57 | /* 0x58 to 0x5f: */ 58 | "X", "Y", "Z", "\u00c4", "\u00d6", "\u00d1", "\u00dc", "\u00a7", 59 | /* 0x60 to 0x67: */ 60 | "\u00bf", "a", "b", "c", "d", "e", "f", "g", 61 | /* 0x68 to 0x6f: */ 62 | "h", "i", "j", "k", "l", "m", "n", "o", 63 | /* 0x70 to 0x77: */ 64 | "p", "q", "r", "s", "t", "u", "v", "w", 65 | /* 0x78 to 0x7f */ 66 | "x", "y", "z", "\u00e4", "\u00f6", "\u00f1", "\u00fc", "\u00e0" 67 | }; 68 | 69 | const char *GSM_basic_charset_extension[] = { 70 | /* 0x00 to 0x07: */ 71 | " ", " ", " ", " ", " ", " ", " ", " ", 72 | /* 0x08 to 0x0f: */ 73 | " ", " ", "\f", " ", " ", " ", " ", " ", 74 | /* 0x10 to 0x17: */ 75 | " ", " ", " ", " ", "^", " ", " ", " ", 76 | /* 0x18 to 0x1f: */ 77 | " ", " ", " ", " ", " ", " ", " ", " ", 78 | /* 0x20 to 0x27: */ 79 | " ", " ", " ", " ", " ", " ", " ", " ", 80 | /* 0x28 to 0x2f: */ 81 | "{", "}", " ", " ", " ", " ", " ", "\\", 82 | /* 0x30 to 0x37: */ 83 | " ", " ", " ", " ", " ", " ", " ", " ", 84 | /* 0x38 to 0x3f: */ 85 | " ", " ", " ", " ", "[", "~", "]", " ", 86 | /* 0x40 to 0x47: */ 87 | "|", " ", " ", " ", " ", " ", " ", " ", 88 | /* 0x48 to 0x4f: */ 89 | " ", " ", " ", " ", " ", " ", " ", " ", 90 | /* 0x50 to 0x57: */ 91 | " ", " ", " ", " ", " ", " ", " ", " ", 92 | /* 0x58 to 0x5f: */ 93 | " ", " ", " ", " ", " ", " ", " ", " ", 94 | /* 0x60 to 0x67: */ 95 | " ", " ", " ", " ", " ", "\u20ac", " ", " ", 96 | /* 0x68 to 0x6f: */ 97 | " ", " ", " ", " ", " ", " ", " ", " ", 98 | /* 0x70 to 0x77: */ 99 | " ", " ", " ", " ", " ", " ", " ", " ", 100 | /* 0x78 to 0x7f */ 101 | " ", " ", " ", " ", " ", " ", " ", " " 102 | }; 103 | 104 | const char *GSM_sms_status[] = { 105 | "Unused space", 106 | "Message received and read", 107 | "[Undefined]", 108 | "Message received but unread", 109 | "[Undefined]", 110 | "Message sent", 111 | "[Undefined]", 112 | "Message not sent" 113 | }; 114 | 115 | const char *GSM_file_type[] = { 116 | "Reserved", 117 | "MF", 118 | "DF", 119 | "[Undefined]", 120 | "EF" 121 | }; 122 | 123 | const char *GSM_ef_structure[] = { 124 | "Transparent", 125 | "Linear fixed", 126 | "[Undefined]", 127 | "Cyclic" 128 | }; 129 | 130 | 131 | /** 132 | * For information about this function, see scsisim.h 133 | */ 134 | int scsisim_parse_sms(const uint8_t *record, uint8_t record_len) 135 | { 136 | /* $FIXUP: This mega-function is just a quick and dirty way to parse 137 | * SMS records. It should be chopped down to size and split into 138 | * multiple functions. */ 139 | 140 | const uint8_t *ptr = record; 141 | unsigned int smsc_len, address_len, msg_len, num_septets, bytes_used, bytes_remaining; 142 | uint8_t sms_status, charset, *tmp = NULL; 143 | uint8_t year, month, day, hours, minutes, seconds; 144 | char *tmp_str1 = NULL, *tmp_str2 = NULL, *tmp_str3 = NULL, *buf = NULL; 145 | bool is_alphanum = false; 146 | 147 | if (ptr == NULL || record_len != GSM_SMS_RECORD_LEN) 148 | { 149 | if (scsisim_verbose()) 150 | scsisim_pinfo("%s: Invalid SMS record or length (%d bytes)", 151 | __func__, record_len); 152 | return SCSISIM_INVALID_PARAM; 153 | } 154 | 155 | /* Now iterate through the entire SMS record, plucking out whatever is 156 | * of interest to us, and skipping the rest. See the GSM spec for more 157 | * information about the various fields. */ 158 | 159 | /* Get SMS status */ 160 | if (*ptr > sizeof(GSM_sms_status) / sizeof(GSM_sms_status[0]) - 1) 161 | { 162 | if (scsisim_verbose()) 163 | scsisim_pinfo("%s: Invalid SMS status %d", 164 | __func__, *ptr); 165 | return SCSISIM_SMS_INVALID_STATUS; 166 | } 167 | 168 | scsisim_printf("Status:\t%s\n", GSM_sms_status[*ptr++]); 169 | 170 | /* Get SMS Center (SMSC) length: subtract 1 byte for TON/NPI, which we 171 | * ignore */ 172 | smsc_len = *ptr++ - 1; 173 | 174 | if (scsisim_verbose()) 175 | scsisim_pinfo("%s: SMS Center length is %d bytes", 176 | __func__, smsc_len); 177 | 178 | if (smsc_len <= 0 || smsc_len > GSM_MAX_SMSC_LEN) 179 | { 180 | /* The entire record is probably free space or invalid, but 181 | we'll press on a bit more to make sure */ 182 | if (scsisim_verbose()) 183 | scsisim_pinfo("%s: Invalid SMS Center length - forcing to %d bytes", 184 | __func__, GSM_MAX_SMSC_LEN); 185 | 186 | smsc_len = GSM_MAX_SMSC_LEN; 187 | } 188 | 189 | /* Skip TON (Type of number) */ 190 | ptr++; 191 | 192 | /* Determine if SMSC number contains valid data */ 193 | if ( *ptr == 0xff ) 194 | { 195 | if (scsisim_verbose()) 196 | scsisim_pinfo("%s: Invalid SMS Center number - aborting parsing for this record", 197 | __func__); 198 | 199 | return SCSISIM_SMS_INVALID_SMSC; 200 | } 201 | 202 | if ((tmp = malloc((size_t)smsc_len)) == NULL) 203 | return SCSISIM_MEMORY_ALLOCATION_ERROR; 204 | 205 | memcpy(tmp, ptr, (size_t)smsc_len); 206 | 207 | /* Unpack the SMSC number */ 208 | buf = scsisim_packed_bcd_to_ascii(tmp, smsc_len, true, true, false); 209 | 210 | scsisim_printf("SMSC:\t%s\n", buf); 211 | free(tmp); 212 | free(buf); 213 | 214 | /* Advance past SMSC number */ 215 | ptr += smsc_len; 216 | 217 | /* TPDU type: get SMS status */ 218 | sms_status = *ptr++ & 0x03; 219 | switch (sms_status) 220 | { 221 | case 0: /* SMS-DELIVER, SMS-DELIVER-REPORT */ 222 | case 1: /* SMS-SUBMIT, SMS-SUBMIT-REPORT */ 223 | 224 | /* Skip TP-MR (Message Reference) for SUBMIT records */ 225 | if (sms_status == 1) 226 | ptr++; 227 | 228 | /* Length of TP-OA (Originating Address) or 229 | * TP-DA (Destination Address) in nibbles */ 230 | address_len = (*ptr++ + (2 - 1)) / 2; 231 | 232 | if (address_len < GSM_MIN_ADDRESS_LEN || 233 | address_len > GSM_MAX_ADDRESS_LEN) 234 | { 235 | if (scsisim_verbose()) 236 | scsisim_pinfo("%s: Invalid address length (%d bytes)", 237 | __func__, address_len); 238 | return SCSISIM_SMS_INVALID_ADDRESS; 239 | } 240 | 241 | if (scsisim_verbose()) 242 | scsisim_pinfo("%s: Valid address length (%d bytes)", 243 | __func__, address_len); 244 | 245 | /* TON / NPI: all we care about is if message is in 246 | * GSM 7-bit alphanumeric instead of BCD digits, 247 | * so examine bits 4-6 */ 248 | if ( (*ptr++ & 0x70) == 0x50 ) 249 | is_alphanum = true; 250 | 251 | /* Get address name or number */ 252 | if ((tmp = malloc((size_t)address_len)) == NULL) 253 | return SCSISIM_MEMORY_ALLOCATION_ERROR; 254 | 255 | memcpy(tmp, ptr, (size_t)address_len); 256 | 257 | if ( is_alphanum ) 258 | { 259 | /* Convert to GSM 7-bit alphanumeric chars */ 260 | num_septets = address_len * 8 / 7; 261 | buf = scsisim_get_gsm_text(tmp, address_len, num_septets); 262 | } 263 | else 264 | { 265 | /* Unpack BCD buffer */ 266 | buf = scsisim_packed_bcd_to_ascii(tmp, 267 | address_len, 268 | true, 269 | true, 270 | false); 271 | } 272 | 273 | scsisim_printf("%s:\t%s\n", 274 | (sms_status == 1) ? "Recipient": "Sender", buf); 275 | free(buf); 276 | free(tmp); 277 | 278 | /* Advance past address number */ 279 | ptr += address_len; 280 | 281 | /* Skip TP-PID (Protocol Identifier) */ 282 | ptr++; 283 | 284 | /* TP-DCS (Data Coding Scheme): bits 2-3 */ 285 | charset = (*ptr++ & 0x0c) >> 2; 286 | 287 | if (sms_status == 1) 288 | { 289 | /* Skip TP-VP (Validity Period) for SUBMIT records */ 290 | ptr++; 291 | } 292 | else 293 | { 294 | /* Get date */ 295 | year = *ptr++; 296 | month = *ptr++; 297 | day = *ptr++; 298 | tmp_str1 = scsisim_packed_bcd_to_ascii(&month, 299 | 1, 300 | true, 301 | false, 302 | false); 303 | tmp_str2 = scsisim_packed_bcd_to_ascii(&day, 304 | 1, 305 | true, 306 | false, 307 | false); 308 | tmp_str3 = scsisim_packed_bcd_to_ascii(&year, 309 | 1, 310 | true, 311 | false, 312 | false); 313 | 314 | scsisim_printf("Date:\t%s/%s/20%s\n", 315 | tmp_str1, tmp_str2, tmp_str3); 316 | 317 | free(tmp_str1); 318 | free(tmp_str2); 319 | free(tmp_str3); 320 | 321 | /* Get time */ 322 | hours = *ptr++; 323 | minutes = *ptr++; 324 | seconds = *ptr++; 325 | tmp_str1 = scsisim_packed_bcd_to_ascii(&hours, 326 | 1, 327 | true, 328 | false, 329 | false); 330 | tmp_str2 = scsisim_packed_bcd_to_ascii(&minutes, 331 | 1, 332 | true, 333 | false, 334 | false); 335 | tmp_str3 = scsisim_packed_bcd_to_ascii(&seconds, 336 | 1, 337 | true, 338 | false, 339 | false); 340 | 341 | scsisim_printf("Time:\t%s:%s:%s\n", 342 | tmp_str1, tmp_str2, tmp_str3); 343 | 344 | free(tmp_str1); 345 | free(tmp_str2); 346 | free(tmp_str3); 347 | 348 | /* Get timezone */ 349 | scsisim_printf("Timezone: %02d\n", *ptr++); 350 | } 351 | 352 | /* Get text length and data */ 353 | num_septets = *ptr++; 354 | msg_len = (num_septets * 7 + (8 - 1)) / 8; 355 | 356 | /* Make sure we don't walk off the end of the record buffer */ 357 | bytes_used = ptr - record; 358 | bytes_remaining = GSM_SMS_RECORD_LEN - bytes_used; 359 | 360 | if (scsisim_verbose()) 361 | scsisim_pinfo("%s: Currently at offset %d in record. %d bytes remaining.", 362 | __func__,bytes_used, bytes_remaining); 363 | 364 | if (msg_len <= 0) 365 | { 366 | scsisim_printf("Message is empty\n"); 367 | return SCSISIM_SUCCESS; 368 | } 369 | else if (msg_len > bytes_remaining) 370 | { 371 | /* This should only happen in rare cases, e.g., a corrupted SIM card */ 372 | scsisim_pinfo("%s: Parsed message length (%d bytes) exceeds bytes remaining in record by %d bytes, truncating message text to %d bytes", 373 | __func__, 374 | msg_len, 375 | msg_len - bytes_remaining, 376 | bytes_remaining); 377 | 378 | /* Truncate message text to however many bytes remain in record */ 379 | msg_len = bytes_remaining; 380 | } 381 | else 382 | { 383 | if (scsisim_verbose()) 384 | scsisim_pinfo("%s: TP-UD has %d septets packed into %d bytes", 385 | __func__, num_septets, msg_len); 386 | } 387 | 388 | /* $TODO: Add multi-part SMS support. Currently we only 389 | * process single, discrete SMS messages. */ 390 | 391 | if ((tmp = malloc((size_t)msg_len)) == NULL) 392 | return SCSISIM_MEMORY_ALLOCATION_ERROR; 393 | 394 | memcpy(tmp, ptr, (size_t)msg_len); 395 | 396 | /* See 3GPP TS 23.038, section 4, "SMS Data Coding Scheme" */ 397 | switch (charset) 398 | { 399 | case 0: /* 7-bit GSM alphabet */ 400 | if (scsisim_verbose()) 401 | scsisim_pinfo("%s: Using 7-bit GSM character set", 402 | __func__); 403 | 404 | buf = scsisim_get_gsm_text(tmp, msg_len, num_septets); 405 | 406 | scsisim_printf("Message: %s\n", buf); 407 | 408 | free(buf); 409 | break; 410 | case 1: /* 8-bit data */ 411 | case 2: /* $TODO: Add support for UTF-16/UCS-2 */ 412 | case 3: /* Reserved for Future Use */ 413 | default: 414 | scsisim_printf("Message: [Unsupported character set]\n"); 415 | 416 | if (scsisim_verbose()) 417 | scsisim_pinfo("%s: Character set code %d unsupported", 418 | __func__, charset); 419 | break; 420 | } 421 | 422 | free(tmp); 423 | 424 | break; 425 | case 2: /* SMS-COMMAND, SMS-STATUS-REPORT */ 426 | /* $TODO: Add support for these SMS statuses */ 427 | break; 428 | default: 429 | /* Reserved for Future Use */ 430 | break; 431 | 432 | } 433 | 434 | return SCSISIM_SUCCESS; 435 | } 436 | 437 | 438 | /** 439 | * For information about this function, see scsisim.h 440 | */ 441 | char *scsisim_get_gsm_text(const uint8_t *packed, 442 | unsigned int packed_len, 443 | unsigned int num_septets) 444 | { 445 | uint8_t *unpacked = NULL; 446 | unsigned int unpacked_len; 447 | char *smsText = NULL; 448 | 449 | if (packed == NULL || packed_len <= 0 || num_septets <= 0) 450 | return NULL; 451 | 452 | /* Unpack the block of septets into bytes */ 453 | scsisim_unpack_septets(num_septets, 454 | packed, 455 | packed_len, 456 | &unpacked, 457 | &unpacked_len); 458 | 459 | /* Map the GSM character codes to printable characters */ 460 | smsText = scsisim_map_gsm_chars(unpacked, unpacked_len); 461 | 462 | free(unpacked); 463 | 464 | return(smsText); 465 | } 466 | 467 | 468 | /** 469 | * For information about this function, see scsisim.h 470 | */ 471 | char *scsisim_map_gsm_chars(const uint8_t *src, unsigned int src_len) 472 | { 473 | unsigned int i; 474 | char *result = NULL, *dest; 475 | const char *ptr; 476 | bool escapeChar = false; 477 | 478 | /* Allocate maximum of 4 bytes per Unicode character, 479 | * plus a null-terminating character: */ 480 | if (src == NULL || src_len <= 0 || 481 | (result = malloc((size_t)src_len * 4 + 1)) == NULL) 482 | return NULL; 483 | 484 | dest = result; 485 | 486 | for (i = 0; i < src_len; i++) 487 | { 488 | /* Check for invalid character index */ 489 | if (src[i] > 0x7f) 490 | { 491 | /* 0xff marks unused bytes, and isn't really "invalid", so: */ 492 | if (scsisim_verbose() && src[i] != 0xff) 493 | scsisim_pinfo("%s: Invalid GSM character code (%d), %d unmapped characters remaining", 494 | __func__, src[i], src_len - i); 495 | break; 496 | } 497 | 498 | if (src[i] == GSM_ESCAPE_CHAR) 499 | { 500 | escapeChar = true; 501 | continue; 502 | } 503 | 504 | if (escapeChar == true) 505 | { 506 | /* Look up the extended character code */ 507 | ptr = GSM_basic_charset_extension[src[i]]; 508 | escapeChar = false; 509 | } 510 | else 511 | { 512 | /* Look up the basic character code */ 513 | ptr = GSM_basic_charset[src[i]]; 514 | } 515 | 516 | /* Avoid repetitive strcat'ing (AKA Shlemiel's algorithm) */ 517 | while ((*dest++ = *ptr++) != '\0') 518 | ; 519 | 520 | /* Back up to null character for next concatenation */ 521 | dest--; 522 | } 523 | 524 | return result; 525 | } 526 | 527 | 528 | /** 529 | * For information about this function, see scsisim.h 530 | */ 531 | int scsisim_parse_adn(const uint8_t *record, uint8_t record_len) 532 | { 533 | const uint8_t* ptr = record; 534 | unsigned int name_len, number_len; 535 | uint8_t *tmp = NULL; 536 | char *buf = NULL; 537 | 538 | /* Check record_len: 14 bytes (required) for number buffer, 539 | * plus at least one byte for the name */ 540 | if (ptr == NULL || record_len < GSM_ADN_NUMBER_BUFFER_LEN + 1u) 541 | { 542 | return SCSISIM_GSM_INVALID_ADN_RECORD; 543 | } 544 | 545 | /* Calculate the name length */ 546 | name_len = record_len - GSM_ADN_NUMBER_BUFFER_LEN; 547 | 548 | if (*ptr == 0xff) 549 | { 550 | scsisim_printf("ADN record unused\n"); 551 | return SCSISIM_SUCCESS; 552 | } 553 | 554 | /* Get the name */ 555 | buf = scsisim_map_gsm_chars(ptr, name_len); 556 | scsisim_printf("Contact name:\t%s\n", buf); 557 | free(buf); 558 | 559 | ptr += name_len; 560 | 561 | /* Get the number length: subtract 1 byte for TON/NPI, which we ignore */ 562 | number_len = *ptr++ - 1; 563 | 564 | if (number_len <= 0 || number_len > GSM_MAX_ADN_NUMBER_LEN ) 565 | { 566 | scsisim_pinfo("%s: Invalid number_len %d, forcing to %d", 567 | __func__, number_len, GSM_MAX_ADN_NUMBER_LEN); 568 | number_len = GSM_MAX_ADN_NUMBER_LEN; 569 | } 570 | 571 | /* Skip TON/NPI */ 572 | ptr++; 573 | 574 | if ((tmp = malloc((size_t)number_len)) == NULL) 575 | return SCSISIM_MEMORY_ALLOCATION_ERROR; 576 | 577 | memcpy(tmp, ptr, (size_t)number_len); 578 | 579 | /* Get the number */ 580 | buf = scsisim_packed_bcd_to_ascii(tmp, number_len, true, true, true); 581 | scsisim_printf("Contact number:\t%s\n", buf); 582 | free(buf); 583 | free(tmp); 584 | 585 | return SCSISIM_SUCCESS; 586 | } 587 | 588 | 589 | /** 590 | * Function: gsm_parse_response 591 | * 592 | * Parameters: 593 | * response: Pointer to buffer containing raw GSM GET RESPONSE data. 594 | * response_len: Length of buffer. 595 | * resp: (Output) Pointer to GSM_response struct holding parsed data. 596 | * 597 | * Description: 598 | * Given an array of raw GSM GET RESPONSE data, parse it out into its components 599 | * and load it into a GSM_response struct. 600 | * See GSM spec, section 9.2.1 (SELECT command) for detailed information about 601 | * all of the fields parsed by this function. 602 | * 603 | * Return values: 604 | * SCSISIM_SUCCESS 605 | * SCSISIM_INVALID_GSM_RESPONSE 606 | */ 607 | int gsm_parse_response(const uint8_t *response, 608 | unsigned int response_len, 609 | struct GSM_response *resp) 610 | { 611 | int ret = SCSISIM_SUCCESS; 612 | 613 | if (response == NULL || 614 | (resp->command == SIM_SELECT_EF && response_len < GSM_MIN_EF_RESPONSE_LEN) || 615 | (resp->command == SIM_SELECT_MF_DF && response_len < GSM_MIN_MF_DF_RESPONSE_LEN)) 616 | return SCSISIM_INVALID_GSM_RESPONSE; 617 | 618 | switch (resp->command) 619 | { 620 | case SIM_SELECT_EF: 621 | /* Bytes 0-1: Reserved for future use */ 622 | resp->type.ef.file_size = response[2] << 8; 623 | resp->type.ef.file_size |= response[3]; 624 | resp->type.ef.file_id = response[4] << 8; 625 | resp->type.ef.file_id |= response[5]; 626 | resp->type.ef.file_type = response[6]; /* 04 = EF (should always be this) */ 627 | /* Byte 7: Reserved for future use */ 628 | /* resp->type.ef.access_conditions = response[8-10]; */ /* $TODO */ 629 | resp->type.ef.status = response[11]; 630 | resp->type.ef.structure = response[13]; 631 | resp->type.ef.record_len = response[14]; 632 | break; 633 | 634 | case SIM_SELECT_MF_DF: 635 | /* Bytes 0-1: Reserved for future use */ 636 | resp->type.mf_df.file_memory = response[2] << 8; 637 | resp->type.mf_df.file_memory |= response[3]; 638 | resp->type.mf_df.file_id = response[4] << 8; 639 | resp->type.mf_df.file_id |= response[5]; 640 | resp->type.mf_df.file_type = response[6]; /* 01 = MF, 02 = DF */ 641 | resp->type.mf_df.characteristics = response[13]; 642 | resp->type.mf_df.CHV1_enabled = (response[13] & 0x80) ? false : true; 643 | resp->type.mf_df.df_children = response[14]; 644 | resp->type.mf_df.ef_children = response[15]; 645 | resp->type.mf_df.num_chvs = response[16]; 646 | resp->type.mf_df.CHV1_initialized = (response[18] & 0x80) ? true : false; 647 | resp->type.mf_df.CHV1_attempts_remaining = response[18] & 0x0f; 648 | resp->type.mf_df.CHV1_unblock_attempts_remaining = response[19] & 0x0f; 649 | resp->type.mf_df.CHV2_initialized = (response[20] & 0x80) ? true : false; 650 | resp->type.mf_df.CHV2_attempts_remaining = response[20] & 0x0f; 651 | resp->type.mf_df.CHV2_unblock_attempts_remaining = response[21] & 0x0f; 652 | break; 653 | 654 | default: 655 | scsisim_pinfo("%s: Unsupported response type", __func__); 656 | break; 657 | } 658 | 659 | if (scsisim_verbose()) 660 | dump_gsm_response(resp); 661 | 662 | return ret; 663 | } 664 | 665 | /** 666 | * Function: dump_gsm_response 667 | * 668 | * Parameters: 669 | * resp: Pointer to GSM_response struct holding response data. 670 | * 671 | * Description: 672 | * Given a pointer to a GSM_response struct, print out its members in a 673 | * readable format. 674 | * 675 | * Return values: 676 | * None 677 | */ 678 | static void dump_gsm_response(const struct GSM_response *resp) 679 | { 680 | switch (resp->command) 681 | { 682 | case SIM_SELECT_EF: 683 | scsisim_pinfo("====== GSM EF Response Data ======"); 684 | scsisim_pinfo("ID: %x", 685 | resp->type.ef.file_id); 686 | scsisim_pinfo("Size: %d bytes", 687 | resp->type.ef.file_size); 688 | scsisim_pinfo("Type: %s", 689 | (resp->type.ef.file_type > sizeof(GSM_file_type) / sizeof(GSM_file_type[0]) - 1) ? "[Undefined]" : GSM_file_type[resp->type.ef.file_type]); 690 | scsisim_pinfo("Status: %d", 691 | resp->type.ef.status); 692 | scsisim_pinfo("Structure: %s", 693 | (resp->type.ef.structure > sizeof(GSM_ef_structure) / sizeof(GSM_ef_structure[0]) - 1) ? "[Undefined]" : GSM_ef_structure[resp->type.ef.structure]); 694 | scsisim_pinfo("Record length: %d bytes", 695 | resp->type.ef.record_len); 696 | scsisim_pinfo("======== End Response Data ======="); 697 | break; 698 | 699 | case SIM_SELECT_MF_DF: 700 | scsisim_pinfo("===== GSM MF/DF Response Data ===="); 701 | scsisim_pinfo("ID: %x", 702 | resp->type.mf_df.file_id); 703 | scsisim_pinfo("Free memory: %d bytes", 704 | resp->type.mf_df.file_memory); 705 | scsisim_pinfo("Type: %s", 706 | (resp->type.mf_df.file_type > sizeof(GSM_file_type) / sizeof(GSM_file_type[0]) - 1) ? "[Undefined]" : GSM_file_type[resp->type.mf_df.file_type]); 707 | scsisim_pinfo("Characteristics: %d", 708 | resp->type.mf_df.characteristics); 709 | scsisim_pinfo("CHV1 enabled: %s", 710 | resp->type.mf_df.CHV1_enabled ? "true" : "false"); 711 | scsisim_pinfo("Child DFs: %d", 712 | resp->type.mf_df.df_children); 713 | scsisim_pinfo("Child EFs: %d", 714 | resp->type.mf_df.ef_children); 715 | scsisim_pinfo("Number of CHVs: %d", 716 | resp->type.mf_df.num_chvs); 717 | scsisim_pinfo("CHV1 initialized: %s", 718 | resp->type.mf_df.CHV1_initialized ? "true" : "false"); 719 | scsisim_pinfo("CHV1 attempts remaining: %d", 720 | resp->type.mf_df.CHV1_attempts_remaining); 721 | scsisim_pinfo("CHV2 initialized: %s", 722 | resp->type.mf_df.CHV2_initialized ? "true" : "false"); 723 | scsisim_pinfo("CHV2 attempts remaining: %d", 724 | resp->type.mf_df.CHV2_attempts_remaining); 725 | scsisim_pinfo("======== End Response Data ======="); 726 | break; 727 | 728 | default: 729 | scsisim_pinfo("%s: Unsupported response type", __func__); 730 | break; 731 | } 732 | 733 | } 734 | 735 | 736 | /* EOF */ 737 | 738 | -------------------------------------------------------------------------------- /src/sim.c: -------------------------------------------------------------------------------- 1 | /* 2 | * sim.c 3 | * SIM-related functions for the scsisim library. 4 | * 5 | * Copyright (c) 2017, Chris Coffey 6 | * 7 | * Permission to use, copy, modify, and/or distribute this software 8 | * for any purpose with or without fee is hereby granted, provided 9 | * that the above copyright notice and this permission notice appear 10 | * in all copies. 11 | * 12 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 14 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 15 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 16 | * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA 17 | * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 18 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 19 | * PERFORMANCE OF THIS SOFTWARE. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "scsisim.h" 32 | #include "gsm.h" 33 | #include "scsi.h" 34 | #include "device.h" 35 | #include "usb.h" 36 | #include "utils.h" 37 | 38 | static int sim_process_scsi_sense(const struct scsisim_dev *device, 39 | const uint8_t *sense, 40 | unsigned int len); 41 | 42 | static inline void sim_free_device_name(struct scsisim_dev *device); 43 | 44 | 45 | /** 46 | * For information about this function, see scsisim.h 47 | */ 48 | int scsisim_open_device(const char *dev_name, struct scsisim_dev *device) 49 | { 50 | int ret; 51 | char full_path[PATH_MAX]; 52 | 53 | if (dev_name == NULL || 54 | strlen(dev_name) < 3 || /* Name must be at least 3 bytes long (e.g., 'sg1') */ 55 | strncmp(dev_name, "sg", 2) != 0) /* Name must start with 'sg' */ 56 | return SCSISIM_INVALID_DEVICE_NAME; 57 | 58 | if ((device->name = malloc(strlen(dev_name) + 1)) == NULL) 59 | return SCSISIM_MEMORY_ALLOCATION_ERROR; 60 | 61 | strcpy(device->name, dev_name); 62 | 63 | snprintf(full_path, PATH_MAX, "/dev/%s", device->name); 64 | 65 | if (scsisim_verbose()) 66 | scsisim_pinfo("%s: ready to open %s", __func__, full_path); 67 | 68 | /* Try to open device */ 69 | device->fd = open(full_path, O_RDWR); 70 | 71 | if (device->fd > 0) 72 | { 73 | /* We have a valid file descriptor */ 74 | if (scsisim_verbose()) 75 | scsisim_pinfo("%s: device opened, fd = %d, name = %s", 76 | __func__, device->fd, device->name); 77 | 78 | ret = SCSISIM_SUCCESS; 79 | } 80 | else 81 | { 82 | /* Device open failed, so clean up */ 83 | sim_free_device_name(device); 84 | ret = SCSISIM_DEVICE_OPEN_FAILED; 85 | } 86 | 87 | return ret; 88 | } 89 | 90 | /** 91 | * For information about this function, see scsisim.h 92 | */ 93 | int scsisim_close_device(struct scsisim_dev *device) 94 | { 95 | int ret; 96 | 97 | if (device == NULL) 98 | return SCSISIM_INVALID_PARAM; 99 | 100 | sim_free_device_name(device); 101 | 102 | if (device->fd > 0) 103 | { 104 | if (close(device->fd)) 105 | ret = SCSISIM_DEVICE_CLOSE_FAILED; 106 | else 107 | { 108 | if (scsisim_verbose()) 109 | scsisim_pinfo("%s: device closed", __func__); 110 | 111 | device->fd = 0; 112 | device->index = 0; 113 | ret = SCSISIM_SUCCESS; 114 | } 115 | } 116 | else 117 | ret = SCSISIM_INVALID_FILE_DESCRIPTOR; 118 | 119 | return ret; 120 | } 121 | 122 | /** 123 | * For information about this function, see scsisim.h 124 | */ 125 | int scsisim_init_device(struct scsisim_dev *device) 126 | { 127 | int ret, i; 128 | unsigned int idVendor, idProduct; 129 | struct scsi_cmd my_cmd = { 0 }; 130 | uint8_t sense[32] = { 0 }; 131 | 132 | if (device == NULL) 133 | return SCSISIM_INVALID_PARAM; 134 | 135 | /* Obtain the USB vendor and product ID based on the device name */ 136 | if ((ret = usb_get_vendor_product(device, &idVendor, &idProduct)) != SCSISIM_SUCCESS) 137 | return ret; 138 | 139 | /* Make sure the attached device is a SIM card reader we support. 140 | We do NOT want to write data to some random, hapless device! */ 141 | if (usb_is_device_supported(device, idVendor, idProduct, supported_devices) == false) 142 | return SCSISIM_DEVICE_NOT_SUPPORTED; 143 | 144 | /* If we get this far, we have a supported SIM card reader. Now we can 145 | send 'magic' sequence of SCSI commands to get the device working */ 146 | for (i = 0; sim_devices[device->index].init_cmd[i].direction != SIM_NO_XFER; i++) 147 | { 148 | /* Set up the command block */ 149 | my_cmd.direction = sim_devices[device->index].init_cmd[i].direction; 150 | my_cmd.cdb = (uint8_t*)sim_devices[device->index].init_cmd[i].cdb; 151 | my_cmd.cdb_len = sim_devices[device->index].cdb_len; 152 | my_cmd.data = sim_devices[device->index].init_cmd[i].data; 153 | my_cmd.data_len = sim_devices[device->index].init_cmd[i].data_len; 154 | my_cmd.sense = sense; 155 | my_cmd.sense_len = (uint8_t)sizeof(sense); 156 | 157 | /* Send the commmand */ 158 | if ((ret = scsi_send_cdb(device, &my_cmd)) != SCSISIM_SUCCESS) 159 | { 160 | break; 161 | } 162 | } 163 | 164 | return ret; 165 | } 166 | 167 | /** 168 | * For information about this function, see scsisim.h 169 | */ 170 | int scsisim_select_file(const struct scsisim_dev *device, uint16_t file) 171 | { 172 | int ret; 173 | struct scsi_cmd my_cmd = { 0 }; 174 | uint8_t cdb[sim_devices[device->index].cdb_len]; 175 | uint8_t data[GSM_CMD_SELECT_DATA_LEN] = { 0 }; 176 | uint8_t sense[sim_devices[device->index].sense_len]; 177 | 178 | if (device == NULL) 179 | return SCSISIM_INVALID_PARAM; 180 | 181 | memset(sense, 0, sizeof(sense)); 182 | 183 | /* Get the base SELECT command for the current device */ 184 | memcpy(cdb, sim_devices[device->index].CDB_select_file, sizeof(cdb)); 185 | 186 | /* Set up the data block with the requested file ID */ 187 | data[0] = file >> 8; 188 | data[1] = file & 0xff; 189 | 190 | /* Set up the command block */ 191 | my_cmd.direction = SIM_WRITE; 192 | my_cmd.cdb = cdb; 193 | my_cmd.cdb_len = (uint8_t)sizeof(cdb); 194 | my_cmd.data = data; 195 | my_cmd.data_len = (uint8_t)sizeof(data); 196 | my_cmd.sense = sense; 197 | my_cmd.sense_len = (uint8_t)sizeof(sense); 198 | 199 | /* Send the command */ 200 | ret = scsi_send_cdb(device, &my_cmd); 201 | 202 | if (ret == SCSISIM_SUCCESS) 203 | { 204 | /* There should ALWAYS be sense data after selecting a file, 205 | * so we can find out how many bytes to request for GET RESPONSE */ 206 | if (my_cmd.sense_xfered) 207 | ret = sim_process_scsi_sense(device, my_cmd.sense, my_cmd.sense_xfered); 208 | else 209 | ret = SCSISIM_SCSI_NO_SENSE_DATA; 210 | } 211 | 212 | return ret; 213 | } 214 | 215 | /** 216 | * For information about this function, see scsisim.h 217 | */ 218 | int scsisim_get_response(const struct scsisim_dev *device, 219 | uint8_t *data, 220 | uint8_t len, 221 | int command, 222 | struct GSM_response *resp) 223 | { 224 | int ret; 225 | struct scsi_cmd my_cmd = { 0 }; 226 | uint8_t cdb[sim_devices[device->index].cdb_len]; 227 | uint8_t sense[sim_devices[device->index].sense_len]; 228 | 229 | if (device == NULL || data == NULL || len <= 0 || resp == NULL) 230 | return SCSISIM_INVALID_PARAM; 231 | 232 | memset(sense, 0, sizeof(sense)); 233 | 234 | /* Get the base GET RESPONSE command for the current device */ 235 | memcpy(cdb, sim_devices[device->index].CDB_get_response, sizeof(cdb)); 236 | 237 | /* Set the length */ 238 | cdb[sim_devices[device->index].get_response_len_offset] = len; 239 | 240 | /* Set up the command block */ 241 | my_cmd.direction = SIM_READ; 242 | my_cmd.cdb = cdb; 243 | my_cmd.cdb_len = (uint8_t)sizeof(cdb); 244 | my_cmd.data = data; 245 | my_cmd.data_len = len; 246 | my_cmd.sense = sense; 247 | my_cmd.sense_len = (uint8_t)sizeof(sense); 248 | 249 | resp->command = command; 250 | 251 | /* Send the command */ 252 | ret = scsi_send_cdb(device, &my_cmd); 253 | 254 | if (scsisim_verbose()) 255 | { 256 | if (my_cmd.data_xfered != len) 257 | scsisim_pinfo("%s: bytes transferred (%d) is less than data buffer length (%d)", 258 | __func__, my_cmd.data_xfered, len); 259 | } 260 | 261 | if (ret == SCSISIM_SUCCESS) 262 | { 263 | /* Parse out the GSM response data and fill in the resp struct */ 264 | if ((ret = gsm_parse_response(data, len, resp)) != SCSISIM_SUCCESS) 265 | { 266 | scsisim_perror("scsisim_get_response()", ret); 267 | } 268 | 269 | /* If there is sense data, process it and use it as the return code instead: */ 270 | if (my_cmd.sense_xfered) 271 | ret = sim_process_scsi_sense(device, my_cmd.sense, my_cmd.sense_xfered); 272 | } 273 | 274 | return ret; 275 | } 276 | 277 | /** 278 | * For information about this function, see scsisim.h 279 | */ 280 | int scsisim_select_file_and_get_response(const struct scsisim_dev *device, 281 | uint16_t file, 282 | uint8_t *data, 283 | uint8_t len, 284 | int command, 285 | struct GSM_response *resp) 286 | { 287 | int ret; 288 | 289 | if ((ret = scsisim_select_file(device, file)) > 0) 290 | { 291 | ret = scsisim_get_response(device, data, MIN(ret, len), command, resp); 292 | } 293 | 294 | return ret; 295 | } 296 | 297 | /** 298 | * For information about this function, see scsisim.h 299 | */ 300 | int scsisim_read_record(const struct scsisim_dev *device, 301 | uint8_t recno, 302 | uint8_t *data, 303 | uint8_t len) 304 | { 305 | int ret; 306 | struct scsi_cmd my_cmd = { 0 }; 307 | uint8_t cdb[sim_devices[device->index].cdb_len]; 308 | uint8_t sense[sim_devices[device->index].sense_len]; 309 | 310 | if (device == NULL || recno == 0 || data == NULL || len <= 0) 311 | return SCSISIM_INVALID_PARAM; 312 | 313 | memset(sense, 0, sizeof(sense)); 314 | 315 | /* Get the base READ RECORD command for the current device */ 316 | memcpy(cdb, sim_devices[device->index].CDB_read_record, sizeof(cdb)); 317 | 318 | /* Set the record number and length */ 319 | cdb[sim_devices[device->index].read_record_rec_offset] = recno; 320 | cdb[sim_devices[device->index].read_record_len_offset] = len; 321 | 322 | /* Set up the command block */ 323 | my_cmd.direction = SIM_READ; 324 | my_cmd.cdb = cdb; 325 | my_cmd.cdb_len = (uint8_t)sizeof(cdb); 326 | my_cmd.data = data; 327 | my_cmd.data_len = len; 328 | my_cmd.sense = sense; 329 | my_cmd.sense_len = (uint8_t)sizeof(sense); 330 | 331 | /* Send the command */ 332 | ret = scsi_send_cdb(device, &my_cmd); 333 | 334 | if (scsisim_verbose()) 335 | { 336 | if (my_cmd.data_xfered != len) 337 | scsisim_pinfo("%s: bytes transferred (%d) is less than data buffer length (%d)", 338 | __func__, my_cmd.data_xfered, len); 339 | } 340 | 341 | /* If there is sense data, process it and use it as the return code instead: */ 342 | if (my_cmd.sense_xfered) 343 | ret = sim_process_scsi_sense(device, my_cmd.sense, my_cmd.sense_xfered); 344 | 345 | return ret; 346 | } 347 | 348 | /** 349 | * For information about this function, see scsisim.h 350 | */ 351 | int scsisim_read_binary(const struct scsisim_dev *device, 352 | uint8_t *data, 353 | uint16_t offset, 354 | uint8_t len) 355 | { 356 | int ret; 357 | struct scsi_cmd my_cmd = { 0 }; 358 | uint8_t cdb[sim_devices[device->index].cdb_len]; 359 | uint8_t sense[sim_devices[device->index].sense_len]; 360 | 361 | if (device == NULL || data == NULL || len == 0) 362 | return SCSISIM_INVALID_PARAM; 363 | 364 | memset(sense, 0, sizeof(sense)); 365 | 366 | /* Get the base READ BINARY command for the current device */ 367 | memcpy(cdb, sim_devices[device->index].CDB_read_binary, sizeof(cdb)); 368 | 369 | /* Set the offsets and length */ 370 | cdb[sim_devices[device->index].read_binary_hi_offset] = offset >> 8; /* offset high */ 371 | cdb[sim_devices[device->index].read_binary_lo_offset] = offset & 0xff; /* offset low */ 372 | cdb[sim_devices[device->index].read_binary_len_offset] = len; 373 | 374 | /* Set up the command block */ 375 | my_cmd.direction = SIM_READ; 376 | my_cmd.cdb = cdb; 377 | my_cmd.cdb_len = (uint8_t)sizeof(cdb); 378 | my_cmd.data = data; 379 | my_cmd.data_len = len; 380 | my_cmd.sense = sense; 381 | my_cmd.sense_len = (uint8_t)sizeof(sense); 382 | 383 | /* Send the command */ 384 | ret = scsi_send_cdb(device, &my_cmd); 385 | 386 | if (scsisim_verbose()) 387 | { 388 | if (my_cmd.data_xfered != len) 389 | scsisim_pinfo("%s: bytes transferred (%d) is less than data buffer length (%d)", 390 | __func__, my_cmd.data_xfered, len); 391 | } 392 | 393 | /* If there is sense data, process it and use it as the return code instead: */ 394 | if (my_cmd.sense_xfered) 395 | ret = sim_process_scsi_sense(device, my_cmd.sense, my_cmd.sense_xfered); 396 | 397 | return ret; 398 | } 399 | 400 | /** 401 | * For information about this function, see scsisim.h 402 | */ 403 | int scsisim_update_record(const struct scsisim_dev *device, 404 | uint8_t recno, 405 | uint8_t *data, 406 | uint8_t len) 407 | { 408 | int ret; 409 | struct scsi_cmd my_cmd = { 0 }; 410 | uint8_t cdb[sim_devices[device->index].cdb_len]; 411 | uint8_t sense[sim_devices[device->index].sense_len]; 412 | 413 | if (device == NULL || recno == 0 || data == NULL || len <= 0) 414 | return SCSISIM_INVALID_PARAM; 415 | 416 | memset(sense, 0, sizeof(sense)); 417 | 418 | /* Get the base UPDATE RECORD command for the current device */ 419 | memcpy(cdb, sim_devices[device->index].CDB_update_record, sizeof(cdb)); 420 | 421 | /* Set the record number and length */ 422 | cdb[sim_devices[device->index].update_record_rec_offset] = recno; 423 | cdb[sim_devices[device->index].update_record_len_offset] = len; 424 | 425 | /* Set up the command block */ 426 | my_cmd.direction = SIM_WRITE; 427 | my_cmd.cdb = cdb; 428 | my_cmd.cdb_len = (uint8_t)sizeof(cdb); 429 | my_cmd.data = data; 430 | my_cmd.data_len = len; 431 | my_cmd.sense = sense; 432 | my_cmd.sense_len = (uint8_t)sizeof(sense); 433 | 434 | /* Send the command */ 435 | ret = scsi_send_cdb(device, &my_cmd); 436 | 437 | if (scsisim_verbose()) 438 | { 439 | if (my_cmd.data_xfered != len) 440 | scsisim_pinfo("%s: bytes transferred (%d) is less than data buffer length (%d)", 441 | __func__, my_cmd.data_xfered, len); 442 | } 443 | 444 | /* If there is sense data, process it and use it as the return code instead: */ 445 | if (my_cmd.sense_xfered) 446 | ret = sim_process_scsi_sense(device, my_cmd.sense, my_cmd.sense_xfered); 447 | 448 | return ret; 449 | } 450 | 451 | /** 452 | * For information about this function, see scsisim.h 453 | */ 454 | int scsisim_update_binary(const struct scsisim_dev *device, 455 | uint8_t *data, 456 | uint16_t offset, 457 | uint8_t len) 458 | { 459 | int ret; 460 | struct scsi_cmd my_cmd = { 0 }; 461 | uint8_t cdb[sim_devices[device->index].cdb_len]; 462 | uint8_t sense[sim_devices[device->index].sense_len]; 463 | 464 | if (device == NULL || data == NULL || len == 0) 465 | return SCSISIM_INVALID_PARAM; 466 | 467 | memset(sense, 0, sizeof(sense)); 468 | 469 | /* Get the base UPDATE BINARY command for the current device */ 470 | memcpy(cdb, sim_devices[device->index].CDB_update_binary, sizeof(cdb)); 471 | 472 | /* Set the offsets and length */ 473 | cdb[sim_devices[device->index].update_binary_hi_offset] = offset >> 8; /* offset high */ 474 | cdb[sim_devices[device->index].update_binary_lo_offset] = offset & 0xff; /* offset low */ 475 | cdb[sim_devices[device->index].update_binary_len_offset] = len; 476 | 477 | /* Set up the command block */ 478 | my_cmd.direction = SIM_WRITE; 479 | my_cmd.cdb = cdb; 480 | my_cmd.cdb_len = (uint8_t)sizeof(cdb); 481 | my_cmd.data = data; 482 | my_cmd.data_len = len; 483 | my_cmd.sense = sense; 484 | my_cmd.sense_len = (uint8_t)sizeof(sense); 485 | 486 | /* Send the command */ 487 | ret = scsi_send_cdb(device, &my_cmd); 488 | 489 | if (scsisim_verbose()) 490 | { 491 | if (my_cmd.data_xfered != len) 492 | scsisim_pinfo("%s: bytes transferred (%d) is less than data buffer length (%d)", 493 | __func__, my_cmd.data_xfered, len); 494 | } 495 | 496 | /* If there is sense data, process it and use it as the return code instead: */ 497 | if (my_cmd.sense_xfered) 498 | ret = sim_process_scsi_sense(device, my_cmd.sense, my_cmd.sense_xfered); 499 | 500 | return ret; 501 | } 502 | 503 | /** 504 | * For information about this function, see scsisim.h 505 | */ 506 | int scsisim_verify_chv(const struct scsisim_dev *device, 507 | uint8_t chv, 508 | const char *pin) 509 | { 510 | int ret; 511 | struct scsi_cmd my_cmd = { 0 }; 512 | uint8_t cdb[sim_devices[device->index].cdb_len]; 513 | uint8_t *data = NULL; 514 | uint8_t sense[sim_devices[device->index].sense_len]; 515 | 516 | if (device == NULL || pin == NULL) 517 | return SCSISIM_INVALID_PARAM; 518 | 519 | if (is_digit_string(pin) == false) 520 | return SCSISIM_INVALID_PIN; 521 | 522 | if (strlen(pin) > GSM_CMD_VERIFY_CHV_DATA_LEN) 523 | return SCSISIM_GSM_ERROR_PARAM_3; 524 | 525 | memset(sense, 0, sizeof(sense)); 526 | 527 | /* Get the base VERIFY CHV command for the current device */ 528 | memcpy(cdb, sim_devices[device->index].CDB_verify_chv, sizeof(cdb)); 529 | 530 | /* Set the CHV number */ 531 | cdb[sim_devices[device->index].verify_chv_chvnum_offset] = chv; 532 | 533 | if ((data = malloc(GSM_CMD_VERIFY_CHV_DATA_LEN)) == NULL) 534 | return SCSISIM_MEMORY_ALLOCATION_ERROR; 535 | 536 | /* Set up the data block with the specified PIN */ 537 | memset(data, 0xff, GSM_CMD_VERIFY_CHV_DATA_LEN); 538 | memcpy(data, pin, strlen(pin)); 539 | 540 | /* Set up the command block */ 541 | my_cmd.direction = SIM_WRITE; 542 | my_cmd.cdb = cdb; 543 | my_cmd.cdb_len = (uint8_t)sizeof(cdb); 544 | my_cmd.data = data; 545 | my_cmd.data_len = GSM_CMD_VERIFY_CHV_DATA_LEN; 546 | my_cmd.sense = sense; 547 | my_cmd.sense_len = (uint8_t)sizeof(sense); 548 | 549 | /* Send the command */ 550 | ret = scsi_send_cdb(device, &my_cmd); 551 | 552 | free(data); 553 | 554 | /* No sense data if successful; error condition = sense data */ 555 | if (my_cmd.sense_xfered) 556 | ret = sim_process_scsi_sense(device, my_cmd.sense, my_cmd.sense_xfered); 557 | 558 | return ret; 559 | } 560 | 561 | /** 562 | * For information about this function, see scsisim.h 563 | */ 564 | int scsisim_send_raw_command(const struct scsisim_dev *device, 565 | uint8_t direction, 566 | uint8_t command, 567 | uint8_t P1, 568 | uint8_t P2, 569 | uint8_t P3, 570 | uint8_t *data, 571 | unsigned int len) 572 | { 573 | int ret; 574 | struct scsi_cmd my_cmd = { 0 }; 575 | uint8_t cdb[sim_devices[device->index].cdb_len]; 576 | uint8_t sense[sim_devices[device->index].sense_len]; 577 | 578 | if (device == NULL) 579 | return SCSISIM_INVALID_PARAM; 580 | 581 | memset(sense, 0, sizeof(sense)); 582 | 583 | /* Get the base raw command for the current device */ 584 | memcpy(cdb, sim_devices[device->index].CDB_raw_cmd, sizeof(cdb)); 585 | 586 | /* Set the command parameters */ 587 | cdb[sim_devices[device->index].raw_cmd_direction_offset] = 588 | (direction == SIM_WRITE) ? sim_devices[device->index].scsi_cmd_write : sim_devices[device->index].scsi_cmd_read; 589 | cdb[sim_devices[device->index].raw_cmd_gsm_cmd_offset] = command; 590 | cdb[sim_devices[device->index].raw_cmd_p1_offset] = P1; 591 | cdb[sim_devices[device->index].raw_cmd_p2_offset] = P2; 592 | cdb[sim_devices[device->index].raw_cmd_p3_offset] = P3; 593 | 594 | /* Set up the command block */ 595 | my_cmd.direction = direction; 596 | my_cmd.cdb = cdb; 597 | my_cmd.cdb_len = (uint8_t)sizeof(cdb); 598 | my_cmd.data = data; 599 | my_cmd.data_len = len; 600 | my_cmd.sense = sense; 601 | my_cmd.sense_len = (uint8_t)sizeof(sense); 602 | 603 | /* Send the command */ 604 | ret = scsi_send_cdb(device, &my_cmd); 605 | 606 | if (scsisim_verbose()) 607 | { 608 | if (my_cmd.data_xfered != len) 609 | scsisim_pinfo("%s: bytes transferred (%d) is less than data buffer length (%d)", 610 | __func__, my_cmd.data_xfered, len); 611 | } 612 | 613 | /* If there is sense data, process it and use it as the return code instead: */ 614 | if (my_cmd.sense_xfered) 615 | ret = sim_process_scsi_sense(device, my_cmd.sense, my_cmd.sense_xfered); 616 | 617 | return ret; 618 | } 619 | 620 | /** 621 | * Function: sim_free_device_name 622 | * 623 | * Parameters: 624 | * device: Pointer to scsisim_dev struct. 625 | * 626 | * Description: 627 | * Given a pointer to an scsisim_dev struct, this function does all of the 628 | * necessary memory cleanup. 629 | * 630 | * Return values: 631 | * None 632 | */ 633 | static inline void sim_free_device_name(struct scsisim_dev *device) 634 | { 635 | free(device->name); 636 | device->name = NULL; 637 | } 638 | 639 | /** 640 | * Function: sim_process_scsi_sense 641 | * 642 | * Parameters: 643 | * device: Pointer to scsisim_dev struct. 644 | * sense: Sense buffer. 645 | * len: Length of sense buffer. 646 | * 647 | * Description: 648 | * Given a buffer of SCSI sense data, parse out the correct return value. 649 | * See GSM TS 100 977, section 9.4 for status conditions returned by SIM. 650 | * 651 | * Return values: 652 | * SCSISIM_SUCCESS 653 | * SCSISIM_SCSI_NO_SENSE_DATA 654 | * SCSISIM_SCSI_UNKNOWN_SENSE_DATA 655 | * Number of bytes in response data 656 | * SCSISIM_GSM_* error code 657 | */ 658 | static int sim_process_scsi_sense(const struct scsisim_dev *device, 659 | const uint8_t *sense, 660 | unsigned int len) 661 | { 662 | int ret; 663 | 664 | if (len < sim_devices[device->index].sense_ascq_offset + 1u) 665 | return SCSISIM_SCSI_NO_SENSE_DATA; 666 | 667 | /* 0x70 = Fixed format, current sense. See SCSI spec for more info */ 668 | if (sense[sim_devices[device->index].sense_type_offset] != 0x70) 669 | return SCSISIM_SCSI_UNKNOWN_SENSE_DATA; 670 | 671 | /* Examine the ASC (additional sense code). This corresponds to 672 | * 'SW1' (status word 1) in the GSM spec. */ 673 | switch (sense[sim_devices[device->index].sense_asc_offset]) 674 | { 675 | case 0x67: 676 | ret = SCSISIM_GSM_ERROR_PARAM_3; 677 | break; 678 | case 0x6b: 679 | ret = SCSISIM_GSM_ERROR_PARAM_1_OR_2; 680 | break; 681 | case 0x6d: 682 | ret = SCSISIM_GSM_UNKNOWN_INSTRUCTION; 683 | break; 684 | case 0x6e: 685 | ret = SCSISIM_GSM_WRONG_INSTRUCTION_CLASS; 686 | break; 687 | case 0x6f: 688 | ret = SCSISIM_GSM_TECHNICAL_PROBLEM; 689 | break; 690 | case 0x90: /* "Responses to commands which are correctly 691 | executed" */ 692 | /* Examine the ASCQ (additional sense code qualifier). 693 | * This corresponds to 'SW2' (status word 2) in the 694 | * GSM spec. */ 695 | switch (sense[sim_devices[device->index].sense_ascq_offset]) 696 | { 697 | case 0x00: 698 | ret = SCSISIM_SUCCESS; 699 | break; 700 | default: 701 | ret = SCSISIM_GSM_UNKNOWN_SW2; 702 | break; 703 | } 704 | break; 705 | case 0x92: /* "Memory management" */ 706 | /* Examine the ASCQ (additional sense code qualifier). 707 | * This corresponds to 'SW2' (status word 2) in the 708 | * GSM spec. */ 709 | switch (sense[sim_devices[device->index].sense_ascq_offset]) 710 | { 711 | case 0x40: 712 | ret = SCSISIM_GSM_MEMORY_ERROR; 713 | break; 714 | default: 715 | /* According to the spec: "Command 716 | * successful but after using an 717 | * internal update retry routine." */ 718 | ret = SCSISIM_SUCCESS; 719 | break; 720 | } 721 | break; 722 | case 0x93: /* "Responses to commands which are postponed" */ 723 | ret = SCSISIM_GSM_BUSY; 724 | break; 725 | case 0x94: /* "Referencing management" */ 726 | /* Examine the ASCQ (additional sense code qualifier). 727 | * This corresponds to 'SW2' (status word 2) in the 728 | * GSM spec. */ 729 | switch (sense[sim_devices[device->index].sense_ascq_offset]) 730 | { 731 | case 0x00: 732 | ret = SCSISIM_GSM_NO_EF_SELECTED; 733 | break; 734 | case 0x02: 735 | ret = SCSISIM_GSM_INVALID_ADDRESS; 736 | break; 737 | case 0x04: 738 | ret = SCSISIM_GSM_FILE_NOT_FOUND; 739 | break; 740 | case 0x08: 741 | ret = SCSISIM_GSM_FILE_INCONSISTENT_WITH_COMMAND; 742 | break; 743 | default: 744 | ret = SCSISIM_GSM_UNKNOWN_SW2; /* unknown ASCQ */ 745 | break; 746 | } 747 | break; 748 | case 0x98: /* "Security management" */ 749 | /* See GSM spec section 9.4.5 for specific codes */ 750 | switch (sense[sim_devices[device->index].sense_ascq_offset]) 751 | { 752 | case 0x02: 753 | ret = SCSISIM_GSM_NO_CHV_INITIALIZED; 754 | break; 755 | case 0x04: 756 | ret = SCSISIM_GSM_CHV_VERIFICATION_FAILED; 757 | break; 758 | case 0x08: 759 | ret = SCSISIM_GSM_CHV_STATUS_CONTRADICTION; 760 | break; 761 | case 0x10: 762 | ret = SCSISIM_GSM_INVALIDATION_STATUS_CONTRADICTION; 763 | break; 764 | case 0x40: 765 | ret = SCSISIM_GSM_CHV_BLOCKED; 766 | break; 767 | case 0x50: 768 | ret = SCSISIM_GSM_INCREASE_FAILED; 769 | break; 770 | default: 771 | ret = SCSISIM_GSM_SECURITY_ERROR; 772 | break; 773 | } 774 | break; 775 | case 0x91: /* Command for ME (Mobile Equipment) */ 776 | case 0x9e: /* SIM data download error */ 777 | case 0x9f: /* Normal response data: return the number of 778 | bytes that should be read in GET RESPONSE */ 779 | ret = sense[sim_devices[device->index].sense_ascq_offset]; 780 | break; 781 | default: 782 | if (scsisim_verbose()) 783 | scsisim_pinfo("%s: unknown GSM Status Word 1 (%d); Status Word 2 = %d", 784 | __func__, 785 | sense[sim_devices[device->index].sense_asc_offset], 786 | sense[sim_devices[device->index].sense_ascq_offset]); 787 | ret = SCSISIM_GSM_UNKNOWN_SW1; 788 | break; 789 | } 790 | 791 | if (scsisim_verbose()) 792 | { 793 | if (ret <= 0) 794 | scsisim_pinfo("%s: returning %d (%s)", __func__, ret, scsisim_strerror(ret)); 795 | else 796 | scsisim_pinfo("%s: returning %d", __func__, ret); 797 | } 798 | 799 | return ret; 800 | } 801 | 802 | /* EOF */ 803 | 804 | -------------------------------------------------------------------------------- /include/device.h: -------------------------------------------------------------------------------- 1 | /* 2 | * device.h 3 | * Device-specific definitions for the scsisim library. If you want 4 | * to add support for a new device, this is the place to do it. 5 | * 6 | * This is an internal interface file for the scsisim library. 7 | * 8 | * Copyright (c) 2017, Chris Coffey 9 | * 10 | * Permission to use, copy, modify, and/or distribute this software 11 | * for any purpose with or without fee is hereby granted, provided 12 | * that the above copyright notice and this permission notice appear 13 | * in all copies. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 16 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 18 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 19 | * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA 20 | * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 21 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 22 | * PERFORMANCE OF THIS SOFTWARE. 23 | */ 24 | 25 | #ifndef __SCSISIM_DEVICE_H__ 26 | #define __SCSISIM_DEVICE_H__ 27 | 28 | #include 29 | #include 30 | 31 | #include "gsm.h" 32 | 33 | 34 | /* List of supported SIM card readers. Arranged by USB vendor ID, product ID, 35 | * and index into "sim_devices" struct defined further below. */ 36 | static const unsigned int supported_devices[][3] = { 37 | { 0x0420, 0x1307, 0 }, /* "Chips and Technologies Celly SIM Card Reader" */ 38 | /* Add future devices here */ 39 | { 0, 0, 0 } 40 | }; 41 | 42 | /* SCSI READ/WRITE operation codes; see SCSI spec for details */ 43 | #define SCSI_CMD_READ_6 0x08 /* READ (6) */ 44 | #define SCSI_CMD_WRITE_6 0x0a /* WRITE (6) */ 45 | #define SCSI_CMD_READ_10 0x28 /* READ (10) */ 46 | #define SCSI_CMD_WRITE_10 0x2a /* WRITE (10) */ 47 | #define SCSI_CMD_READ_12 0xa8 /* READ (12) */ 48 | #define SCSI_CMD_WRITE_12 0xaa /* WRITE (12) */ 49 | #define SCSI_CMD_READ_16 0x88 /* READ (16) */ 50 | #define SCSI_CMD_WRITE_16 0x8a /* WRITE (16) */ 51 | 52 | #define MAX_CDB_LEN 16 /* Support SCSI READ/WRITE CDBs up to 16 bytes long */ 53 | 54 | /* Celly SIM Card Reader: 55 | * Device initialization data - write buffer 1 */ 56 | static const uint8_t celly_buf_1[512] = { 57 | 0x5a, 0x5a, 0x5a, 0x5a, 0xbe, 0x54, 0x62, 0x0d, 0x00, 0x00, 0x00, 0x00, 58 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 59 | 0x01, 0x00, 0xc9, 0xee, 0xdb, 0xda, 0xca, 0xd0, 0xd0, 0xbe, 0xb0, 0xee, 60 | 0xce, 0xa2, 0xb5, 0xe7, 0xd7, 0xd3, 0xd3, 0xd0, 0xcf, 0xde, 0xb9, 0xab, 61 | 0xcb, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x6c, 62 | 0x61, 0x73, 0x68, 0x20, 0x44, 0x69, 0x73, 0x6b, 0x00, 0x00, 0x00, 0x00, 63 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 64 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 65 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 66 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 67 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 68 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 69 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 70 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 71 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x25, 0x06, 72 | 0x43, 0x68, 0x69, 0x70, 0x73, 0x42, 0x6e, 0x6b, 0x46, 0x6c, 0x61, 0x73, 73 | 0x68, 0x20, 0x44, 0x69, 0x73, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 74 | 0x00, 0x00, 0x00, 0x00, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 75 | 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x21, 0x00, 0x42, 0x00, 76 | 0x00, 0x00, 0x1f, 0x00, 0x04, 0x80, 0x00, 0x01, 0x00, 0xe8, 0x03, 0x00, 77 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x03, 0x00, 78 | 0x00, 0xe8, 0x03, 0x00, 0x40, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x01, 79 | 0x02, 0x03, 0x04, 0x00, 0xff, 0x00, 0x08, 0x00, 0x84, 0x01, 0x02, 0x02, 80 | 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x02, 0x01, 0xfd, 0x00, 0x00, 0x01, 81 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 82 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 83 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 84 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 85 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 86 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 87 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 88 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 89 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 90 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 91 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 92 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 93 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 94 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 95 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 96 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 97 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 98 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 99 | 0x68, 0x69, 0x70, 0x73, 0x42, 0x61, 0x6e, 0x6b 100 | }; 101 | 102 | /* Celly SIM Card Reader: 103 | * Device initialization data - write buffer 2 */ 104 | static const uint8_t celly_buf_2[4096] = { 105 | 0x06, 0x58, 0x60, 0x00, 0x06, 0x00, 0x68, 0x00, 0x06, 0x58, 0x60, 0x00, 106 | 0x06, 0x58, 0x60, 0x00, 0x06, 0xf8, 0x67, 0x00, 0x06, 0xf8, 0x67, 0x00, 107 | 0x06, 0x38, 0x67, 0x00, 0x06, 0xf8, 0x67, 0x00, 0x06, 0xf8, 0x67, 0x00, 108 | 0x06, 0xf8, 0x67, 0x00, 0x06, 0xf8, 0x67, 0x00, 0x06, 0xf8, 0x67, 0x00, 109 | 0x06, 0xf8, 0x67, 0x00, 0x06, 0xf8, 0x67, 0x00, 0x06, 0xf8, 0x67, 0x00, 110 | 0x06, 0x94, 0x67, 0x00, 0x06, 0xf8, 0x67, 0x00, 0x06, 0xf8, 0x67, 0x00, 111 | 0x06, 0xf8, 0x67, 0x00, 0x06, 0xf8, 0x67, 0x00, 0x06, 0xf8, 0x67, 0x00, 112 | 0x06, 0xf8, 0x67, 0x00, 0x11, 0x00, 0x04, 0x00, 0x17, 0xa0, 0x02, 0x02, 113 | 0x11, 0x00, 0x00, 0x00, 0x17, 0x60, 0x02, 0x01, 0x10, 0xfc, 0xff, 0x03, 114 | 0x11, 0xfc, 0xff, 0x03, 0x1b, 0xb8, 0x02, 0x00, 0x90, 0x00, 0x04, 0x00, 115 | 0x50, 0x00, 0x02, 0x00, 0x03, 0x48, 0x5b, 0x00, 0xdc, 0xd8, 0x02, 0x00, 116 | 0x10, 0xfc, 0xff, 0x03, 0x11, 0x3c, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 117 | 0x1b, 0xdc, 0x02, 0x00, 0xd6, 0x00, 0x00, 0x00, 0x2a, 0x00, 0xc0, 0x03, 118 | 0x2a, 0x00, 0x00, 0x02, 0x50, 0x1c, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 119 | 0x03, 0x28, 0x51, 0x00, 0x5c, 0x30, 0x02, 0x00, 0x70, 0x14, 0x00, 0x00, 120 | 0x0b, 0xc8, 0x60, 0x00, 0x30, 0x00, 0x00, 0x00, 0x0c, 0xc8, 0x60, 0x00, 121 | 0x27, 0x00, 0x00, 0x00, 0x17, 0xac, 0x42, 0x00, 0x18, 0xd8, 0x02, 0x02, 122 | 0x2a, 0x00, 0x80, 0x00, 0x50, 0x0c, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 123 | 0x29, 0x00, 0x00, 0x02, 0x17, 0xd8, 0x82, 0x00, 0x18, 0xd8, 0x02, 0x01, 124 | 0x2a, 0x00, 0x00, 0x01, 0x50, 0x1c, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 125 | 0x17, 0xd8, 0x42, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x07, 0x60, 0x63, 0x00, 126 | 0x2c, 0x04, 0x00, 0x00, 0x07, 0x3c, 0x61, 0x00, 0x2c, 0x14, 0x00, 0x00, 127 | 0x07, 0x1c, 0x61, 0x00, 0x18, 0x20, 0xc2, 0x00, 0x17, 0xec, 0xc2, 0x00, 128 | 0x03, 0x78, 0x65, 0x00, 0x06, 0xc4, 0x61, 0x00, 0x03, 0xd4, 0x61, 0x00, 129 | 0x10, 0xfc, 0xff, 0x03, 0x11, 0xfc, 0xff, 0x03, 0x1b, 0x90, 0x02, 0x00, 130 | 0x18, 0x20, 0xc2, 0x00, 0x17, 0xec, 0xc2, 0x00, 0x03, 0x78, 0x65, 0x00, 131 | 0x06, 0xc8, 0x61, 0x00, 0x03, 0x9c, 0x63, 0x00, 0x18, 0x20, 0xc2, 0x00, 132 | 0x17, 0xec, 0xc2, 0x00, 0x1c, 0xd8, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 133 | 0x10, 0x14, 0x00, 0x00, 0x17, 0xd8, 0x42, 0x00, 0x03, 0x78, 0x65, 0x00, 134 | 0x02, 0x00, 0x00, 0x00, 0x1b, 0xd8, 0x02, 0x00, 0x18, 0xd8, 0x82, 0x00, 135 | 0x2c, 0x00, 0x00, 0x00, 0x07, 0xa0, 0x61, 0x00, 0x2c, 0x04, 0x00, 0x00, 136 | 0x07, 0xac, 0x61, 0x00, 0xb1, 0x19, 0x00, 0x00, 0x03, 0x64, 0x62, 0x00, 137 | 0x03, 0x78, 0x62, 0x00, 0x03, 0x8c, 0x62, 0x00, 0x03, 0x94, 0x62, 0x00, 138 | 0xb2, 0x19, 0x00, 0x00, 0x03, 0x64, 0x62, 0x00, 0x03, 0x54, 0x62, 0x00, 139 | 0xb1, 0x19, 0x00, 0x00, 0x06, 0xc4, 0x61, 0x00, 0x03, 0x64, 0x62, 0x00, 140 | 0x03, 0x78, 0x62, 0x00, 0x06, 0xc4, 0x61, 0x00, 0xb1, 0x19, 0x00, 0x00, 141 | 0x03, 0x8c, 0x62, 0x00, 0x03, 0x94, 0x62, 0x00, 0xb2, 0x19, 0x00, 0x00, 142 | 0x03, 0x8c, 0x62, 0x00, 0xb1, 0x19, 0x00, 0x00, 0x03, 0xd4, 0x61, 0x00, 143 | 0x03, 0x34, 0x63, 0x00, 0x91, 0x00, 0x08, 0x00, 0x06, 0x1c, 0x57, 0x00, 144 | 0x03, 0x34, 0x62, 0x00, 0x16, 0x04, 0x00, 0x00, 0x9c, 0xdc, 0x02, 0x00, 145 | 0x18, 0x20, 0xc2, 0x00, 0xa3, 0x00, 0x00, 0x00, 0x16, 0x08, 0x00, 0x00, 146 | 0x41, 0x00, 0x00, 0x00, 0x03, 0x24, 0x14, 0x00, 0x18, 0x20, 0xc2, 0x00, 147 | 0xa1, 0x00, 0x00, 0x00, 0x16, 0x08, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 148 | 0x0d, 0xec, 0x61, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0xc0, 0x01, 0x00, 149 | 0x06, 0x18, 0x62, 0x00, 0x10, 0xc4, 0x01, 0x00, 0x50, 0x28, 0x00, 0x00, 150 | 0x03, 0xa0, 0x21, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x50, 0x28, 0x00, 0x00, 151 | 0x03, 0xa0, 0x21, 0x00, 0x1c, 0x00, 0x48, 0x00, 0x04, 0x00, 0x00, 0x00, 152 | 0x18, 0x98, 0x81, 0x00, 0x29, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 153 | 0x10, 0xfc, 0xff, 0x03, 0x11, 0xfc, 0xff, 0x03, 0x90, 0x10, 0x06, 0x00, 154 | 0x50, 0x10, 0x02, 0x00, 0x06, 0x4c, 0x5b, 0x00, 0x1c, 0x84, 0x01, 0x00, 155 | 0x1b, 0x90, 0x02, 0x00, 0x03, 0x48, 0x62, 0x00, 0x06, 0x98, 0x62, 0x00, 156 | 0x10, 0x54, 0x55, 0x01, 0x11, 0x54, 0x55, 0x01, 0x1b, 0x90, 0x02, 0x00, 157 | 0x03, 0x48, 0x62, 0x00, 0x06, 0x98, 0x62, 0x00, 0x10, 0xa8, 0xaa, 0x02, 158 | 0x11, 0xa8, 0xaa, 0x02, 0x1b, 0x90, 0x02, 0x00, 0x03, 0x48, 0x62, 0x00, 159 | 0x06, 0x98, 0x62, 0x00, 0x03, 0xac, 0x62, 0x00, 0x06, 0x98, 0x62, 0x00, 160 | 0x03, 0xf0, 0x62, 0x00, 0x03, 0x9c, 0x63, 0x00, 0x03, 0xa8, 0x64, 0x00, 161 | 0x18, 0x20, 0xc2, 0x00, 0x17, 0xec, 0xc2, 0x00, 0x06, 0x78, 0x65, 0x00, 162 | 0x90, 0x10, 0x06, 0x00, 0x91, 0x00, 0x00, 0x00, 0x10, 0x00, 0x04, 0x00, 163 | 0x11, 0x08, 0x0c, 0x00, 0xd0, 0x10, 0x10, 0x00, 0xd1, 0x10, 0x10, 0x00, 164 | 0x50, 0x10, 0x02, 0x00, 0x1d, 0x08, 0x00, 0x00, 0x2c, 0xf0, 0xf7, 0x03, 165 | 0x08, 0xe0, 0x62, 0x00, 0x10, 0x00, 0x04, 0x00, 0x11, 0x08, 0x0c, 0x00, 166 | 0x06, 0xe4, 0x62, 0x00, 0xe1, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 167 | 0x0d, 0xc8, 0x62, 0x00, 0x04, 0x00, 0x00, 0x00, 0x90, 0x10, 0x06, 0x00, 168 | 0x91, 0x00, 0x00, 0x00, 0x10, 0xfc, 0xfb, 0x03, 0x11, 0xf4, 0xf3, 0x03, 169 | 0xd0, 0x10, 0x10, 0x00, 0xd1, 0x10, 0x10, 0x00, 0x50, 0x10, 0x02, 0x00, 170 | 0x1d, 0x08, 0x00, 0x00, 0x2c, 0x0c, 0x08, 0x00, 0x08, 0x24, 0x63, 0x00, 171 | 0x10, 0xfc, 0xfb, 0x03, 0x11, 0xf4, 0xf3, 0x03, 0x06, 0x28, 0x63, 0x00, 172 | 0xe2, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x0d, 0x0c, 0x63, 0x00, 173 | 0x04, 0x00, 0x00, 0x00, 0x50, 0x00, 0x02, 0x00, 0x90, 0x00, 0x04, 0x00, 174 | 0x91, 0x00, 0x00, 0x00, 0xd0, 0xfc, 0xff, 0x03, 0xd1, 0xfc, 0xff, 0x03, 175 | 0x1e, 0x08, 0x00, 0x00, 0xe6, 0x00, 0x00, 0x00, 0x1d, 0x08, 0x00, 0x00, 176 | 0xa7, 0x00, 0x00, 0x00, 0x0d, 0x48, 0x63, 0x00, 0x04, 0x00, 0x00, 0x00, 177 | 0x18, 0xd8, 0x82, 0x00, 0x17, 0x6c, 0x42, 0x00, 0x03, 0x7c, 0x63, 0x00, 178 | 0x37, 0x04, 0x00, 0x00, 0x1b, 0x00, 0x04, 0x00, 0x91, 0x00, 0x08, 0x00, 179 | 0x06, 0x1c, 0x57, 0x00, 0x10, 0x40, 0x02, 0x00, 0x3d, 0x00, 0x00, 0x00, 180 | 0x50, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3e, 0x04, 0x00, 0x00, 181 | 0x50, 0x28, 0x00, 0x00, 0x03, 0xa0, 0x21, 0x00, 0x06, 0xe8, 0x15, 0x00, 182 | 0x1c, 0xdc, 0x02, 0x00, 0xd8, 0x20, 0xc2, 0x00, 0xe3, 0x00, 0x00, 0x00, 183 | 0x16, 0x08, 0x00, 0x00, 0x50, 0x02, 0x04, 0x00, 0x90, 0x02, 0x00, 0x00, 184 | 0x03, 0x34, 0x62, 0x00, 0x16, 0x04, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 185 | 0x20, 0x00, 0x00, 0x00, 0x2c, 0xfc, 0xff, 0x03, 0x07, 0xe8, 0x63, 0x00, 186 | 0x03, 0x24, 0x14, 0x00, 0x03, 0x0c, 0x62, 0x00, 0x30, 0x60, 0x00, 0x00, 187 | 0x0b, 0xe8, 0x63, 0x00, 0x10, 0xfc, 0xff, 0x03, 0x1f, 0x00, 0x00, 0x00, 188 | 0x03, 0x54, 0x1b, 0x00, 0xa7, 0x02, 0x00, 0x00, 0x18, 0x20, 0xc2, 0x00, 189 | 0xa1, 0x00, 0x00, 0x00, 0x16, 0x08, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 190 | 0x0d, 0xbc, 0x63, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9c, 0xdc, 0x02, 0x00, 191 | 0x18, 0x20, 0xc2, 0x00, 0xa3, 0x00, 0x00, 0x00, 0x16, 0x10, 0x00, 0x00, 192 | 0x50, 0x02, 0x04, 0x00, 0x90, 0x02, 0x00, 0x00, 0x03, 0x34, 0x62, 0x00, 193 | 0x16, 0x04, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 194 | 0x2c, 0x00, 0x00, 0x00, 0x07, 0x44, 0x64, 0x00, 0x18, 0x20, 0xc2, 0x00, 195 | 0x21, 0x01, 0x00, 0x00, 0x16, 0x10, 0x00, 0x00, 0x06, 0x48, 0x64, 0x00, 196 | 0x03, 0x58, 0x64, 0x00, 0xa7, 0x02, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 197 | 0x0d, 0x24, 0x64, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x54, 0x1b, 0x00, 198 | 0x58, 0x20, 0xc2, 0x00, 0x41, 0x00, 0x00, 0x00, 0x51, 0x10, 0x06, 0x00, 199 | 0x03, 0x8c, 0x14, 0x00, 0x27, 0x01, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 200 | 0x6c, 0x04, 0x00, 0x00, 0x07, 0x94, 0x64, 0x00, 0x18, 0x38, 0x82, 0x00, 201 | 0xb0, 0x59, 0x00, 0x00, 0x0b, 0x80, 0x64, 0x00, 0x3d, 0x00, 0x00, 0x00, 202 | 0x68, 0x00, 0x00, 0x00, 0x06, 0x60, 0x64, 0x00, 0x10, 0x40, 0x00, 0x00, 203 | 0xb0, 0x59, 0x00, 0x00, 0x0b, 0x98, 0x64, 0x00, 0x3d, 0x00, 0x00, 0x00, 204 | 0x04, 0x00, 0x00, 0x00, 0x1c, 0xdd, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 205 | 0x18, 0x20, 0xc2, 0x00, 0x23, 0x01, 0x00, 0x00, 0x1b, 0xdc, 0x02, 0x00, 206 | 0x16, 0x10, 0x00, 0x00, 0x50, 0x02, 0x04, 0x00, 0x90, 0x02, 0x00, 0x00, 207 | 0x03, 0x34, 0x62, 0x00, 0x16, 0x04, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 208 | 0x03, 0x04, 0x65, 0x00, 0x1c, 0xdd, 0x02, 0x00, 0x18, 0x20, 0xc2, 0x00, 209 | 0x21, 0x01, 0x00, 0x00, 0x1b, 0xdc, 0x02, 0x00, 0x16, 0x10, 0x00, 0x00, 210 | 0xa7, 0x02, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x0d, 0xd0, 0x64, 0x00, 211 | 0x02, 0x01, 0x00, 0x00, 0x1b, 0xdd, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 212 | 0x20, 0x00, 0x00, 0x00, 0x2c, 0xfc, 0xff, 0x03, 0x07, 0x3c, 0x5b, 0x00, 213 | 0x96, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0xac, 0x42, 0x00, 214 | 0x95, 0x01, 0x00, 0x00, 0x98, 0x22, 0xc2, 0x00, 0x1c, 0x10, 0x08, 0x00, 215 | 0x1b, 0xb8, 0x01, 0x00, 0x1c, 0x14, 0x08, 0x00, 0x1b, 0xbc, 0x01, 0x00, 216 | 0xd0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x1a, 0x00, 0x51, 0x10, 0x06, 0x00, 217 | 0x03, 0x2c, 0x1a, 0x00, 0x03, 0x40, 0x1a, 0x00, 0x27, 0x01, 0x00, 0x00, 218 | 0xac, 0x06, 0x00, 0x00, 0x07, 0x64, 0x65, 0x00, 0x18, 0x38, 0x82, 0x00, 219 | 0x3d, 0x00, 0x00, 0x00, 0xa8, 0x02, 0x00, 0x00, 0x06, 0x34, 0x65, 0x00, 220 | 0x10, 0x40, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 221 | 0x16, 0x28, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1c, 0xdd, 0x02, 0x00, 222 | 0x01, 0x01, 0x00, 0x00, 0x18, 0x20, 0xc2, 0x00, 0x23, 0x01, 0x00, 0x00, 223 | 0x1b, 0xdc, 0x02, 0x00, 0x16, 0x10, 0x00, 0x00, 0x50, 0x02, 0x04, 0x00, 224 | 0x90, 0x02, 0x00, 0x00, 0x03, 0x34, 0x62, 0x00, 0x16, 0x04, 0x00, 0x00, 225 | 0x41, 0x00, 0x00, 0x00, 0x03, 0xd4, 0x65, 0x00, 0x1c, 0xdd, 0x02, 0x00, 226 | 0x18, 0x20, 0xc2, 0x00, 0x21, 0x01, 0x00, 0x00, 0x1b, 0xdc, 0x02, 0x00, 227 | 0x16, 0x10, 0x00, 0x00, 0xa7, 0x02, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 228 | 0x0d, 0xa0, 0x65, 0x00, 0x02, 0x01, 0x00, 0x00, 0x1b, 0xdd, 0x02, 0x00, 229 | 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x2c, 0xfc, 0xff, 0x03, 230 | 0x07, 0x3c, 0x5b, 0x00, 0x18, 0xec, 0xc2, 0x00, 0x1b, 0x78, 0x02, 0x00, 231 | 0x18, 0xd8, 0x42, 0x00, 0x2c, 0x08, 0x00, 0x00, 0x0a, 0x3c, 0x66, 0x00, 232 | 0x1b, 0xa9, 0x01, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x18, 0xac, 0x42, 0x00, 233 | 0x95, 0x01, 0x00, 0x00, 0x03, 0x4c, 0x16, 0x00, 0x51, 0x20, 0x08, 0x00, 234 | 0x03, 0x6c, 0x16, 0x00, 0x03, 0xa0, 0x66, 0x00, 0x18, 0x6d, 0x42, 0x00, 235 | 0x2b, 0x10, 0x00, 0x00, 0x0a, 0x94, 0x66, 0x00, 0x1c, 0x78, 0x02, 0x00, 236 | 0x28, 0x00, 0x00, 0x00, 0x0b, 0x3c, 0x5b, 0x00, 0x1b, 0x78, 0x02, 0x00, 237 | 0x1c, 0xa9, 0x01, 0x00, 0x27, 0x01, 0x00, 0x00, 0x06, 0xf4, 0x65, 0x00, 238 | 0xd0, 0x00, 0x00, 0x00, 0x18, 0xac, 0x42, 0x00, 0x95, 0x01, 0x00, 0x00, 239 | 0x03, 0x4c, 0x16, 0x00, 0x03, 0x9c, 0x21, 0x00, 0x90, 0xfc, 0xff, 0x03, 240 | 0x91, 0xfc, 0xff, 0x03, 0x50, 0x10, 0x02, 0x00, 0xb0, 0x31, 0x00, 0x00, 241 | 0x0b, 0x5c, 0x66, 0x00, 0x03, 0xe8, 0x15, 0x00, 0x2b, 0x08, 0x00, 0x00, 242 | 0x08, 0x94, 0x66, 0x00, 0x0d, 0x64, 0x66, 0x00, 0x26, 0x00, 0x00, 0x00, 243 | 0x95, 0x01, 0x00, 0x00, 0x1c, 0x78, 0x02, 0x00, 0x28, 0x00, 0x00, 0x00, 244 | 0x0b, 0x3c, 0x5b, 0x00, 0x1b, 0x78, 0x02, 0x00, 0x27, 0x01, 0x00, 0x00, 245 | 0x06, 0x3c, 0x66, 0x00, 0x10, 0xfc, 0xff, 0x03, 0x1f, 0x00, 0x00, 0x00, 246 | 0x04, 0x00, 0x00, 0x00, 0x10, 0x20, 0x08, 0x00, 0x17, 0xe4, 0xc2, 0x00, 247 | 0x51, 0xc8, 0x01, 0x00, 0x50, 0x08, 0x00, 0x00, 0xb0, 0x59, 0x00, 0x00, 248 | 0x0b, 0xb0, 0x66, 0x00, 0x78, 0x00, 0x00, 0x00, 0xb0, 0x59, 0x00, 0x00, 249 | 0x0b, 0xbc, 0x66, 0x00, 0xb2, 0x49, 0x00, 0x00, 0xb1, 0x51, 0x00, 0x00, 250 | 0x03, 0xe8, 0x15, 0x00, 0x16, 0x08, 0x00, 0x00, 0x03, 0xe8, 0x15, 0x00, 251 | 0x34, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 252 | 0x34, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 253 | 0xb2, 0x51, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x36, 0x09, 0x00, 0x00, 254 | 0x30, 0x01, 0x00, 0x00, 0x0b, 0xe0, 0x16, 0x00, 0x35, 0x00, 0x00, 0x00, 255 | 0x36, 0x09, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x30, 0x05, 0x00, 0x00, 256 | 0x0c, 0x3c, 0x5b, 0x00, 0x10, 0x08, 0x00, 0x00, 0x30, 0x09, 0x00, 0x00, 257 | 0x0c, 0x3c, 0x5b, 0x00, 0x10, 0x0c, 0x00, 0x00, 0x30, 0x0d, 0x00, 0x00, 258 | 0x0c, 0x3c, 0x5b, 0x00, 0x10, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 259 | 0x03, 0x54, 0x1b, 0x00, 0x03, 0x30, 0x14, 0x00, 0x03, 0x0c, 0x62, 0x00, 260 | 0x04, 0x00, 0x00, 0x00, 0x18, 0xac, 0x42, 0x00, 0x95, 0x01, 0x00, 0x00, 261 | 0x10, 0x80, 0x01, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x96, 0x10, 0x00, 0x00, 262 | 0xd8, 0x30, 0x02, 0x02, 0x3e, 0x0d, 0x00, 0x00, 0x50, 0x0c, 0x00, 0x00, 263 | 0x10, 0x80, 0x01, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x18, 0x20, 0xc2, 0x00, 264 | 0x2a, 0x00, 0x80, 0x00, 0x21, 0x01, 0x00, 0x00, 0x16, 0x10, 0x00, 0x00, 265 | 0x3e, 0x0d, 0x00, 0x00, 0x0d, 0x68, 0x67, 0x00, 0x10, 0x40, 0x03, 0x00, 266 | 0x3d, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x18, 0x8c, 0x01, 0x02, 267 | 0x2c, 0xa0, 0x00, 0x00, 0x07, 0xa4, 0x67, 0x00, 0x06, 0x0c, 0x4a, 0x00, 268 | 0x18, 0x00, 0xc2, 0x00, 0x55, 0x01, 0x00, 0x00, 0x1c, 0x90, 0x01, 0x00, 269 | 0x5c, 0x94, 0x01, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x77, 0x04, 0x00, 0x00, 270 | 0x57, 0xd4, 0xc2, 0x00, 0x37, 0x04, 0x00, 0x00, 0x16, 0x10, 0x00, 0x00, 271 | 0xd1, 0x00, 0xc0, 0x03, 0xe4, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 272 | 0x07, 0xdc, 0x67, 0x00, 0x06, 0x40, 0x4b, 0x00, 0x18, 0xa0, 0x02, 0x02, 273 | 0x2c, 0x00, 0x00, 0x00, 0x07, 0x54, 0x4b, 0x00, 0x1c, 0x88, 0x01, 0x00, 274 | 0x2a, 0x00, 0x40, 0x02, 0x17, 0xd4, 0xc2, 0x00, 0x06, 0x14, 0x4c, 0x00, 275 | 0x06, 0xcc, 0x2a, 0x00, 0x06, 0xa0, 0x4a, 0x00, 0x70, 0x31, 0x00, 0x00, 276 | 0x0c, 0xa0, 0x4a, 0x00, 0x1c, 0xd8, 0x02, 0x00, 0x2d, 0x00, 0x44, 0x03, 277 | 0x07, 0x40, 0x68, 0x00, 0x2d, 0x00, 0x48, 0x03, 0x07, 0x20, 0x69, 0x00, 278 | 0x2d, 0x00, 0x4c, 0x03, 0x07, 0xd8, 0x68, 0x00, 0x2d, 0x00, 0x50, 0x03, 279 | 0x07, 0x30, 0x68, 0x00, 0x06, 0xa0, 0x4a, 0x00, 0x03, 0x10, 0x69, 0x00, 280 | 0x1c, 0x00, 0x04, 0x00, 0x1b, 0xf8, 0x02, 0x00, 0x06, 0x24, 0x57, 0x00, 281 | 0x10, 0x40, 0x1f, 0x00, 0x11, 0x10, 0x10, 0x00, 0x1b, 0xf8, 0x02, 0x00, 282 | 0x10, 0xfc, 0xff, 0x03, 0x1b, 0x00, 0x04, 0x00, 0x03, 0xbc, 0x68, 0x00, 283 | 0x03, 0x44, 0x6d, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x08, 0xb4, 0x68, 0x00, 284 | 0x51, 0x00, 0x04, 0x00, 0x57, 0xf4, 0x02, 0x03, 0x03, 0x60, 0x6a, 0x00, 285 | 0x58, 0xf0, 0x42, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x07, 0xb4, 0x68, 0x00, 286 | 0x2c, 0xec, 0x00, 0x00, 0x07, 0x9c, 0x68, 0x00, 0x2c, 0x0c, 0x00, 0x00, 287 | 0x08, 0xb4, 0x68, 0x00, 0x51, 0x04, 0x00, 0x00, 0x57, 0xf4, 0x02, 0x01, 288 | 0x03, 0x78, 0x6d, 0x00, 0x10, 0xec, 0x00, 0x00, 0x17, 0x00, 0x44, 0x00, 289 | 0x10, 0x01, 0x04, 0x00, 0x98, 0x9a, 0x41, 0x00, 0xa8, 0x02, 0x00, 0x00, 290 | 0x50, 0x06, 0x00, 0x00, 0x03, 0x48, 0x6b, 0x00, 0x03, 0x18, 0x69, 0x00, 291 | 0x06, 0x24, 0x57, 0x00, 0xb1, 0x2d, 0x00, 0x00, 0x03, 0x8c, 0x21, 0x00, 292 | 0xb2, 0x25, 0x00, 0x00, 0x50, 0x20, 0x03, 0x00, 0x03, 0x6c, 0x21, 0x00, 293 | 0xb2, 0x2d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xb1, 0x2d, 0x00, 0x00, 294 | 0x03, 0x84, 0x21, 0x00, 0xb1, 0x25, 0x00, 0x00, 0x03, 0x18, 0x69, 0x00, 295 | 0x06, 0x24, 0x57, 0x00, 0x10, 0x01, 0x04, 0x00, 0x98, 0x9a, 0x41, 0x00, 296 | 0x50, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x18, 0x98, 0x41, 0x00, 297 | 0x29, 0x00, 0x00, 0x02, 0x29, 0x00, 0x00, 0x02, 0x16, 0x08, 0x00, 0x00, 298 | 0x04, 0x00, 0x00, 0x00, 0x03, 0xfc, 0x68, 0x00, 0x06, 0x08, 0x56, 0x00, 299 | 0x03, 0xfc, 0x68, 0x00, 0x06, 0x78, 0x56, 0x00, 0x18, 0x8c, 0x01, 0x02, 300 | 0x2c, 0xa0, 0x00, 0x00, 0x07, 0x38, 0x69, 0x00, 0x03, 0x10, 0x69, 0x00, 301 | 0x03, 0x50, 0x69, 0x00, 0x06, 0x40, 0x69, 0x00, 0x03, 0x50, 0x69, 0x00, 302 | 0x03, 0x18, 0x69, 0x00, 0x18, 0xf0, 0x02, 0x02, 0x2c, 0x04, 0x00, 0x00, 303 | 0x07, 0x24, 0x57, 0x00, 0x06, 0x30, 0x57, 0x00, 0x51, 0x00, 0x00, 0x00, 304 | 0x57, 0xf0, 0x02, 0x01, 0x10, 0x95, 0x01, 0x00, 0x98, 0x92, 0x01, 0x02, 305 | 0x50, 0x02, 0x00, 0x00, 0x03, 0x80, 0x6c, 0x00, 0x03, 0x44, 0x6d, 0x00, 306 | 0x2c, 0x00, 0x00, 0x00, 0x07, 0x7c, 0x69, 0x00, 0x10, 0x00, 0x2c, 0x03, 307 | 0x06, 0x4c, 0x6a, 0x00, 0x03, 0x60, 0x6a, 0x00, 0x17, 0xf4, 0x42, 0x00, 308 | 0x50, 0x3c, 0xfc, 0x03, 0x65, 0x00, 0x00, 0x00, 0x2c, 0xbc, 0xfd, 0x03, 309 | 0x07, 0x18, 0x6a, 0x00, 0x18, 0xf4, 0x42, 0x00, 0x50, 0x3c, 0xfc, 0x03, 310 | 0x65, 0x00, 0x00, 0x00, 0x2c, 0x7c, 0xfe, 0x03, 0x07, 0x18, 0x6a, 0x00, 311 | 0x58, 0xf4, 0x42, 0x00, 0x18, 0x94, 0x81, 0x00, 0x2b, 0x04, 0x00, 0x00, 312 | 0x07, 0xe0, 0x69, 0x00, 0x51, 0x04, 0x00, 0x00, 0x57, 0xf0, 0x02, 0x01, 313 | 0x50, 0xfc, 0x03, 0x00, 0x51, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 314 | 0x58, 0xf4, 0x42, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x07, 0xe0, 0x69, 0x00, 315 | 0x10, 0x04, 0x2c, 0x03, 0x06, 0x4c, 0x6a, 0x00, 0x03, 0xec, 0x68, 0x00, 316 | 0x18, 0x8c, 0x01, 0x02, 0x2c, 0xa0, 0x00, 0x00, 0x07, 0x00, 0x6a, 0x00, 317 | 0x58, 0xf8, 0x02, 0x02, 0x03, 0x38, 0x6d, 0x00, 0x03, 0x80, 0x6c, 0x00, 318 | 0x06, 0x04, 0x6a, 0x00, 0x03, 0x48, 0x6b, 0x00, 0x96, 0x02, 0x00, 0x00, 319 | 0x2c, 0x00, 0x00, 0x00, 0x07, 0x68, 0x69, 0x00, 0x10, 0x08, 0x2c, 0x03, 320 | 0x06, 0x4c, 0x6a, 0x00, 0x18, 0xf4, 0x42, 0x00, 0x2c, 0x80, 0x01, 0x00, 321 | 0x07, 0x68, 0x69, 0x00, 0x03, 0x44, 0x6d, 0x00, 0x03, 0x60, 0x6a, 0x00, 322 | 0x29, 0x00, 0x00, 0x02, 0x17, 0xf4, 0x82, 0x00, 0x18, 0xf4, 0x42, 0x00, 323 | 0x2c, 0x40, 0x02, 0x00, 0x08, 0x50, 0x6a, 0x00, 0x11, 0x00, 0x04, 0x00, 324 | 0x17, 0xf0, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00, 0x17, 0xf4, 0xc2, 0x00, 325 | 0x11, 0x00, 0x00, 0x00, 0x17, 0xf0, 0x02, 0x02, 0x1c, 0xf4, 0x02, 0x00, 326 | 0x06, 0xb8, 0x57, 0x00, 0x10, 0x00, 0x00, 0x00, 0x17, 0xf0, 0x42, 0x00, 327 | 0xd0, 0x00, 0x00, 0x00, 0x03, 0xf8, 0x6d, 0x00, 0x26, 0x00, 0x00, 0x00, 328 | 0x90, 0x00, 0x00, 0x00, 0x50, 0x20, 0x00, 0x00, 0x32, 0x20, 0x00, 0x00, 329 | 0x70, 0x1d, 0x00, 0x00, 0x0c, 0x8c, 0x6a, 0x00, 0x06, 0x94, 0x6a, 0x00, 330 | 0x31, 0x20, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x40, 0x00, 331 | 0x03, 0xfc, 0x6d, 0x00, 0x0d, 0x7c, 0x6a, 0x00, 0x58, 0xf4, 0x02, 0x02, 332 | 0x6c, 0x00, 0x00, 0x00, 0x07, 0xc4, 0x6a, 0x00, 0x03, 0xfc, 0x6d, 0x00, 333 | 0x51, 0x00, 0x00, 0x00, 0x57, 0xf4, 0x02, 0x02, 0x50, 0x04, 0x00, 0x00, 334 | 0x57, 0xf0, 0x42, 0x00, 0x04, 0x00, 0x00, 0x00, 0x58, 0xf4, 0x02, 0x01, 335 | 0x6c, 0x00, 0x00, 0x00, 0x07, 0xdc, 0x6a, 0x00, 0x70, 0x1d, 0x00, 0x00, 336 | 0x0b, 0xe4, 0x6a, 0x00, 0x06, 0xf0, 0x6a, 0x00, 0x70, 0x1d, 0x00, 0x00, 337 | 0x0b, 0xf0, 0x6a, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x0b, 0xf8, 0x6a, 0x00, 338 | 0x06, 0x24, 0x6b, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x0b, 0x24, 0x6b, 0x00, 339 | 0xe7, 0x00, 0x00, 0x00, 0xec, 0x10, 0x00, 0x00, 0x07, 0x3c, 0x5b, 0x00, 340 | 0x03, 0xfc, 0x6d, 0x00, 0xb1, 0x25, 0x00, 0x00, 0x03, 0xfc, 0x6d, 0x00, 341 | 0xb2, 0x25, 0x00, 0x00, 0x03, 0x44, 0x6d, 0x00, 0x2c, 0x00, 0x00, 0x00, 342 | 0x08, 0x3c, 0x5b, 0x00, 0x06, 0x6c, 0x6a, 0x00, 0x50, 0x04, 0x00, 0x00, 343 | 0x57, 0xf0, 0x42, 0x00, 0x03, 0x00, 0x6e, 0x00, 0xb2, 0x25, 0x00, 0x00, 344 | 0x58, 0xf4, 0x02, 0x01, 0x6c, 0x00, 0x00, 0x00, 0x07, 0xf8, 0x6d, 0x00, 345 | 0x03, 0x78, 0x6d, 0x00, 0x06, 0xf8, 0x6d, 0x00, 0x03, 0x44, 0x6d, 0x00, 346 | 0x2c, 0x04, 0x00, 0x00, 0x07, 0x3c, 0x5b, 0x00, 0x03, 0x60, 0x6a, 0x00, 347 | 0x58, 0xf0, 0x42, 0x00, 0x6c, 0x04, 0x00, 0x00, 0x08, 0x3c, 0x5b, 0x00, 348 | 0x6c, 0x02, 0x00, 0x00, 0x07, 0x98, 0x6b, 0x00, 0x29, 0x00, 0x00, 0x02, 349 | 0x6c, 0x06, 0x00, 0x00, 0x07, 0xa0, 0x6b, 0x00, 0x29, 0x00, 0x00, 0x02, 350 | 0x6c, 0x0a, 0x00, 0x00, 0x07, 0xa8, 0x6b, 0x00, 0x29, 0x00, 0x00, 0x02, 351 | 0x19, 0x10, 0x00, 0x02, 0x27, 0x01, 0x00, 0x00, 0x50, 0x02, 0x00, 0x00, 352 | 0x06, 0xb0, 0x6b, 0x00, 0x19, 0x10, 0x40, 0x00, 0x06, 0xac, 0x6b, 0x00, 353 | 0x19, 0x10, 0x80, 0x00, 0x06, 0xac, 0x6b, 0x00, 0x19, 0x10, 0x00, 0x01, 354 | 0x67, 0x02, 0x00, 0x00, 0xa8, 0x02, 0x00, 0x00, 0x0b, 0x3c, 0x5b, 0x00, 355 | 0x06, 0x48, 0x6b, 0x00, 0x58, 0xf4, 0x02, 0x01, 0x6c, 0x00, 0x00, 0x00, 356 | 0x07, 0xcc, 0x6b, 0x00, 0x03, 0x78, 0x6d, 0x00, 0x29, 0x00, 0x00, 0x02, 357 | 0x17, 0xf0, 0x82, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x18, 0xf0, 0x82, 0x00, 358 | 0xb1, 0x25, 0x00, 0x00, 0x03, 0xfc, 0x6d, 0x00, 0x90, 0x00, 0x00, 0x00, 359 | 0x50, 0x20, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x6c, 0x00, 360 | 0xb2, 0x25, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x06, 0x04, 0x6c, 0x00, 361 | 0xb1, 0x25, 0x00, 0x00, 0x2a, 0x00, 0x40, 0x00, 0x03, 0xfc, 0x6d, 0x00, 362 | 0x0d, 0xec, 0x6b, 0x00, 0x58, 0xf4, 0x02, 0x01, 0x6c, 0x00, 0x00, 0x00, 363 | 0x07, 0x28, 0x6c, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x0b, 0x30, 0x6c, 0x00, 364 | 0x06, 0x38, 0x6c, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x0b, 0x38, 0x6c, 0x00, 365 | 0xb2, 0x25, 0x00, 0x00, 0x06, 0x3c, 0x6c, 0x00, 0xb1, 0x25, 0x00, 0x00, 366 | 0x03, 0xfc, 0x6d, 0x00, 0xb2, 0x25, 0x00, 0x00, 0x03, 0x14, 0x6e, 0x00, 367 | 0x70, 0x1d, 0x00, 0x00, 0x0c, 0x74, 0x6c, 0x00, 0x03, 0xf8, 0x6d, 0x00, 368 | 0xe7, 0x00, 0x00, 0x00, 0xec, 0x0c, 0x00, 0x00, 0x07, 0x68, 0x6c, 0x00, 369 | 0x03, 0x34, 0x6d, 0x00, 0x06, 0xd8, 0x6b, 0x00, 0x90, 0x00, 0x00, 0x00, 370 | 0x97, 0xf0, 0x42, 0x00, 0x04, 0x00, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 371 | 0x97, 0xf0, 0x42, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6c, 0x0e, 0x00, 0x00, 372 | 0x07, 0xb0, 0x6c, 0x00, 0x6c, 0x0a, 0x00, 0x00, 0x07, 0xa8, 0x6c, 0x00, 373 | 0x6c, 0x06, 0x00, 0x00, 0x07, 0xa0, 0x6c, 0x00, 0x1a, 0x10, 0x40, 0x00, 374 | 0x06, 0xc0, 0x6c, 0x00, 0x1a, 0x10, 0x80, 0x00, 0x06, 0xc0, 0x6c, 0x00, 375 | 0x1a, 0x10, 0x00, 0x01, 0x06, 0xc0, 0x6c, 0x00, 0x1a, 0x10, 0x00, 0x02, 376 | 0x27, 0x01, 0x00, 0x00, 0x50, 0x02, 0x00, 0x00, 0x06, 0xc4, 0x6c, 0x00, 377 | 0x67, 0x02, 0x00, 0x00, 0x03, 0xbc, 0x6b, 0x00, 0x18, 0xf0, 0x42, 0x00, 378 | 0x2c, 0x04, 0x00, 0x00, 0x08, 0x3c, 0x5b, 0x00, 0xa8, 0x02, 0x00, 0x00, 379 | 0x0b, 0x3c, 0x5b, 0x00, 0x18, 0xf0, 0x02, 0x01, 0x2c, 0x04, 0x00, 0x00, 380 | 0x07, 0xf4, 0x6c, 0x00, 0x03, 0xf8, 0x6d, 0x00, 0x03, 0x34, 0x6d, 0x00, 381 | 0x06, 0x80, 0x6c, 0x00, 0x03, 0x44, 0x6d, 0x00, 0x2c, 0x04, 0x00, 0x00, 382 | 0x07, 0x3c, 0x5b, 0x00, 0x03, 0x60, 0x6a, 0x00, 0x58, 0xf0, 0x42, 0x00, 383 | 0x6c, 0x04, 0x00, 0x00, 0x08, 0x3c, 0x5b, 0x00, 0x58, 0xf4, 0x42, 0x00, 384 | 0x2b, 0x04, 0x00, 0x00, 0x07, 0x80, 0x6c, 0x00, 0x58, 0x94, 0x81, 0x00, 385 | 0x2b, 0x04, 0x00, 0x00, 0x08, 0x80, 0x6c, 0x00, 0x11, 0x00, 0x00, 0x00, 386 | 0x17, 0xf0, 0x02, 0x01, 0x06, 0x80, 0x6c, 0x00, 0x58, 0xf8, 0x02, 0x01, 387 | 0x03, 0xfc, 0x6d, 0x00, 0x0d, 0x38, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 388 | 0x58, 0xf8, 0xc2, 0x00, 0x41, 0x00, 0x00, 0x00, 0x50, 0x20, 0x4e, 0x00, 389 | 0x70, 0x1d, 0x00, 0x00, 0x0b, 0x6c, 0x6d, 0x00, 0x0d, 0x50, 0x6d, 0x00, 390 | 0x42, 0x00, 0x00, 0x00, 0x0d, 0x48, 0x6d, 0x00, 0x10, 0x04, 0x00, 0x00, 391 | 0x04, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 392 | 0x04, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x50, 0xfc, 0x03, 0x00, 393 | 0x51, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 394 | 0x30, 0x00, 0x00, 0x00, 0x0b, 0x98, 0x6d, 0x00, 0x71, 0x1c, 0x00, 0x00, 395 | 0x30, 0x04, 0x00, 0x00, 0x0b, 0xa4, 0x6d, 0x00, 0x71, 0x18, 0x00, 0x00, 396 | 0x30, 0x08, 0x00, 0x00, 0x0b, 0xb0, 0x6d, 0x00, 0x71, 0x14, 0x00, 0x00, 397 | 0x30, 0x0c, 0x00, 0x00, 0x0b, 0xbc, 0x6d, 0x00, 0x71, 0x10, 0x00, 0x00, 398 | 0x30, 0x10, 0x00, 0x00, 0x0b, 0xc8, 0x6d, 0x00, 0x71, 0x0c, 0x00, 0x00, 399 | 0x30, 0x14, 0x00, 0x00, 0x0b, 0xd4, 0x6d, 0x00, 0x71, 0x08, 0x00, 0x00, 400 | 0x30, 0x18, 0x00, 0x00, 0x0b, 0xe0, 0x6d, 0x00, 0x71, 0x04, 0x00, 0x00, 401 | 0x30, 0x1c, 0x00, 0x00, 0x0b, 0xec, 0x6d, 0x00, 0x71, 0x00, 0x00, 0x00, 402 | 0x56, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 403 | 0x03, 0x00, 0x6e, 0x00, 0x03, 0x00, 0x6e, 0x00, 0x41, 0x00, 0x00, 0x00, 404 | 0x50, 0x44, 0x0c, 0x00, 0x03, 0xa0, 0x21, 0x00, 0x42, 0x00, 0x00, 0x00, 405 | 0x04, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x50, 0x48, 0x12, 0x00, 406 | 0x03, 0xa0, 0x21, 0x00, 0x42, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 407 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 408 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 409 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 410 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 411 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 412 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 413 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 414 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 415 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 416 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 417 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 418 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 419 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 420 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 421 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 422 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 423 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 424 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 425 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 426 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 427 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 428 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 429 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 430 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 431 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 432 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 433 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 434 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 435 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 436 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 437 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 438 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 439 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 440 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 441 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 442 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 443 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 444 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 445 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 446 | 0x00, 0x00, 0x00, 0x00 447 | }; 448 | 449 | /* Celly SIM Card Reader: 450 | * Device initialization data - write buffer 3 */ 451 | static const uint8_t celly_buf_3[4] = { 0x00, 0x20, 0x04, 0x2a }; 452 | 453 | /* Device initialization data - read buffer 454 | * (Generic - can be used by any device) */ 455 | static uint8_t init_read_buf[4096] = { 0x00 }; 456 | 457 | /* Structure to hold everything necessary to send an initialization command 458 | * to the device */ 459 | struct device_init_cmd { 460 | 461 | /* SIM_READ, SIM_WRITE, or SIM_NO_XFER */ 462 | int direction; 463 | 464 | /* Raw SCSI CDB (command data block) */ 465 | uint8_t cdb[MAX_CDB_LEN]; 466 | 467 | /* Length of data buffer */ 468 | unsigned int data_len; 469 | 470 | /* Pointer to data buffer. The data can be output (for a write command) 471 | * or input (for a read command). */ 472 | uint8_t *data; 473 | }; 474 | 475 | /* Structure to hold everything necessary to define a SCSI-based SIM card reader */ 476 | struct device { 477 | 478 | /* Maximum length of SCSI sense buffer for the device */ 479 | uint8_t sense_len; 480 | 481 | /* Offset in sense data for type */ 482 | uint8_t sense_type_offset; 483 | 484 | /* Offset in sense data for ASC (additional sense code) */ 485 | uint8_t sense_asc_offset; 486 | 487 | /* Offset in sense data for ASCQ (additional sense code qualifier) */ 488 | uint8_t sense_ascq_offset; 489 | 490 | /* SCSI READ and WRITE command codes for the device */ 491 | uint8_t scsi_cmd_read; 492 | uint8_t scsi_cmd_write; 493 | 494 | /* SCSI CDB (command data block) length for the device */ 495 | uint8_t cdb_len; 496 | 497 | /* CDB for the GSM SELECT command */ 498 | uint8_t CDB_select_file[MAX_CDB_LEN]; 499 | 500 | /* CDB and relevant offsets for the GSM GET RESPONSE command */ 501 | uint8_t CDB_get_response[MAX_CDB_LEN]; 502 | uint8_t get_response_len_offset; 503 | 504 | /* CDB and relevant offsets for the GSM READ RECORD command */ 505 | uint8_t CDB_read_record[MAX_CDB_LEN]; 506 | uint8_t read_record_rec_offset; 507 | uint8_t read_record_len_offset; 508 | 509 | /* CDB and relevant offsets for the GSM READ BINARY command */ 510 | uint8_t CDB_read_binary[MAX_CDB_LEN]; 511 | uint8_t read_binary_hi_offset; 512 | uint8_t read_binary_lo_offset; 513 | uint8_t read_binary_len_offset; 514 | 515 | /* CDB and relevant offsets for the GSM UPDATE RECORD command */ 516 | uint8_t CDB_update_record[MAX_CDB_LEN]; 517 | uint8_t update_record_rec_offset; 518 | uint8_t update_record_len_offset; 519 | 520 | /* CDB and relevant offsets for the GSM UPDATE BINARY command */ 521 | uint8_t CDB_update_binary[MAX_CDB_LEN]; 522 | uint8_t update_binary_hi_offset; 523 | uint8_t update_binary_lo_offset; 524 | uint8_t update_binary_len_offset; 525 | 526 | /* CDB and relevant offsets for the GSM VERIFY CHV command */ 527 | uint8_t CDB_verify_chv[MAX_CDB_LEN]; 528 | uint8_t verify_chv_chvnum_offset; 529 | 530 | /* CDB and relevant offsets for raw, arbitrary GSM command */ 531 | uint8_t CDB_raw_cmd[MAX_CDB_LEN]; 532 | uint8_t raw_cmd_direction_offset; 533 | uint8_t raw_cmd_gsm_cmd_offset; 534 | uint8_t raw_cmd_p1_offset; 535 | uint8_t raw_cmd_p2_offset; 536 | uint8_t raw_cmd_p3_offset; 537 | 538 | /* Initialization commands: the size of 16 for this array is completely 539 | * arbitrary. The device I have sends 9 init commands. If a device turns 540 | * up that needs more than 15 init commands (15 commands plus SIM_NO_XFER 541 | * array terminator), it's easy enough to increase this array's size. */ 542 | struct device_init_cmd init_cmd[16]; 543 | 544 | }; 545 | 546 | /* "Magic" LBA (logical block addressing) for GSM commands on Celly SIM card reader, 547 | * as determined by sniffing USB packets */ 548 | #define CELLY_LBA_1 0x00 549 | #define CELLY_LBA_2 0xd2 550 | #define CELLY_LBA_3 0x00 551 | #define CELLY_LBA_4 0x05 552 | 553 | static const struct device sim_devices[1] = { 554 | { /* BEGIN definitions for Celly SIM Card Reader */ 555 | .sense_len = 32, 556 | .sense_type_offset = 0, 557 | .sense_asc_offset = 12, 558 | .sense_ascq_offset = 13, 559 | .scsi_cmd_read = SCSI_CMD_READ_10, 560 | .scsi_cmd_write = SCSI_CMD_WRITE_10, 561 | .cdb_len = 10, 562 | .CDB_select_file = { 563 | SCSI_CMD_WRITE_10, 564 | CELLY_LBA_1, 565 | CELLY_LBA_2, 566 | CELLY_LBA_3, 567 | CELLY_LBA_4, 568 | GSM_CLASS, 569 | GSM_CMD_SELECT, 570 | 0x00, 571 | 0x00, 572 | GSM_CMD_SELECT_DATA_LEN 573 | }, 574 | .CDB_get_response = { 575 | SCSI_CMD_READ_10, 576 | CELLY_LBA_1, 577 | CELLY_LBA_2, 578 | CELLY_LBA_3, 579 | CELLY_LBA_4, 580 | GSM_CLASS, 581 | GSM_CMD_GET_RESPONSE, 582 | 0x00, 583 | 0x00, 584 | 0x00 585 | }, 586 | .get_response_len_offset = 9, 587 | .CDB_read_record = { 588 | SCSI_CMD_READ_10, 589 | CELLY_LBA_1, 590 | CELLY_LBA_2, 591 | CELLY_LBA_3, 592 | CELLY_LBA_4, 593 | GSM_CLASS, 594 | GSM_CMD_READ_RECORD, 595 | 0x00, 596 | 0x04, /* Absolute mode: see GSM spec, sections 8.5 and 9.2.5 */ 597 | 0x00 598 | }, 599 | .read_record_rec_offset = 7, 600 | .read_record_len_offset = 9, 601 | .CDB_read_binary = { 602 | SCSI_CMD_READ_10, 603 | CELLY_LBA_1, 604 | CELLY_LBA_2, 605 | CELLY_LBA_3, 606 | CELLY_LBA_4, 607 | GSM_CLASS, 608 | GSM_CMD_READ_BINARY, 609 | 0x00, 610 | 0x00, 611 | 0x00 612 | }, 613 | .read_binary_hi_offset = 7, 614 | .read_binary_lo_offset = 8, 615 | .read_binary_len_offset = 9, 616 | .CDB_update_record = { 617 | SCSI_CMD_WRITE_10, 618 | CELLY_LBA_1, 619 | CELLY_LBA_2, 620 | CELLY_LBA_3, 621 | CELLY_LBA_4, 622 | GSM_CLASS, 623 | GSM_CMD_UPDATE_RECORD, 624 | 0x00, 625 | 0x04, /* Absolute mode: see GSM spec, sections 8.6 and 9.2.6 */ 626 | 0x00 627 | }, 628 | .update_record_rec_offset = 7, 629 | .update_record_len_offset = 9, 630 | .CDB_update_binary = { 631 | SCSI_CMD_WRITE_10, 632 | CELLY_LBA_1, 633 | CELLY_LBA_2, 634 | CELLY_LBA_3, 635 | CELLY_LBA_4, 636 | GSM_CLASS, 637 | GSM_CMD_UPDATE_BINARY, 638 | 0x00, 639 | 0x00, 640 | 0x00 641 | }, 642 | .update_binary_hi_offset = 7, 643 | .update_binary_lo_offset = 8, 644 | .update_binary_len_offset = 9, 645 | .CDB_verify_chv = { 646 | SCSI_CMD_WRITE_10, 647 | CELLY_LBA_1, 648 | CELLY_LBA_2, 649 | CELLY_LBA_3, 650 | CELLY_LBA_4, 651 | GSM_CLASS, 652 | GSM_CMD_VERIFY_CHV, 653 | 0x00, 654 | 0x00, 655 | GSM_CMD_VERIFY_CHV_DATA_LEN 656 | }, 657 | .verify_chv_chvnum_offset = 8, 658 | .CDB_raw_cmd = { 659 | 0x00, 660 | CELLY_LBA_1, 661 | CELLY_LBA_2, 662 | CELLY_LBA_3, 663 | CELLY_LBA_4, 664 | GSM_CLASS, 665 | 0x00, 666 | 0x00, 667 | 0x00, 668 | 0x00 669 | }, 670 | .raw_cmd_direction_offset = 0, 671 | .raw_cmd_gsm_cmd_offset = 6, 672 | .raw_cmd_p1_offset = 7, 673 | .raw_cmd_p2_offset = 8, 674 | .raw_cmd_p3_offset = 9, 675 | .init_cmd = { 676 | /* Array of initialization commands to send to the device. scsisim_init_device() 677 | * sends these commands in the specified order to get the device ready to 678 | * accept GSM commands. */ 679 | { 680 | SIM_READ, 681 | { SCSI_CMD_READ_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 }, 682 | 512, 683 | init_read_buf 684 | }, 685 | { 686 | SIM_READ, 687 | { SCSI_CMD_READ_10, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x04, 0x00 }, 688 | 2048, 689 | init_read_buf 690 | }, 691 | { 692 | SIM_READ, 693 | { SCSI_CMD_READ_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 }, 694 | 4096, 695 | init_read_buf 696 | }, 697 | { 698 | SIM_READ, 699 | { SCSI_CMD_READ_10, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 }, 700 | 512, 701 | init_read_buf 702 | }, 703 | { 704 | SIM_WRITE, 705 | { SCSI_CMD_WRITE_10, 0x00, 0x22, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 }, 706 | sizeof(celly_buf_1), 707 | (uint8_t*)celly_buf_1 708 | }, 709 | { 710 | SIM_WRITE, 711 | { SCSI_CMD_WRITE_10, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 }, 712 | sizeof(celly_buf_2), 713 | (uint8_t*)celly_buf_2 714 | }, 715 | { 716 | SIM_READ, 717 | { SCSI_CMD_READ_10, 0x00, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20 }, 718 | 512, 719 | init_read_buf 720 | }, 721 | { 722 | SIM_WRITE, 723 | { SCSI_CMD_WRITE_10, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04 }, 724 | sizeof(celly_buf_3), 725 | (uint8_t*)celly_buf_3 726 | }, 727 | { 728 | SIM_READ, 729 | { SCSI_CMD_READ_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 }, 730 | 512, 731 | init_read_buf 732 | }, 733 | { SIM_NO_XFER } 734 | } 735 | }, /* END definitions for Celly SIM Card Reader */ 736 | /* Add future devices here */ 737 | }; 738 | 739 | 740 | #endif /* __SCSISIM_DEVICE_H__ */ 741 | 742 | /* EOF */ 743 | 744 | --------------------------------------------------------------------------------