+ return 0;
+}
+
+/**
+ * zfcp_fsf_exchange_port_data - request information about local port
+ * @erp_action: ERP action for the adapter for which port data is requested
+ * @adapter: for which port data is requested
+ * @data: response to exchange port data request
+ */
+int
+zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action,
+ struct zfcp_adapter *adapter,
+ struct fsf_qtcb_bottom_port *data)
+{
+ volatile struct qdio_buffer_element *sbale;
+ struct zfcp_fsf_req *fsf_req;
+ unsigned long lock_flags;
+ int retval = 0;
+
+ if (!(adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT)) {
+ ZFCP_LOG_INFO("error: exchange port data "
+ "command not supported by adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ return -EOPNOTSUPP;
+ }
+
+ /* setup new FSF request */
+ retval = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA,
+ erp_action ? ZFCP_REQ_AUTO_CLEANUP : 0,
+ NULL, &lock_flags, &fsf_req);
+ if (retval < 0) {
+ ZFCP_LOG_INFO("error: Out of resources. Could not create an "
+ "exchange port data request for"
+ "the adapter %s.\n",
+ zfcp_get_busid_by_adapter(adapter));
+ write_unlock_irqrestore(&adapter->request_queue.queue_lock,
+ lock_flags);
+ return retval;
+ }
+
+ if (data)
+ fsf_req->data = (unsigned long) data;
+
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
+ sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+
+ if (erp_action) {
+ erp_action->fsf_req = fsf_req;
+ fsf_req->erp_action = erp_action;
+ zfcp_erp_start_timer(fsf_req);
+ } else
+ zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT);
+
+ retval = zfcp_fsf_req_send(fsf_req);
+ if (retval) {
+ ZFCP_LOG_INFO("error: Could not send an exchange port data "
+ "command on the adapter %s\n",
+ zfcp_get_busid_by_adapter(adapter));
+ zfcp_fsf_req_free(fsf_req);
+ if (erp_action)
+ erp_action->fsf_req = NULL;
+ write_unlock_irqrestore(&adapter->request_queue.queue_lock,
+ lock_flags);
+ return retval;
+ }
+
+ write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
+
+ if (!erp_action) {
+ wait_event(fsf_req->completion_wq,
+ fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
+ zfcp_fsf_req_free(fsf_req);
+ }
+ return retval;
+}
+
+/**
+ * zfcp_fsf_exchange_port_evaluate
+ * @fsf_req: fsf_req which belongs to xchg port data request
+ * @xchg_ok: specifies if xchg port data was incomplete or complete (0/1)
+ */
+static void
+zfcp_fsf_exchange_port_evaluate(struct zfcp_fsf_req *fsf_req, int xchg_ok)
+{
+ struct zfcp_adapter *adapter;
+ struct fsf_qtcb *qtcb;
+ struct fsf_qtcb_bottom_port *bottom, *data;
+ struct Scsi_Host *shost;
+
+ adapter = fsf_req->adapter;
+ qtcb = fsf_req->qtcb;
+ bottom = &qtcb->bottom.port;
+ shost = adapter->scsi_host;
+
+ data = (struct fsf_qtcb_bottom_port*) fsf_req->data;
+ if (data)
+ memcpy(data, bottom, sizeof(struct fsf_qtcb_bottom_port));
+
+ if (adapter->connection_features & FSF_FEATURE_NPIV_MODE)
+ fc_host_permanent_port_name(shost) = bottom->wwpn;
+ else
+ fc_host_permanent_port_name(shost) = fc_host_port_name(shost);
+ fc_host_maxframe_size(shost) = bottom->maximum_frame_size;
+ fc_host_supported_speeds(shost) = bottom->supported_speed;
+}
+
+/**
+ * zfcp_fsf_exchange_port_data_handler - handler for exchange_port_data request
+ * @fsf_req: pointer to struct zfcp_fsf_req
+ */
+static void
+zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *fsf_req)
+{
+ struct zfcp_adapter *adapter;
+ struct fsf_qtcb *qtcb;
+
+ adapter = fsf_req->adapter;
+ qtcb = fsf_req->qtcb;
+
+ if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)
+ return;
+
+ switch (qtcb->header.fsf_status) {
+ case FSF_GOOD:
+ zfcp_fsf_exchange_port_evaluate(fsf_req, 1);
+ atomic_set_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status);
+ break;
+ case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE:
+ zfcp_fsf_exchange_port_evaluate(fsf_req, 0);
+ atomic_set_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status);
+ zfcp_fsf_link_down_info_eval(adapter,
+ &qtcb->header.fsf_status_qual.link_down_info);
+ break;
+ default:
+ debug_text_event(adapter->erp_dbf, 0, "xchg-port-ng");
+ debug_event(adapter->erp_dbf, 0,
+ &fsf_req->qtcb->header.fsf_status, sizeof(u32));
+ }