1*4882a593Smuzhiyun /* SPDX-License-Identifier: GPL-2.0-or-later */ 2*4882a593Smuzhiyun /******************************************************************************* 3*4882a593Smuzhiyun * IBM Virtual SCSI Target Driver 4*4882a593Smuzhiyun * Copyright (C) 2003-2005 Dave Boutcher (boutcher@us.ibm.com) IBM Corp. 5*4882a593Smuzhiyun * Santiago Leon (santil@us.ibm.com) IBM Corp. 6*4882a593Smuzhiyun * Linda Xie (lxie@us.ibm.com) IBM Corp. 7*4882a593Smuzhiyun * 8*4882a593Smuzhiyun * Copyright (C) 2005-2011 FUJITA Tomonori <tomof@acm.org> 9*4882a593Smuzhiyun * Copyright (C) 2010 Nicholas A. Bellinger <nab@kernel.org> 10*4882a593Smuzhiyun * Copyright (C) 2016 Bryant G. Ly <bryantly@linux.vnet.ibm.com> IBM Corp. 11*4882a593Smuzhiyun * 12*4882a593Smuzhiyun * Authors: Bryant G. Ly <bryantly@linux.vnet.ibm.com> 13*4882a593Smuzhiyun * Authors: Michael Cyr <mikecyr@linux.vnet.ibm.com> 14*4882a593Smuzhiyun * 15*4882a593Smuzhiyun ****************************************************************************/ 16*4882a593Smuzhiyun 17*4882a593Smuzhiyun #ifndef __H_IBMVSCSI_TGT 18*4882a593Smuzhiyun #define __H_IBMVSCSI_TGT 19*4882a593Smuzhiyun 20*4882a593Smuzhiyun #include <linux/interrupt.h> 21*4882a593Smuzhiyun #include "libsrp.h" 22*4882a593Smuzhiyun 23*4882a593Smuzhiyun #define SYS_ID_NAME_LEN 64 24*4882a593Smuzhiyun #define PARTITION_NAMELEN 96 25*4882a593Smuzhiyun #define IBMVSCSIS_NAMELEN 32 26*4882a593Smuzhiyun 27*4882a593Smuzhiyun #define MSG_HI 0 28*4882a593Smuzhiyun #define MSG_LOW 1 29*4882a593Smuzhiyun 30*4882a593Smuzhiyun #define MAX_CMD_Q_PAGES 4 31*4882a593Smuzhiyun #define CRQ_PER_PAGE (PAGE_SIZE / sizeof(struct viosrp_crq)) 32*4882a593Smuzhiyun /* in terms of number of elements */ 33*4882a593Smuzhiyun #define DEFAULT_CMD_Q_SIZE CRQ_PER_PAGE 34*4882a593Smuzhiyun #define MAX_CMD_Q_SIZE (DEFAULT_CMD_Q_SIZE * MAX_CMD_Q_PAGES) 35*4882a593Smuzhiyun 36*4882a593Smuzhiyun #define SRP_VIOLATION 0x102 /* general error code */ 37*4882a593Smuzhiyun 38*4882a593Smuzhiyun /* 39*4882a593Smuzhiyun * SRP buffer formats defined as of 16.a supported by this driver. 40*4882a593Smuzhiyun */ 41*4882a593Smuzhiyun #define SUPPORTED_FORMATS ((SRP_DATA_DESC_DIRECT << 1) | \ 42*4882a593Smuzhiyun (SRP_DATA_DESC_INDIRECT << 1)) 43*4882a593Smuzhiyun 44*4882a593Smuzhiyun #define SCSI_LUN_ADDR_METHOD_FLAT 1 45*4882a593Smuzhiyun 46*4882a593Smuzhiyun struct dma_window { 47*4882a593Smuzhiyun u32 liobn; /* Unique per vdevice */ 48*4882a593Smuzhiyun u64 tce_base; /* Physical location of the TCE table */ 49*4882a593Smuzhiyun u64 tce_size; /* Size of the TCE table in bytes */ 50*4882a593Smuzhiyun }; 51*4882a593Smuzhiyun 52*4882a593Smuzhiyun struct target_dds { 53*4882a593Smuzhiyun u64 unit_id; /* 64 bit will force alignment */ 54*4882a593Smuzhiyun #define NUM_DMA_WINDOWS 2 55*4882a593Smuzhiyun #define LOCAL 0 56*4882a593Smuzhiyun #define REMOTE 1 57*4882a593Smuzhiyun struct dma_window window[NUM_DMA_WINDOWS]; 58*4882a593Smuzhiyun 59*4882a593Smuzhiyun /* root node property "ibm,partition-no" */ 60*4882a593Smuzhiyun uint partition_num; 61*4882a593Smuzhiyun char partition_name[PARTITION_NAMELEN]; 62*4882a593Smuzhiyun }; 63*4882a593Smuzhiyun 64*4882a593Smuzhiyun #define MAX_NUM_PORTS 1 65*4882a593Smuzhiyun #define MAX_H_COPY_RDMA (128 * 1024) 66*4882a593Smuzhiyun 67*4882a593Smuzhiyun #define MAX_EYE 64 68*4882a593Smuzhiyun 69*4882a593Smuzhiyun /* Return codes */ 70*4882a593Smuzhiyun #define ADAPT_SUCCESS 0L 71*4882a593Smuzhiyun /* choose error codes that do not conflict with PHYP */ 72*4882a593Smuzhiyun #define ERROR -40L 73*4882a593Smuzhiyun 74*4882a593Smuzhiyun struct format_code { 75*4882a593Smuzhiyun u8 reserved; 76*4882a593Smuzhiyun u8 buffers; 77*4882a593Smuzhiyun }; 78*4882a593Smuzhiyun 79*4882a593Smuzhiyun struct client_info { 80*4882a593Smuzhiyun #define SRP_VERSION "16.a" 81*4882a593Smuzhiyun char srp_version[8]; 82*4882a593Smuzhiyun /* root node property ibm,partition-name */ 83*4882a593Smuzhiyun char partition_name[PARTITION_NAMELEN]; 84*4882a593Smuzhiyun /* root node property ibm,partition-no */ 85*4882a593Smuzhiyun u32 partition_number; 86*4882a593Smuzhiyun /* initially 1 */ 87*4882a593Smuzhiyun u32 mad_version; 88*4882a593Smuzhiyun u32 os_type; 89*4882a593Smuzhiyun }; 90*4882a593Smuzhiyun 91*4882a593Smuzhiyun /* 92*4882a593Smuzhiyun * Changing this constant changes the number of seconds to wait before 93*4882a593Smuzhiyun * considering the client will never service its queue again. 94*4882a593Smuzhiyun */ 95*4882a593Smuzhiyun #define SECONDS_TO_CONSIDER_FAILED 30 96*4882a593Smuzhiyun /* 97*4882a593Smuzhiyun * These constants set the polling period used to determine if the client 98*4882a593Smuzhiyun * has freed at least one element in the response queue. 99*4882a593Smuzhiyun */ 100*4882a593Smuzhiyun #define WAIT_SECONDS 1 101*4882a593Smuzhiyun #define WAIT_NANO_SECONDS 5000 102*4882a593Smuzhiyun #define MAX_TIMER_POPS ((1000000 / WAIT_NANO_SECONDS) * \ 103*4882a593Smuzhiyun SECONDS_TO_CONSIDER_FAILED) 104*4882a593Smuzhiyun /* 105*4882a593Smuzhiyun * general purpose timer control block 106*4882a593Smuzhiyun * which can be used for multiple functions 107*4882a593Smuzhiyun */ 108*4882a593Smuzhiyun struct timer_cb { 109*4882a593Smuzhiyun struct hrtimer timer; 110*4882a593Smuzhiyun /* 111*4882a593Smuzhiyun * how long has it been since the client 112*4882a593Smuzhiyun * serviced the queue. The variable is incrmented 113*4882a593Smuzhiyun * in the service_wait_q routine and cleared 114*4882a593Smuzhiyun * in send messages 115*4882a593Smuzhiyun */ 116*4882a593Smuzhiyun int timer_pops; 117*4882a593Smuzhiyun /* the timer is started */ 118*4882a593Smuzhiyun bool started; 119*4882a593Smuzhiyun }; 120*4882a593Smuzhiyun 121*4882a593Smuzhiyun struct cmd_queue { 122*4882a593Smuzhiyun /* kva */ 123*4882a593Smuzhiyun struct viosrp_crq *base_addr; 124*4882a593Smuzhiyun dma_addr_t crq_token; 125*4882a593Smuzhiyun /* used to maintain index */ 126*4882a593Smuzhiyun uint mask; 127*4882a593Smuzhiyun /* current element */ 128*4882a593Smuzhiyun uint index; 129*4882a593Smuzhiyun int size; 130*4882a593Smuzhiyun }; 131*4882a593Smuzhiyun 132*4882a593Smuzhiyun #define SCSOLNT_RESP_SHIFT 1 133*4882a593Smuzhiyun #define UCSOLNT_RESP_SHIFT 2 134*4882a593Smuzhiyun 135*4882a593Smuzhiyun #define SCSOLNT BIT(SCSOLNT_RESP_SHIFT) 136*4882a593Smuzhiyun #define UCSOLNT BIT(UCSOLNT_RESP_SHIFT) 137*4882a593Smuzhiyun 138*4882a593Smuzhiyun enum cmd_type { 139*4882a593Smuzhiyun SCSI_CDB = 0x01, 140*4882a593Smuzhiyun TASK_MANAGEMENT = 0x02, 141*4882a593Smuzhiyun /* MAD or addressed to port 0 */ 142*4882a593Smuzhiyun ADAPTER_MAD = 0x04, 143*4882a593Smuzhiyun UNSET_TYPE = 0x08, 144*4882a593Smuzhiyun }; 145*4882a593Smuzhiyun 146*4882a593Smuzhiyun struct iu_rsp { 147*4882a593Smuzhiyun u8 format; 148*4882a593Smuzhiyun u8 sol_not; 149*4882a593Smuzhiyun u16 len; 150*4882a593Smuzhiyun /* tag is just to help client identify cmd, so don't translate be/le */ 151*4882a593Smuzhiyun u64 tag; 152*4882a593Smuzhiyun }; 153*4882a593Smuzhiyun 154*4882a593Smuzhiyun struct ibmvscsis_cmd { 155*4882a593Smuzhiyun struct list_head list; 156*4882a593Smuzhiyun /* Used for TCM Core operations */ 157*4882a593Smuzhiyun struct se_cmd se_cmd; 158*4882a593Smuzhiyun struct iu_entry *iue; 159*4882a593Smuzhiyun struct iu_rsp rsp; 160*4882a593Smuzhiyun struct work_struct work; 161*4882a593Smuzhiyun struct scsi_info *adapter; 162*4882a593Smuzhiyun struct ibmvscsis_cmd *abort_cmd; 163*4882a593Smuzhiyun /* Sense buffer that will be mapped into outgoing status */ 164*4882a593Smuzhiyun unsigned char sense_buf[TRANSPORT_SENSE_BUFFER]; 165*4882a593Smuzhiyun u64 init_time; 166*4882a593Smuzhiyun #define CMD_FAST_FAIL BIT(0) 167*4882a593Smuzhiyun #define DELAY_SEND BIT(1) 168*4882a593Smuzhiyun u32 flags; 169*4882a593Smuzhiyun char type; 170*4882a593Smuzhiyun }; 171*4882a593Smuzhiyun 172*4882a593Smuzhiyun struct ibmvscsis_nexus { 173*4882a593Smuzhiyun struct se_session *se_sess; 174*4882a593Smuzhiyun }; 175*4882a593Smuzhiyun 176*4882a593Smuzhiyun struct ibmvscsis_tport { 177*4882a593Smuzhiyun /* SCSI protocol the tport is providing */ 178*4882a593Smuzhiyun u8 tport_proto_id; 179*4882a593Smuzhiyun /* ASCII formatted WWPN for SRP Target port */ 180*4882a593Smuzhiyun char tport_name[IBMVSCSIS_NAMELEN]; 181*4882a593Smuzhiyun /* Returned by ibmvscsis_make_tport() */ 182*4882a593Smuzhiyun struct se_wwn tport_wwn; 183*4882a593Smuzhiyun /* Returned by ibmvscsis_make_tpg() */ 184*4882a593Smuzhiyun struct se_portal_group se_tpg; 185*4882a593Smuzhiyun /* ibmvscsis port target portal group tag for TCM */ 186*4882a593Smuzhiyun u16 tport_tpgt; 187*4882a593Smuzhiyun /* Pointer to TCM session for I_T Nexus */ 188*4882a593Smuzhiyun struct ibmvscsis_nexus *ibmv_nexus; 189*4882a593Smuzhiyun bool enabled; 190*4882a593Smuzhiyun bool releasing; 191*4882a593Smuzhiyun }; 192*4882a593Smuzhiyun 193*4882a593Smuzhiyun struct scsi_info { 194*4882a593Smuzhiyun struct list_head list; 195*4882a593Smuzhiyun char eye[MAX_EYE]; 196*4882a593Smuzhiyun 197*4882a593Smuzhiyun /* commands waiting for space on repsonse queue */ 198*4882a593Smuzhiyun struct list_head waiting_rsp; 199*4882a593Smuzhiyun #define NO_QUEUE 0x00 200*4882a593Smuzhiyun #define WAIT_ENABLED 0X01 201*4882a593Smuzhiyun #define WAIT_CONNECTION 0x04 202*4882a593Smuzhiyun /* have established a connection */ 203*4882a593Smuzhiyun #define CONNECTED 0x08 204*4882a593Smuzhiyun /* at least one port is processing SRP IU */ 205*4882a593Smuzhiyun #define SRP_PROCESSING 0x10 206*4882a593Smuzhiyun /* remove request received */ 207*4882a593Smuzhiyun #define UNCONFIGURING 0x20 208*4882a593Smuzhiyun /* disconnect by letting adapter go idle, no error */ 209*4882a593Smuzhiyun #define WAIT_IDLE 0x40 210*4882a593Smuzhiyun /* disconnecting to clear an error */ 211*4882a593Smuzhiyun #define ERR_DISCONNECT 0x80 212*4882a593Smuzhiyun /* disconnect to clear error state, then come back up */ 213*4882a593Smuzhiyun #define ERR_DISCONNECT_RECONNECT 0x100 214*4882a593Smuzhiyun /* disconnected after clearing an error */ 215*4882a593Smuzhiyun #define ERR_DISCONNECTED 0x200 216*4882a593Smuzhiyun /* A series of errors caused unexpected errors */ 217*4882a593Smuzhiyun #define UNDEFINED 0x400 218*4882a593Smuzhiyun u16 state; 219*4882a593Smuzhiyun int fast_fail; 220*4882a593Smuzhiyun struct target_dds dds; 221*4882a593Smuzhiyun char *cmd_pool; 222*4882a593Smuzhiyun /* list of free commands */ 223*4882a593Smuzhiyun struct list_head free_cmd; 224*4882a593Smuzhiyun /* command elements ready for scheduler */ 225*4882a593Smuzhiyun struct list_head schedule_q; 226*4882a593Smuzhiyun /* commands sent to TCM */ 227*4882a593Smuzhiyun struct list_head active_q; 228*4882a593Smuzhiyun caddr_t *map_buf; 229*4882a593Smuzhiyun /* ioba of map buffer */ 230*4882a593Smuzhiyun dma_addr_t map_ioba; 231*4882a593Smuzhiyun /* allowable number of outstanding SRP requests */ 232*4882a593Smuzhiyun int request_limit; 233*4882a593Smuzhiyun /* extra credit */ 234*4882a593Smuzhiyun int credit; 235*4882a593Smuzhiyun /* outstanding transactions against credit limit */ 236*4882a593Smuzhiyun int debit; 237*4882a593Smuzhiyun 238*4882a593Smuzhiyun /* allow only one outstanding mad request */ 239*4882a593Smuzhiyun #define PROCESSING_MAD 0x00002 240*4882a593Smuzhiyun /* Waiting to go idle */ 241*4882a593Smuzhiyun #define WAIT_FOR_IDLE 0x00004 242*4882a593Smuzhiyun /* H_REG_CRQ called */ 243*4882a593Smuzhiyun #define CRQ_CLOSED 0x00010 244*4882a593Smuzhiyun /* detected that client has failed */ 245*4882a593Smuzhiyun #define CLIENT_FAILED 0x00040 246*4882a593Smuzhiyun /* detected that transport event occurred */ 247*4882a593Smuzhiyun #define TRANS_EVENT 0x00080 248*4882a593Smuzhiyun /* don't attempt to send anything to the client */ 249*4882a593Smuzhiyun #define RESPONSE_Q_DOWN 0x00100 250*4882a593Smuzhiyun /* request made to schedule disconnect handler */ 251*4882a593Smuzhiyun #define SCHEDULE_DISCONNECT 0x00400 252*4882a593Smuzhiyun /* disconnect handler is scheduled */ 253*4882a593Smuzhiyun #define DISCONNECT_SCHEDULED 0x00800 254*4882a593Smuzhiyun /* remove function is sleeping */ 255*4882a593Smuzhiyun #define CFG_SLEEPING 0x01000 256*4882a593Smuzhiyun /* Register for Prepare for Suspend Transport Events */ 257*4882a593Smuzhiyun #define PREP_FOR_SUSPEND_ENABLED 0x02000 258*4882a593Smuzhiyun /* Prepare for Suspend event sent */ 259*4882a593Smuzhiyun #define PREP_FOR_SUSPEND_PENDING 0x04000 260*4882a593Smuzhiyun /* Resume from Suspend event sent */ 261*4882a593Smuzhiyun #define PREP_FOR_SUSPEND_ABORTED 0x08000 262*4882a593Smuzhiyun /* Prepare for Suspend event overwrote another CRQ entry */ 263*4882a593Smuzhiyun #define PREP_FOR_SUSPEND_OVERWRITE 0x10000 264*4882a593Smuzhiyun u32 flags; 265*4882a593Smuzhiyun /* adapter lock */ 266*4882a593Smuzhiyun spinlock_t intr_lock; 267*4882a593Smuzhiyun /* information needed to manage command queue */ 268*4882a593Smuzhiyun struct cmd_queue cmd_q; 269*4882a593Smuzhiyun /* used in hcall to copy response back into srp buffer */ 270*4882a593Smuzhiyun u64 empty_iu_id; 271*4882a593Smuzhiyun /* used in crq, to tag what iu the response is for */ 272*4882a593Smuzhiyun u64 empty_iu_tag; 273*4882a593Smuzhiyun uint new_state; 274*4882a593Smuzhiyun uint resume_state; 275*4882a593Smuzhiyun /* control block for the response queue timer */ 276*4882a593Smuzhiyun struct timer_cb rsp_q_timer; 277*4882a593Smuzhiyun /* keep last client to enable proper accounting */ 278*4882a593Smuzhiyun struct client_info client_data; 279*4882a593Smuzhiyun /* what can this client do */ 280*4882a593Smuzhiyun u32 client_cap; 281*4882a593Smuzhiyun /* 282*4882a593Smuzhiyun * The following two fields capture state and flag changes that 283*4882a593Smuzhiyun * can occur when the lock is given up. In the orginal design, 284*4882a593Smuzhiyun * the lock was held during calls into phyp; 285*4882a593Smuzhiyun * however, phyp did not meet PAPR architecture. This is 286*4882a593Smuzhiyun * a work around. 287*4882a593Smuzhiyun */ 288*4882a593Smuzhiyun u16 phyp_acr_state; 289*4882a593Smuzhiyun u32 phyp_acr_flags; 290*4882a593Smuzhiyun 291*4882a593Smuzhiyun struct workqueue_struct *work_q; 292*4882a593Smuzhiyun struct completion wait_idle; 293*4882a593Smuzhiyun struct completion unconfig; 294*4882a593Smuzhiyun struct device dev; 295*4882a593Smuzhiyun struct vio_dev *dma_dev; 296*4882a593Smuzhiyun struct srp_target target; 297*4882a593Smuzhiyun struct ibmvscsis_tport tport; 298*4882a593Smuzhiyun struct tasklet_struct work_task; 299*4882a593Smuzhiyun struct work_struct proc_work; 300*4882a593Smuzhiyun }; 301*4882a593Smuzhiyun 302*4882a593Smuzhiyun /* 303*4882a593Smuzhiyun * Provide a constant that allows software to detect the adapter is 304*4882a593Smuzhiyun * disconnecting from the client from one of several states. 305*4882a593Smuzhiyun */ 306*4882a593Smuzhiyun #define IS_DISCONNECTING (UNCONFIGURING | ERR_DISCONNECT_RECONNECT | \ 307*4882a593Smuzhiyun ERR_DISCONNECT) 308*4882a593Smuzhiyun 309*4882a593Smuzhiyun /* 310*4882a593Smuzhiyun * Provide a constant that can be used with interrupt handling that 311*4882a593Smuzhiyun * essentially lets the interrupt handler know that all requests should 312*4882a593Smuzhiyun * be thrown out, 313*4882a593Smuzhiyun */ 314*4882a593Smuzhiyun #define DONT_PROCESS_STATE (IS_DISCONNECTING | UNDEFINED | \ 315*4882a593Smuzhiyun ERR_DISCONNECTED | WAIT_IDLE) 316*4882a593Smuzhiyun 317*4882a593Smuzhiyun /* 318*4882a593Smuzhiyun * If any of these flag bits are set then do not allow the interrupt 319*4882a593Smuzhiyun * handler to schedule the off level handler. 320*4882a593Smuzhiyun */ 321*4882a593Smuzhiyun #define BLOCK (DISCONNECT_SCHEDULED) 322*4882a593Smuzhiyun 323*4882a593Smuzhiyun /* State and transition events that stop the interrupt handler */ 324*4882a593Smuzhiyun #define TARGET_STOP(VSCSI) (long)(((VSCSI)->state & DONT_PROCESS_STATE) | \ 325*4882a593Smuzhiyun ((VSCSI)->flags & BLOCK)) 326*4882a593Smuzhiyun 327*4882a593Smuzhiyun #define PREP_FOR_SUSPEND_FLAGS (PREP_FOR_SUSPEND_ENABLED | \ 328*4882a593Smuzhiyun PREP_FOR_SUSPEND_PENDING | \ 329*4882a593Smuzhiyun PREP_FOR_SUSPEND_ABORTED | \ 330*4882a593Smuzhiyun PREP_FOR_SUSPEND_OVERWRITE) 331*4882a593Smuzhiyun 332*4882a593Smuzhiyun /* flag bit that are not reset during disconnect */ 333*4882a593Smuzhiyun #define PRESERVE_FLAG_FIELDS (PREP_FOR_SUSPEND_FLAGS) 334*4882a593Smuzhiyun 335*4882a593Smuzhiyun #define vio_iu(IUE) ((union viosrp_iu *)((IUE)->sbuf->buf)) 336*4882a593Smuzhiyun 337*4882a593Smuzhiyun #define READ_CMD(cdb) (((cdb)[0] & 0x1F) == 8) 338*4882a593Smuzhiyun #define WRITE_CMD(cdb) (((cdb)[0] & 0x1F) == 0xA) 339*4882a593Smuzhiyun 340*4882a593Smuzhiyun #ifndef H_GET_PARTNER_INFO 341*4882a593Smuzhiyun #define H_GET_PARTNER_INFO 0x0000000000000008LL 342*4882a593Smuzhiyun #endif 343*4882a593Smuzhiyun #ifndef H_ENABLE_PREPARE_FOR_SUSPEND 344*4882a593Smuzhiyun #define H_ENABLE_PREPARE_FOR_SUSPEND 0x000000000000001DLL 345*4882a593Smuzhiyun #endif 346*4882a593Smuzhiyun #ifndef H_READY_FOR_SUSPEND 347*4882a593Smuzhiyun #define H_READY_FOR_SUSPEND 0x000000000000001ELL 348*4882a593Smuzhiyun #endif 349*4882a593Smuzhiyun 350*4882a593Smuzhiyun 351*4882a593Smuzhiyun #define h_copy_rdma(l, sa, sb, da, db) \ 352*4882a593Smuzhiyun plpar_hcall_norets(H_COPY_RDMA, l, sa, sb, da, db) 353*4882a593Smuzhiyun #define h_vioctl(u, o, a, u1, u2, u3, u4) \ 354*4882a593Smuzhiyun plpar_hcall_norets(H_VIOCTL, u, o, a, u1, u2) 355*4882a593Smuzhiyun #define h_reg_crq(ua, tok, sz) \ 356*4882a593Smuzhiyun plpar_hcall_norets(H_REG_CRQ, ua, tok, sz) 357*4882a593Smuzhiyun #define h_free_crq(ua) \ 358*4882a593Smuzhiyun plpar_hcall_norets(H_FREE_CRQ, ua) 359*4882a593Smuzhiyun #define h_send_crq(ua, d1, d2) \ 360*4882a593Smuzhiyun plpar_hcall_norets(H_SEND_CRQ, ua, d1, d2) 361*4882a593Smuzhiyun 362*4882a593Smuzhiyun #endif 363