1 /** 2 * Copyright (C) 2005 - 2011 Emulex 3 * All rights reserved. 4 * 5 * This program is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU General Public License version 2 7 * as published by the Free Software Foundation. The full GNU General 8 * Public License is included in this distribution in the file called COPYING. 9 * 10 * Written by: Jayamohan Kallickal (jayamohan.kallickal@emulex.com) 11 * 12 * Contact Information: 13 * linux-drivers@emulex.com 14 * 15 * Emulex 16 * 3333 Susan Street 17 * Costa Mesa, CA 92626 18 */ 19 20 #include <scsi/libiscsi.h> 21 #include <scsi/scsi_transport_iscsi.h> 22 #include <scsi/scsi_transport.h> 23 #include <scsi/scsi_cmnd.h> 24 #include <scsi/scsi_device.h> 25 #include <scsi/scsi_host.h> 26 #include <scsi/scsi_netlink.h> 27 #include <net/netlink.h> 28 #include <scsi/scsi.h> 29 30 #include "be_iscsi.h" 31 32 extern struct iscsi_transport beiscsi_iscsi_transport; 33 34 /** 35 * beiscsi_session_create - creates a new iscsi session 36 * @cmds_max: max commands supported 37 * @qdepth: max queue depth supported 38 * @initial_cmdsn: initial iscsi CMDSN 39 */ 40 struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep, 41 u16 cmds_max, 42 u16 qdepth, 43 u32 initial_cmdsn) 44 { 45 struct Scsi_Host *shost; 46 struct beiscsi_endpoint *beiscsi_ep; 47 struct iscsi_cls_session *cls_session; 48 struct beiscsi_hba *phba; 49 struct iscsi_session *sess; 50 struct beiscsi_session *beiscsi_sess; 51 struct beiscsi_io_task *io_task; 52 53 SE_DEBUG(DBG_LVL_8, "In beiscsi_session_create\n"); 54 55 if (!ep) { 56 SE_DEBUG(DBG_LVL_1, "beiscsi_session_create: invalid ep\n"); 57 return NULL; 58 } 59 beiscsi_ep = ep->dd_data; 60 phba = beiscsi_ep->phba; 61 shost = phba->shost; 62 if (cmds_max > beiscsi_ep->phba->params.wrbs_per_cxn) { 63 shost_printk(KERN_ERR, shost, "Cannot handle %d cmds." 64 "Max cmds per session supported is %d. Using %d. " 65 "\n", cmds_max, 66 beiscsi_ep->phba->params.wrbs_per_cxn, 67 beiscsi_ep->phba->params.wrbs_per_cxn); 68 cmds_max = beiscsi_ep->phba->params.wrbs_per_cxn; 69 } 70 71 cls_session = iscsi_session_setup(&beiscsi_iscsi_transport, 72 shost, cmds_max, 73 sizeof(*beiscsi_sess), 74 sizeof(*io_task), 75 initial_cmdsn, ISCSI_MAX_TARGET); 76 if (!cls_session) 77 return NULL; 78 sess = cls_session->dd_data; 79 beiscsi_sess = sess->dd_data; 80 beiscsi_sess->bhs_pool = pci_pool_create("beiscsi_bhs_pool", 81 phba->pcidev, 82 sizeof(struct be_cmd_bhs), 83 64, 0); 84 if (!beiscsi_sess->bhs_pool) 85 goto destroy_sess; 86 87 return cls_session; 88 destroy_sess: 89 iscsi_session_teardown(cls_session); 90 return NULL; 91 } 92 93 /** 94 * beiscsi_session_destroy - destroys iscsi session 95 * @cls_session: pointer to iscsi cls session 96 * 97 * Destroys iSCSI session instance and releases 98 * resources allocated for it. 99 */ 100 void beiscsi_session_destroy(struct iscsi_cls_session *cls_session) 101 { 102 struct iscsi_session *sess = cls_session->dd_data; 103 struct beiscsi_session *beiscsi_sess = sess->dd_data; 104 105 SE_DEBUG(DBG_LVL_8, "In beiscsi_session_destroy\n"); 106 pci_pool_destroy(beiscsi_sess->bhs_pool); 107 iscsi_session_teardown(cls_session); 108 } 109 110 /** 111 * beiscsi_conn_create - create an instance of iscsi connection 112 * @cls_session: ptr to iscsi_cls_session 113 * @cid: iscsi cid 114 */ 115 struct iscsi_cls_conn * 116 beiscsi_conn_create(struct iscsi_cls_session *cls_session, u32 cid) 117 { 118 struct beiscsi_hba *phba; 119 struct Scsi_Host *shost; 120 struct iscsi_cls_conn *cls_conn; 121 struct beiscsi_conn *beiscsi_conn; 122 struct iscsi_conn *conn; 123 struct iscsi_session *sess; 124 struct beiscsi_session *beiscsi_sess; 125 126 SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_create ,cid" 127 "from iscsi layer=%d\n", cid); 128 shost = iscsi_session_to_shost(cls_session); 129 phba = iscsi_host_priv(shost); 130 131 cls_conn = iscsi_conn_setup(cls_session, sizeof(*beiscsi_conn), cid); 132 if (!cls_conn) 133 return NULL; 134 135 conn = cls_conn->dd_data; 136 beiscsi_conn = conn->dd_data; 137 beiscsi_conn->ep = NULL; 138 beiscsi_conn->phba = phba; 139 beiscsi_conn->conn = conn; 140 sess = cls_session->dd_data; 141 beiscsi_sess = sess->dd_data; 142 beiscsi_conn->beiscsi_sess = beiscsi_sess; 143 return cls_conn; 144 } 145 146 /** 147 * beiscsi_bindconn_cid - Bind the beiscsi_conn with phba connection table 148 * @beiscsi_conn: The pointer to beiscsi_conn structure 149 * @phba: The phba instance 150 * @cid: The cid to free 151 */ 152 static int beiscsi_bindconn_cid(struct beiscsi_hba *phba, 153 struct beiscsi_conn *beiscsi_conn, 154 unsigned int cid) 155 { 156 if (phba->conn_table[cid]) { 157 SE_DEBUG(DBG_LVL_1, 158 "Connection table already occupied. Detected clash\n"); 159 return -EINVAL; 160 } else { 161 SE_DEBUG(DBG_LVL_8, "phba->conn_table[%d]=%p(beiscsi_conn)\n", 162 cid, beiscsi_conn); 163 phba->conn_table[cid] = beiscsi_conn; 164 } 165 return 0; 166 } 167 168 /** 169 * beiscsi_conn_bind - Binds iscsi session/connection with TCP connection 170 * @cls_session: pointer to iscsi cls session 171 * @cls_conn: pointer to iscsi cls conn 172 * @transport_fd: EP handle(64 bit) 173 * 174 * This function binds the TCP Conn with iSCSI Connection and Session. 175 */ 176 int beiscsi_conn_bind(struct iscsi_cls_session *cls_session, 177 struct iscsi_cls_conn *cls_conn, 178 u64 transport_fd, int is_leading) 179 { 180 struct iscsi_conn *conn = cls_conn->dd_data; 181 struct beiscsi_conn *beiscsi_conn = conn->dd_data; 182 struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); 183 struct beiscsi_hba *phba = iscsi_host_priv(shost); 184 struct beiscsi_endpoint *beiscsi_ep; 185 struct iscsi_endpoint *ep; 186 187 SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_bind\n"); 188 ep = iscsi_lookup_endpoint(transport_fd); 189 if (!ep) 190 return -EINVAL; 191 192 beiscsi_ep = ep->dd_data; 193 194 if (iscsi_conn_bind(cls_session, cls_conn, is_leading)) 195 return -EINVAL; 196 197 if (beiscsi_ep->phba != phba) { 198 SE_DEBUG(DBG_LVL_8, 199 "beiscsi_ep->hba=%p not equal to phba=%p\n", 200 beiscsi_ep->phba, phba); 201 return -EEXIST; 202 } 203 204 beiscsi_conn->beiscsi_conn_cid = beiscsi_ep->ep_cid; 205 beiscsi_conn->ep = beiscsi_ep; 206 beiscsi_ep->conn = beiscsi_conn; 207 SE_DEBUG(DBG_LVL_8, "beiscsi_conn=%p conn=%p ep_cid=%d\n", 208 beiscsi_conn, conn, beiscsi_ep->ep_cid); 209 return beiscsi_bindconn_cid(phba, beiscsi_conn, beiscsi_ep->ep_cid); 210 } 211 212 static int beiscsi_create_ipv4_iface(struct beiscsi_hba *phba) 213 { 214 if (phba->ipv4_iface) 215 return 0; 216 217 phba->ipv4_iface = iscsi_create_iface(phba->shost, 218 &beiscsi_iscsi_transport, 219 ISCSI_IFACE_TYPE_IPV4, 220 0, 0); 221 if (!phba->ipv4_iface) { 222 shost_printk(KERN_ERR, phba->shost, "Could not " 223 "create default IPv4 address.\n"); 224 return -ENODEV; 225 } 226 227 return 0; 228 } 229 230 static int beiscsi_create_ipv6_iface(struct beiscsi_hba *phba) 231 { 232 if (phba->ipv6_iface) 233 return 0; 234 235 phba->ipv6_iface = iscsi_create_iface(phba->shost, 236 &beiscsi_iscsi_transport, 237 ISCSI_IFACE_TYPE_IPV6, 238 0, 0); 239 if (!phba->ipv6_iface) { 240 shost_printk(KERN_ERR, phba->shost, "Could not " 241 "create default IPv6 address.\n"); 242 return -ENODEV; 243 } 244 245 return 0; 246 } 247 248 void beiscsi_create_def_ifaces(struct beiscsi_hba *phba) 249 { 250 struct be_cmd_get_if_info_resp if_info; 251 252 if (!mgmt_get_if_info(phba, BE2_IPV4, &if_info)) 253 beiscsi_create_ipv4_iface(phba); 254 255 if (!mgmt_get_if_info(phba, BE2_IPV6, &if_info)) 256 beiscsi_create_ipv6_iface(phba); 257 } 258 259 void beiscsi_destroy_def_ifaces(struct beiscsi_hba *phba) 260 { 261 if (phba->ipv6_iface) 262 iscsi_destroy_iface(phba->ipv6_iface); 263 if (phba->ipv4_iface) 264 iscsi_destroy_iface(phba->ipv4_iface); 265 } 266 267 static int 268 beiscsi_set_static_ip(struct Scsi_Host *shost, 269 struct iscsi_iface_param_info *iface_param, 270 void *data, uint32_t dt_len) 271 { 272 struct beiscsi_hba *phba = iscsi_host_priv(shost); 273 struct iscsi_iface_param_info *iface_ip = NULL; 274 struct iscsi_iface_param_info *iface_subnet = NULL; 275 struct nlattr *nla; 276 int ret; 277 278 279 switch (iface_param->param) { 280 case ISCSI_NET_PARAM_IPV4_BOOTPROTO: 281 nla = nla_find(data, dt_len, ISCSI_NET_PARAM_IPV4_ADDR); 282 if (nla) 283 iface_ip = nla_data(nla); 284 285 nla = nla_find(data, dt_len, ISCSI_NET_PARAM_IPV4_SUBNET); 286 if (nla) 287 iface_subnet = nla_data(nla); 288 break; 289 case ISCSI_NET_PARAM_IPV4_ADDR: 290 iface_ip = iface_param; 291 nla = nla_find(data, dt_len, ISCSI_NET_PARAM_IPV4_SUBNET); 292 if (nla) 293 iface_subnet = nla_data(nla); 294 break; 295 case ISCSI_NET_PARAM_IPV4_SUBNET: 296 iface_subnet = iface_param; 297 nla = nla_find(data, dt_len, ISCSI_NET_PARAM_IPV4_ADDR); 298 if (nla) 299 iface_ip = nla_data(nla); 300 break; 301 default: 302 shost_printk(KERN_ERR, shost, "Unsupported param %d\n", 303 iface_param->param); 304 } 305 306 if (!iface_ip || !iface_subnet) { 307 shost_printk(KERN_ERR, shost, "IP and Subnet Mask required\n"); 308 return -EINVAL; 309 } 310 311 ret = mgmt_set_ip(phba, iface_ip, iface_subnet, 312 ISCSI_BOOTPROTO_STATIC); 313 314 return ret; 315 } 316 317 static int 318 beiscsi_set_ipv4(struct Scsi_Host *shost, 319 struct iscsi_iface_param_info *iface_param, 320 void *data, uint32_t dt_len) 321 { 322 struct beiscsi_hba *phba = iscsi_host_priv(shost); 323 int ret = 0; 324 325 /* Check the param */ 326 switch (iface_param->param) { 327 case ISCSI_NET_PARAM_IPV4_GW: 328 ret = mgmt_set_gateway(phba, iface_param); 329 break; 330 case ISCSI_NET_PARAM_IPV4_BOOTPROTO: 331 if (iface_param->value[0] == ISCSI_BOOTPROTO_DHCP) 332 ret = mgmt_set_ip(phba, iface_param, 333 NULL, ISCSI_BOOTPROTO_DHCP); 334 else if (iface_param->value[0] == ISCSI_BOOTPROTO_STATIC) 335 ret = beiscsi_set_static_ip(shost, iface_param, 336 data, dt_len); 337 else 338 shost_printk(KERN_ERR, shost, "Invalid BOOTPROTO: %d\n", 339 iface_param->value[0]); 340 break; 341 case ISCSI_NET_PARAM_IFACE_ENABLE: 342 if (iface_param->value[0] == ISCSI_IFACE_ENABLE) 343 ret = beiscsi_create_ipv4_iface(phba); 344 else 345 iscsi_destroy_iface(phba->ipv4_iface); 346 break; 347 case ISCSI_NET_PARAM_IPV4_SUBNET: 348 case ISCSI_NET_PARAM_IPV4_ADDR: 349 ret = beiscsi_set_static_ip(shost, iface_param, 350 data, dt_len); 351 break; 352 default: 353 shost_printk(KERN_ERR, shost, "Param %d not supported\n", 354 iface_param->param); 355 } 356 357 return ret; 358 } 359 360 static int 361 beiscsi_set_ipv6(struct Scsi_Host *shost, 362 struct iscsi_iface_param_info *iface_param, 363 void *data, uint32_t dt_len) 364 { 365 struct beiscsi_hba *phba = iscsi_host_priv(shost); 366 int ret = 0; 367 368 switch (iface_param->param) { 369 case ISCSI_NET_PARAM_IFACE_ENABLE: 370 if (iface_param->value[0] == ISCSI_IFACE_ENABLE) 371 ret = beiscsi_create_ipv6_iface(phba); 372 else { 373 iscsi_destroy_iface(phba->ipv6_iface); 374 ret = 0; 375 } 376 break; 377 case ISCSI_NET_PARAM_IPV6_ADDR: 378 ret = mgmt_set_ip(phba, iface_param, NULL, 379 ISCSI_BOOTPROTO_STATIC); 380 break; 381 default: 382 shost_printk(KERN_ERR, shost, "Param %d not supported\n", 383 iface_param->param); 384 } 385 386 return ret; 387 } 388 389 int be2iscsi_iface_set_param(struct Scsi_Host *shost, 390 void *data, uint32_t dt_len) 391 { 392 struct iscsi_iface_param_info *iface_param = NULL; 393 struct nlattr *attrib; 394 uint32_t rm_len = dt_len; 395 int ret = 0 ; 396 397 nla_for_each_attr(attrib, data, dt_len, rm_len) { 398 iface_param = nla_data(attrib); 399 400 if (iface_param->param_type != ISCSI_NET_PARAM) 401 continue; 402 403 /* 404 * BE2ISCSI only supports 1 interface 405 */ 406 if (iface_param->iface_num) { 407 shost_printk(KERN_ERR, shost, "Invalid iface_num %d." 408 "Only iface_num 0 is supported.\n", 409 iface_param->iface_num); 410 return -EINVAL; 411 } 412 413 switch (iface_param->iface_type) { 414 case ISCSI_IFACE_TYPE_IPV4: 415 ret = beiscsi_set_ipv4(shost, iface_param, 416 data, dt_len); 417 break; 418 case ISCSI_IFACE_TYPE_IPV6: 419 ret = beiscsi_set_ipv6(shost, iface_param, 420 data, dt_len); 421 break; 422 default: 423 shost_printk(KERN_ERR, shost, 424 "Invalid iface type :%d passed\n", 425 iface_param->iface_type); 426 break; 427 } 428 429 if (ret) 430 return ret; 431 } 432 433 return ret; 434 } 435 436 static int be2iscsi_get_if_param(struct beiscsi_hba *phba, 437 struct iscsi_iface *iface, int param, 438 char *buf) 439 { 440 struct be_cmd_get_if_info_resp if_info; 441 int len, ip_type = BE2_IPV4; 442 443 memset(&if_info, 0, sizeof(if_info)); 444 445 if (iface->iface_type == ISCSI_IFACE_TYPE_IPV6) 446 ip_type = BE2_IPV6; 447 448 len = mgmt_get_if_info(phba, ip_type, &if_info); 449 if (len) 450 return len; 451 452 switch (param) { 453 case ISCSI_NET_PARAM_IPV4_ADDR: 454 len = sprintf(buf, "%pI4\n", &if_info.ip_addr.addr); 455 break; 456 case ISCSI_NET_PARAM_IPV6_ADDR: 457 len = sprintf(buf, "%pI6\n", &if_info.ip_addr.addr); 458 break; 459 case ISCSI_NET_PARAM_IPV4_BOOTPROTO: 460 if (!if_info.dhcp_state) 461 len = sprintf(buf, "static"); 462 else 463 len = sprintf(buf, "dhcp"); 464 break; 465 case ISCSI_NET_PARAM_IPV4_SUBNET: 466 len = sprintf(buf, "%pI4\n", &if_info.ip_addr.subnet_mask); 467 break; 468 default: 469 WARN_ON(1); 470 } 471 472 return len; 473 } 474 475 int be2iscsi_iface_get_param(struct iscsi_iface *iface, 476 enum iscsi_param_type param_type, 477 int param, char *buf) 478 { 479 struct Scsi_Host *shost = iscsi_iface_to_shost(iface); 480 struct beiscsi_hba *phba = iscsi_host_priv(shost); 481 struct be_cmd_get_def_gateway_resp gateway; 482 int len = -ENOSYS; 483 484 switch (param) { 485 case ISCSI_NET_PARAM_IPV4_ADDR: 486 case ISCSI_NET_PARAM_IPV4_SUBNET: 487 case ISCSI_NET_PARAM_IPV4_BOOTPROTO: 488 case ISCSI_NET_PARAM_IPV6_ADDR: 489 len = be2iscsi_get_if_param(phba, iface, param, buf); 490 break; 491 case ISCSI_NET_PARAM_IFACE_ENABLE: 492 len = sprintf(buf, "enabled"); 493 break; 494 case ISCSI_NET_PARAM_IPV4_GW: 495 memset(&gateway, 0, sizeof(gateway)); 496 len = mgmt_get_gateway(phba, BE2_IPV4, &gateway); 497 if (!len) 498 len = sprintf(buf, "%pI4\n", &gateway.ip_addr.addr); 499 break; 500 default: 501 len = -ENOSYS; 502 } 503 504 return len; 505 } 506 507 /** 508 * beiscsi_ep_get_param - get the iscsi parameter 509 * @ep: pointer to iscsi ep 510 * @param: parameter type identifier 511 * @buf: buffer pointer 512 * 513 * returns iscsi parameter 514 */ 515 int beiscsi_ep_get_param(struct iscsi_endpoint *ep, 516 enum iscsi_param param, char *buf) 517 { 518 struct beiscsi_endpoint *beiscsi_ep = ep->dd_data; 519 int len = 0; 520 521 SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_get_param, param= %d\n", param); 522 523 switch (param) { 524 case ISCSI_PARAM_CONN_PORT: 525 len = sprintf(buf, "%hu\n", beiscsi_ep->dst_tcpport); 526 break; 527 case ISCSI_PARAM_CONN_ADDRESS: 528 if (beiscsi_ep->ip_type == BE2_IPV4) 529 len = sprintf(buf, "%pI4\n", &beiscsi_ep->dst_addr); 530 else 531 len = sprintf(buf, "%pI6\n", &beiscsi_ep->dst6_addr); 532 break; 533 default: 534 return -ENOSYS; 535 } 536 return len; 537 } 538 539 int beiscsi_set_param(struct iscsi_cls_conn *cls_conn, 540 enum iscsi_param param, char *buf, int buflen) 541 { 542 struct iscsi_conn *conn = cls_conn->dd_data; 543 struct iscsi_session *session = conn->session; 544 int ret; 545 546 SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_set_param, param= %d\n", param); 547 ret = iscsi_set_param(cls_conn, param, buf, buflen); 548 if (ret) 549 return ret; 550 /* 551 * If userspace tried to set the value to higher than we can 552 * support override here. 553 */ 554 switch (param) { 555 case ISCSI_PARAM_FIRST_BURST: 556 if (session->first_burst > 8192) 557 session->first_burst = 8192; 558 break; 559 case ISCSI_PARAM_MAX_RECV_DLENGTH: 560 if (conn->max_recv_dlength > 65536) 561 conn->max_recv_dlength = 65536; 562 break; 563 case ISCSI_PARAM_MAX_BURST: 564 if (session->max_burst > 262144) 565 session->max_burst = 262144; 566 break; 567 case ISCSI_PARAM_MAX_XMIT_DLENGTH: 568 if ((conn->max_xmit_dlength > 65536) || 569 (conn->max_xmit_dlength == 0)) 570 conn->max_xmit_dlength = 65536; 571 default: 572 return 0; 573 } 574 575 return 0; 576 } 577 578 /** 579 * beiscsi_get_initname - Read Initiator Name from flash 580 * @buf: buffer bointer 581 * @phba: The device priv structure instance 582 * 583 * returns number of bytes 584 */ 585 static int beiscsi_get_initname(char *buf, struct beiscsi_hba *phba) 586 { 587 int rc; 588 unsigned int tag, wrb_num; 589 unsigned short status, extd_status; 590 struct be_mcc_wrb *wrb; 591 struct be_cmd_hba_name *resp; 592 struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q; 593 594 tag = be_cmd_get_initname(phba); 595 if (!tag) { 596 SE_DEBUG(DBG_LVL_1, "Getting Initiator Name Failed\n"); 597 return -EBUSY; 598 } else 599 wait_event_interruptible(phba->ctrl.mcc_wait[tag], 600 phba->ctrl.mcc_numtag[tag]); 601 602 wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16; 603 extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8; 604 status = phba->ctrl.mcc_numtag[tag] & 0x000000FF; 605 606 if (status || extd_status) { 607 SE_DEBUG(DBG_LVL_1, "MailBox Command Failed with " 608 "status = %d extd_status = %d\n", 609 status, extd_status); 610 free_mcc_tag(&phba->ctrl, tag); 611 return -EAGAIN; 612 } 613 wrb = queue_get_wrb(mccq, wrb_num); 614 free_mcc_tag(&phba->ctrl, tag); 615 resp = embedded_payload(wrb); 616 rc = sprintf(buf, "%s\n", resp->initiator_name); 617 return rc; 618 } 619 620 /** 621 * beiscsi_get_port_state - Get the Port State 622 * @shost : pointer to scsi_host structure 623 * 624 * returns number of bytes 625 */ 626 static void beiscsi_get_port_state(struct Scsi_Host *shost) 627 { 628 struct beiscsi_hba *phba = iscsi_host_priv(shost); 629 struct iscsi_cls_host *ihost = shost->shost_data; 630 631 ihost->port_state = (phba->state == BE_ADAPTER_UP) ? 632 ISCSI_PORT_STATE_UP : ISCSI_PORT_STATE_DOWN; 633 } 634 635 /** 636 * beiscsi_get_port_speed - Get the Port Speed from Adapter 637 * @shost : pointer to scsi_host structure 638 * 639 * returns Success/Failure 640 */ 641 static int beiscsi_get_port_speed(struct Scsi_Host *shost) 642 { 643 unsigned int tag, wrb_num; 644 unsigned short status, extd_status; 645 struct be_mcc_wrb *wrb; 646 struct be_cmd_ntwk_link_status_resp *resp; 647 struct beiscsi_hba *phba = iscsi_host_priv(shost); 648 struct iscsi_cls_host *ihost = shost->shost_data; 649 struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q; 650 651 tag = be_cmd_get_port_speed(phba); 652 if (!tag) { 653 SE_DEBUG(DBG_LVL_1, "Getting Port Speed Failed\n"); 654 return -EBUSY; 655 } else 656 wait_event_interruptible(phba->ctrl.mcc_wait[tag], 657 phba->ctrl.mcc_numtag[tag]); 658 659 wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16; 660 extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8; 661 status = phba->ctrl.mcc_numtag[tag] & 0x000000FF; 662 663 if (status || extd_status) { 664 SE_DEBUG(DBG_LVL_1, "MailBox Command Failed with " 665 "status = %d extd_status = %d\n", 666 status, extd_status); 667 free_mcc_tag(&phba->ctrl, tag); 668 return -EAGAIN; 669 } 670 wrb = queue_get_wrb(mccq, wrb_num); 671 free_mcc_tag(&phba->ctrl, tag); 672 resp = embedded_payload(wrb); 673 674 switch (resp->mac_speed) { 675 case BE2ISCSI_LINK_SPEED_10MBPS: 676 ihost->port_speed = ISCSI_PORT_SPEED_10MBPS; 677 break; 678 case BE2ISCSI_LINK_SPEED_100MBPS: 679 ihost->port_speed = BE2ISCSI_LINK_SPEED_100MBPS; 680 break; 681 case BE2ISCSI_LINK_SPEED_1GBPS: 682 ihost->port_speed = ISCSI_PORT_SPEED_1GBPS; 683 break; 684 case BE2ISCSI_LINK_SPEED_10GBPS: 685 ihost->port_speed = ISCSI_PORT_SPEED_10GBPS; 686 break; 687 default: 688 ihost->port_speed = ISCSI_PORT_SPEED_UNKNOWN; 689 } 690 return 0; 691 } 692 693 /** 694 * beiscsi_get_host_param - get the iscsi parameter 695 * @shost: pointer to scsi_host structure 696 * @param: parameter type identifier 697 * @buf: buffer pointer 698 * 699 * returns host parameter 700 */ 701 int beiscsi_get_host_param(struct Scsi_Host *shost, 702 enum iscsi_host_param param, char *buf) 703 { 704 struct beiscsi_hba *phba = iscsi_host_priv(shost); 705 int status = 0; 706 707 SE_DEBUG(DBG_LVL_8, "In beiscsi_get_host_param, param= %d\n", param); 708 switch (param) { 709 case ISCSI_HOST_PARAM_HWADDRESS: 710 status = beiscsi_get_macaddr(buf, phba); 711 if (status < 0) { 712 SE_DEBUG(DBG_LVL_1, "beiscsi_get_macaddr Failed\n"); 713 return status; 714 } 715 break; 716 case ISCSI_HOST_PARAM_INITIATOR_NAME: 717 status = beiscsi_get_initname(buf, phba); 718 if (status < 0) { 719 SE_DEBUG(DBG_LVL_1, 720 "Retreiving Initiator Name Failed\n"); 721 return status; 722 } 723 break; 724 case ISCSI_HOST_PARAM_PORT_STATE: 725 beiscsi_get_port_state(shost); 726 status = sprintf(buf, "%s\n", iscsi_get_port_state_name(shost)); 727 break; 728 case ISCSI_HOST_PARAM_PORT_SPEED: 729 status = beiscsi_get_port_speed(shost); 730 if (status) { 731 SE_DEBUG(DBG_LVL_1, 732 "Retreiving Port Speed Failed\n"); 733 return status; 734 } 735 status = sprintf(buf, "%s\n", iscsi_get_port_speed_name(shost)); 736 break; 737 default: 738 return iscsi_host_get_param(shost, param, buf); 739 } 740 return status; 741 } 742 743 int beiscsi_get_macaddr(char *buf, struct beiscsi_hba *phba) 744 { 745 struct be_cmd_get_nic_conf_resp resp; 746 int rc; 747 748 if (strlen(phba->mac_address)) 749 return strlcpy(buf, phba->mac_address, PAGE_SIZE); 750 751 memset(&resp, 0, sizeof(resp)); 752 rc = mgmt_get_nic_conf(phba, &resp); 753 if (rc) 754 return rc; 755 756 memcpy(phba->mac_address, resp.mac_address, ETH_ALEN); 757 return sysfs_format_mac(buf, phba->mac_address, ETH_ALEN); 758 } 759 760 /** 761 * beiscsi_conn_get_stats - get the iscsi stats 762 * @cls_conn: pointer to iscsi cls conn 763 * @stats: pointer to iscsi_stats structure 764 * 765 * returns iscsi stats 766 */ 767 void beiscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn, 768 struct iscsi_stats *stats) 769 { 770 struct iscsi_conn *conn = cls_conn->dd_data; 771 772 SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_get_stats\n"); 773 stats->txdata_octets = conn->txdata_octets; 774 stats->rxdata_octets = conn->rxdata_octets; 775 stats->dataout_pdus = conn->dataout_pdus_cnt; 776 stats->scsirsp_pdus = conn->scsirsp_pdus_cnt; 777 stats->scsicmd_pdus = conn->scsicmd_pdus_cnt; 778 stats->datain_pdus = conn->datain_pdus_cnt; 779 stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt; 780 stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt; 781 stats->r2t_pdus = conn->r2t_pdus_cnt; 782 stats->digest_err = 0; 783 stats->timeout_err = 0; 784 stats->custom_length = 0; 785 strcpy(stats->custom[0].desc, "eh_abort_cnt"); 786 stats->custom[0].value = conn->eh_abort_cnt; 787 } 788 789 /** 790 * beiscsi_set_params_for_offld - get the parameters for offload 791 * @beiscsi_conn: pointer to beiscsi_conn 792 * @params: pointer to offload_params structure 793 */ 794 static void beiscsi_set_params_for_offld(struct beiscsi_conn *beiscsi_conn, 795 struct beiscsi_offload_params *params) 796 { 797 struct iscsi_conn *conn = beiscsi_conn->conn; 798 struct iscsi_session *session = conn->session; 799 800 AMAP_SET_BITS(struct amap_beiscsi_offload_params, max_burst_length, 801 params, session->max_burst); 802 AMAP_SET_BITS(struct amap_beiscsi_offload_params, 803 max_send_data_segment_length, params, 804 conn->max_xmit_dlength); 805 AMAP_SET_BITS(struct amap_beiscsi_offload_params, first_burst_length, 806 params, session->first_burst); 807 AMAP_SET_BITS(struct amap_beiscsi_offload_params, erl, params, 808 session->erl); 809 AMAP_SET_BITS(struct amap_beiscsi_offload_params, dde, params, 810 conn->datadgst_en); 811 AMAP_SET_BITS(struct amap_beiscsi_offload_params, hde, params, 812 conn->hdrdgst_en); 813 AMAP_SET_BITS(struct amap_beiscsi_offload_params, ir2t, params, 814 session->initial_r2t_en); 815 AMAP_SET_BITS(struct amap_beiscsi_offload_params, imd, params, 816 session->imm_data_en); 817 AMAP_SET_BITS(struct amap_beiscsi_offload_params, exp_statsn, params, 818 (conn->exp_statsn - 1)); 819 } 820 821 /** 822 * beiscsi_conn_start - offload of session to chip 823 * @cls_conn: pointer to beiscsi_conn 824 */ 825 int beiscsi_conn_start(struct iscsi_cls_conn *cls_conn) 826 { 827 struct iscsi_conn *conn = cls_conn->dd_data; 828 struct beiscsi_conn *beiscsi_conn = conn->dd_data; 829 struct beiscsi_endpoint *beiscsi_ep; 830 struct beiscsi_offload_params params; 831 832 SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_start\n"); 833 memset(¶ms, 0, sizeof(struct beiscsi_offload_params)); 834 beiscsi_ep = beiscsi_conn->ep; 835 if (!beiscsi_ep) 836 SE_DEBUG(DBG_LVL_1, "In beiscsi_conn_start , no beiscsi_ep\n"); 837 838 beiscsi_conn->login_in_progress = 0; 839 beiscsi_set_params_for_offld(beiscsi_conn, ¶ms); 840 beiscsi_offload_connection(beiscsi_conn, ¶ms); 841 iscsi_conn_start(cls_conn); 842 return 0; 843 } 844 845 /** 846 * beiscsi_get_cid - Allocate a cid 847 * @phba: The phba instance 848 */ 849 static int beiscsi_get_cid(struct beiscsi_hba *phba) 850 { 851 unsigned short cid = 0xFFFF; 852 853 if (!phba->avlbl_cids) 854 return cid; 855 856 cid = phba->cid_array[phba->cid_alloc++]; 857 if (phba->cid_alloc == phba->params.cxns_per_ctrl) 858 phba->cid_alloc = 0; 859 phba->avlbl_cids--; 860 return cid; 861 } 862 863 /** 864 * beiscsi_put_cid - Free the cid 865 * @phba: The phba for which the cid is being freed 866 * @cid: The cid to free 867 */ 868 static void beiscsi_put_cid(struct beiscsi_hba *phba, unsigned short cid) 869 { 870 phba->avlbl_cids++; 871 phba->cid_array[phba->cid_free++] = cid; 872 if (phba->cid_free == phba->params.cxns_per_ctrl) 873 phba->cid_free = 0; 874 } 875 876 /** 877 * beiscsi_free_ep - free endpoint 878 * @ep: pointer to iscsi endpoint structure 879 */ 880 static void beiscsi_free_ep(struct beiscsi_endpoint *beiscsi_ep) 881 { 882 struct beiscsi_hba *phba = beiscsi_ep->phba; 883 884 beiscsi_put_cid(phba, beiscsi_ep->ep_cid); 885 beiscsi_ep->phba = NULL; 886 } 887 888 /** 889 * beiscsi_open_conn - Ask FW to open a TCP connection 890 * @ep: endpoint to be used 891 * @src_addr: The source IP address 892 * @dst_addr: The Destination IP address 893 * 894 * Asks the FW to open a TCP connection 895 */ 896 static int beiscsi_open_conn(struct iscsi_endpoint *ep, 897 struct sockaddr *src_addr, 898 struct sockaddr *dst_addr, int non_blocking) 899 { 900 struct beiscsi_endpoint *beiscsi_ep = ep->dd_data; 901 struct beiscsi_hba *phba = beiscsi_ep->phba; 902 struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q; 903 struct be_mcc_wrb *wrb; 904 struct tcp_connect_and_offload_out *ptcpcnct_out; 905 unsigned short status, extd_status; 906 struct be_dma_mem nonemb_cmd; 907 unsigned int tag, wrb_num; 908 int ret = -ENOMEM; 909 910 SE_DEBUG(DBG_LVL_8, "In beiscsi_open_conn\n"); 911 beiscsi_ep->ep_cid = beiscsi_get_cid(phba); 912 if (beiscsi_ep->ep_cid == 0xFFFF) { 913 SE_DEBUG(DBG_LVL_1, "No free cid available\n"); 914 return ret; 915 } 916 SE_DEBUG(DBG_LVL_8, "In beiscsi_open_conn, ep_cid=%d\n", 917 beiscsi_ep->ep_cid); 918 phba->ep_array[beiscsi_ep->ep_cid - 919 phba->fw_config.iscsi_cid_start] = ep; 920 if (beiscsi_ep->ep_cid > (phba->fw_config.iscsi_cid_start + 921 phba->params.cxns_per_ctrl * 2)) { 922 SE_DEBUG(DBG_LVL_1, "Failed in allocate iscsi cid\n"); 923 goto free_ep; 924 } 925 926 beiscsi_ep->cid_vld = 0; 927 nonemb_cmd.va = pci_alloc_consistent(phba->ctrl.pdev, 928 sizeof(struct tcp_connect_and_offload_in), 929 &nonemb_cmd.dma); 930 if (nonemb_cmd.va == NULL) { 931 SE_DEBUG(DBG_LVL_1, 932 "Failed to allocate memory for mgmt_open_connection" 933 "\n"); 934 beiscsi_put_cid(phba, beiscsi_ep->ep_cid); 935 return -ENOMEM; 936 } 937 nonemb_cmd.size = sizeof(struct tcp_connect_and_offload_in); 938 memset(nonemb_cmd.va, 0, nonemb_cmd.size); 939 tag = mgmt_open_connection(phba, dst_addr, beiscsi_ep, &nonemb_cmd); 940 if (!tag) { 941 SE_DEBUG(DBG_LVL_1, 942 "mgmt_open_connection Failed for cid=%d\n", 943 beiscsi_ep->ep_cid); 944 beiscsi_put_cid(phba, beiscsi_ep->ep_cid); 945 pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size, 946 nonemb_cmd.va, nonemb_cmd.dma); 947 return -EAGAIN; 948 } else { 949 wait_event_interruptible(phba->ctrl.mcc_wait[tag], 950 phba->ctrl.mcc_numtag[tag]); 951 } 952 wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16; 953 extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8; 954 status = phba->ctrl.mcc_numtag[tag] & 0x000000FF; 955 if (status || extd_status) { 956 SE_DEBUG(DBG_LVL_1, "mgmt_open_connection Failed" 957 " status = %d extd_status = %d\n", 958 status, extd_status); 959 free_mcc_tag(&phba->ctrl, tag); 960 pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size, 961 nonemb_cmd.va, nonemb_cmd.dma); 962 goto free_ep; 963 } else { 964 wrb = queue_get_wrb(mccq, wrb_num); 965 free_mcc_tag(&phba->ctrl, tag); 966 967 ptcpcnct_out = embedded_payload(wrb); 968 beiscsi_ep = ep->dd_data; 969 beiscsi_ep->fw_handle = ptcpcnct_out->connection_handle; 970 beiscsi_ep->cid_vld = 1; 971 SE_DEBUG(DBG_LVL_8, "mgmt_open_connection Success\n"); 972 } 973 pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size, 974 nonemb_cmd.va, nonemb_cmd.dma); 975 return 0; 976 977 free_ep: 978 beiscsi_free_ep(beiscsi_ep); 979 return -EBUSY; 980 } 981 982 /** 983 * beiscsi_ep_connect - Ask chip to create TCP Conn 984 * @scsi_host: Pointer to scsi_host structure 985 * @dst_addr: The IP address of Target 986 * @non_blocking: blocking or non-blocking call 987 * 988 * This routines first asks chip to create a connection and then allocates an EP 989 */ 990 struct iscsi_endpoint * 991 beiscsi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr, 992 int non_blocking) 993 { 994 struct beiscsi_hba *phba; 995 struct beiscsi_endpoint *beiscsi_ep; 996 struct iscsi_endpoint *ep; 997 int ret; 998 999 SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_connect\n"); 1000 if (shost) 1001 phba = iscsi_host_priv(shost); 1002 else { 1003 ret = -ENXIO; 1004 SE_DEBUG(DBG_LVL_1, "shost is NULL\n"); 1005 return ERR_PTR(ret); 1006 } 1007 1008 if (phba->state != BE_ADAPTER_UP) { 1009 ret = -EBUSY; 1010 SE_DEBUG(DBG_LVL_1, "The Adapter state is Not UP\n"); 1011 return ERR_PTR(ret); 1012 } 1013 1014 ep = iscsi_create_endpoint(sizeof(struct beiscsi_endpoint)); 1015 if (!ep) { 1016 ret = -ENOMEM; 1017 return ERR_PTR(ret); 1018 } 1019 1020 beiscsi_ep = ep->dd_data; 1021 beiscsi_ep->phba = phba; 1022 beiscsi_ep->openiscsi_ep = ep; 1023 ret = beiscsi_open_conn(ep, NULL, dst_addr, non_blocking); 1024 if (ret) { 1025 SE_DEBUG(DBG_LVL_1, "Failed in beiscsi_open_conn\n"); 1026 goto free_ep; 1027 } 1028 1029 return ep; 1030 1031 free_ep: 1032 iscsi_destroy_endpoint(ep); 1033 return ERR_PTR(ret); 1034 } 1035 1036 /** 1037 * beiscsi_ep_poll - Poll to see if connection is established 1038 * @ep: endpoint to be used 1039 * @timeout_ms: timeout specified in millisecs 1040 * 1041 * Poll to see if TCP connection established 1042 */ 1043 int beiscsi_ep_poll(struct iscsi_endpoint *ep, int timeout_ms) 1044 { 1045 struct beiscsi_endpoint *beiscsi_ep = ep->dd_data; 1046 1047 SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_poll\n"); 1048 if (beiscsi_ep->cid_vld == 1) 1049 return 1; 1050 else 1051 return 0; 1052 } 1053 1054 /** 1055 * beiscsi_close_conn - Upload the connection 1056 * @ep: The iscsi endpoint 1057 * @flag: The type of connection closure 1058 */ 1059 static int beiscsi_close_conn(struct beiscsi_endpoint *beiscsi_ep, int flag) 1060 { 1061 int ret = 0; 1062 unsigned int tag; 1063 struct beiscsi_hba *phba = beiscsi_ep->phba; 1064 1065 tag = mgmt_upload_connection(phba, beiscsi_ep->ep_cid, flag); 1066 if (!tag) { 1067 SE_DEBUG(DBG_LVL_8, "upload failed for cid 0x%x\n", 1068 beiscsi_ep->ep_cid); 1069 ret = -EAGAIN; 1070 } else { 1071 wait_event_interruptible(phba->ctrl.mcc_wait[tag], 1072 phba->ctrl.mcc_numtag[tag]); 1073 free_mcc_tag(&phba->ctrl, tag); 1074 } 1075 return ret; 1076 } 1077 1078 /** 1079 * beiscsi_unbind_conn_to_cid - Unbind the beiscsi_conn from phba conn table 1080 * @phba: The phba instance 1081 * @cid: The cid to free 1082 */ 1083 static int beiscsi_unbind_conn_to_cid(struct beiscsi_hba *phba, 1084 unsigned int cid) 1085 { 1086 if (phba->conn_table[cid]) 1087 phba->conn_table[cid] = NULL; 1088 else { 1089 SE_DEBUG(DBG_LVL_8, "Connection table Not occupied.\n"); 1090 return -EINVAL; 1091 } 1092 return 0; 1093 } 1094 1095 /** 1096 * beiscsi_ep_disconnect - Tears down the TCP connection 1097 * @ep: endpoint to be used 1098 * 1099 * Tears down the TCP connection 1100 */ 1101 void beiscsi_ep_disconnect(struct iscsi_endpoint *ep) 1102 { 1103 struct beiscsi_conn *beiscsi_conn; 1104 struct beiscsi_endpoint *beiscsi_ep; 1105 struct beiscsi_hba *phba; 1106 unsigned int tag; 1107 unsigned short savecfg_flag = CMD_ISCSI_SESSION_SAVE_CFG_ON_FLASH; 1108 1109 beiscsi_ep = ep->dd_data; 1110 phba = beiscsi_ep->phba; 1111 SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_disconnect for ep_cid = %d\n", 1112 beiscsi_ep->ep_cid); 1113 1114 if (!beiscsi_ep->conn) { 1115 SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_disconnect, no " 1116 "beiscsi_ep\n"); 1117 return; 1118 } 1119 beiscsi_conn = beiscsi_ep->conn; 1120 iscsi_suspend_queue(beiscsi_conn->conn); 1121 1122 SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_disconnect ep_cid = %d\n", 1123 beiscsi_ep->ep_cid); 1124 1125 tag = mgmt_invalidate_connection(phba, beiscsi_ep, 1126 beiscsi_ep->ep_cid, 1, 1127 savecfg_flag); 1128 if (!tag) { 1129 SE_DEBUG(DBG_LVL_1, 1130 "mgmt_invalidate_connection Failed for cid=%d\n", 1131 beiscsi_ep->ep_cid); 1132 } else { 1133 wait_event_interruptible(phba->ctrl.mcc_wait[tag], 1134 phba->ctrl.mcc_numtag[tag]); 1135 free_mcc_tag(&phba->ctrl, tag); 1136 } 1137 1138 beiscsi_close_conn(beiscsi_ep, CONNECTION_UPLOAD_GRACEFUL); 1139 beiscsi_free_ep(beiscsi_ep); 1140 beiscsi_unbind_conn_to_cid(phba, beiscsi_ep->ep_cid); 1141 iscsi_destroy_endpoint(beiscsi_ep->openiscsi_ep); 1142 } 1143 1144 umode_t be2iscsi_attr_is_visible(int param_type, int param) 1145 { 1146 switch (param_type) { 1147 case ISCSI_NET_PARAM: 1148 switch (param) { 1149 case ISCSI_NET_PARAM_IFACE_ENABLE: 1150 case ISCSI_NET_PARAM_IPV4_ADDR: 1151 case ISCSI_NET_PARAM_IPV4_SUBNET: 1152 case ISCSI_NET_PARAM_IPV4_BOOTPROTO: 1153 case ISCSI_NET_PARAM_IPV4_GW: 1154 case ISCSI_NET_PARAM_IPV6_ADDR: 1155 return S_IRUGO; 1156 default: 1157 return 0; 1158 } 1159 case ISCSI_HOST_PARAM: 1160 switch (param) { 1161 case ISCSI_HOST_PARAM_HWADDRESS: 1162 case ISCSI_HOST_PARAM_INITIATOR_NAME: 1163 case ISCSI_HOST_PARAM_PORT_STATE: 1164 case ISCSI_HOST_PARAM_PORT_SPEED: 1165 return S_IRUGO; 1166 default: 1167 return 0; 1168 } 1169 case ISCSI_PARAM: 1170 switch (param) { 1171 case ISCSI_PARAM_MAX_RECV_DLENGTH: 1172 case ISCSI_PARAM_MAX_XMIT_DLENGTH: 1173 case ISCSI_PARAM_HDRDGST_EN: 1174 case ISCSI_PARAM_DATADGST_EN: 1175 case ISCSI_PARAM_CONN_ADDRESS: 1176 case ISCSI_PARAM_CONN_PORT: 1177 case ISCSI_PARAM_EXP_STATSN: 1178 case ISCSI_PARAM_PERSISTENT_ADDRESS: 1179 case ISCSI_PARAM_PERSISTENT_PORT: 1180 case ISCSI_PARAM_PING_TMO: 1181 case ISCSI_PARAM_RECV_TMO: 1182 case ISCSI_PARAM_INITIAL_R2T_EN: 1183 case ISCSI_PARAM_MAX_R2T: 1184 case ISCSI_PARAM_IMM_DATA_EN: 1185 case ISCSI_PARAM_FIRST_BURST: 1186 case ISCSI_PARAM_MAX_BURST: 1187 case ISCSI_PARAM_PDU_INORDER_EN: 1188 case ISCSI_PARAM_DATASEQ_INORDER_EN: 1189 case ISCSI_PARAM_ERL: 1190 case ISCSI_PARAM_TARGET_NAME: 1191 case ISCSI_PARAM_TPGT: 1192 case ISCSI_PARAM_USERNAME: 1193 case ISCSI_PARAM_PASSWORD: 1194 case ISCSI_PARAM_USERNAME_IN: 1195 case ISCSI_PARAM_PASSWORD_IN: 1196 case ISCSI_PARAM_FAST_ABORT: 1197 case ISCSI_PARAM_ABORT_TMO: 1198 case ISCSI_PARAM_LU_RESET_TMO: 1199 case ISCSI_PARAM_IFACE_NAME: 1200 case ISCSI_PARAM_INITIATOR_NAME: 1201 return S_IRUGO; 1202 default: 1203 return 0; 1204 } 1205 } 1206 1207 return 0; 1208 } 1209