| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /**************************************************************************** |
|---|
| 2 | 3 | * Driver for Solarflare network controllers and boards |
|---|
| 3 | 4 | * Copyright 2008-2013 Solarflare Communications Inc. |
|---|
| 4 | | - * |
|---|
| 5 | | - * This program is free software; you can redistribute it and/or modify it |
|---|
| 6 | | - * under the terms of the GNU General Public License version 2 as published |
|---|
| 7 | | - * by the Free Software Foundation, incorporated herein by reference. |
|---|
| 8 | 5 | */ |
|---|
| 9 | 6 | |
|---|
| 10 | 7 | #include <linux/delay.h> |
|---|
| .. | .. |
|---|
| 166 | 163 | /* Serialise with efx_mcdi_ev_cpl() and efx_mcdi_ev_death() */ |
|---|
| 167 | 164 | spin_lock_bh(&mcdi->iface_lock); |
|---|
| 168 | 165 | ++mcdi->seqno; |
|---|
| 166 | + seqno = mcdi->seqno & SEQ_MASK; |
|---|
| 169 | 167 | spin_unlock_bh(&mcdi->iface_lock); |
|---|
| 170 | 168 | |
|---|
| 171 | | - seqno = mcdi->seqno & SEQ_MASK; |
|---|
| 172 | 169 | xflags = 0; |
|---|
| 173 | 170 | if (mcdi->mode == MCDI_MODE_EVENTS) |
|---|
| 174 | 171 | xflags |= MCDI_HEADER_XFLAGS_EVREQ; |
|---|
| .. | .. |
|---|
| 215 | 212 | * progress on a NIC at any one time. So no need for locking. |
|---|
| 216 | 213 | */ |
|---|
| 217 | 214 | for (i = 0; i < hdr_len / 4 && bytes < PAGE_SIZE; i++) |
|---|
| 218 | | - bytes += snprintf(buf + bytes, PAGE_SIZE - bytes, |
|---|
| 219 | | - " %08x", le32_to_cpu(hdr[i].u32[0])); |
|---|
| 215 | + bytes += scnprintf(buf + bytes, PAGE_SIZE - bytes, |
|---|
| 216 | + " %08x", |
|---|
| 217 | + le32_to_cpu(hdr[i].u32[0])); |
|---|
| 220 | 218 | |
|---|
| 221 | 219 | for (i = 0; i < inlen / 4 && bytes < PAGE_SIZE; i++) |
|---|
| 222 | | - bytes += snprintf(buf + bytes, PAGE_SIZE - bytes, |
|---|
| 223 | | - " %08x", le32_to_cpu(inbuf[i].u32[0])); |
|---|
| 220 | + bytes += scnprintf(buf + bytes, PAGE_SIZE - bytes, |
|---|
| 221 | + " %08x", |
|---|
| 222 | + le32_to_cpu(inbuf[i].u32[0])); |
|---|
| 224 | 223 | |
|---|
| 225 | 224 | netif_info(efx, hw, efx->net_dev, "MCDI RPC REQ:%s\n", buf); |
|---|
| 226 | 225 | } |
|---|
| .. | .. |
|---|
| 305 | 304 | */ |
|---|
| 306 | 305 | for (i = 0; i < hdr_len && bytes < PAGE_SIZE; i++) { |
|---|
| 307 | 306 | efx->type->mcdi_read_response(efx, &hdr, (i * 4), 4); |
|---|
| 308 | | - bytes += snprintf(buf + bytes, PAGE_SIZE - bytes, |
|---|
| 309 | | - " %08x", le32_to_cpu(hdr.u32[0])); |
|---|
| 307 | + bytes += scnprintf(buf + bytes, PAGE_SIZE - bytes, |
|---|
| 308 | + " %08x", le32_to_cpu(hdr.u32[0])); |
|---|
| 310 | 309 | } |
|---|
| 311 | 310 | |
|---|
| 312 | 311 | for (i = 0; i < data_len && bytes < PAGE_SIZE; i++) { |
|---|
| 313 | 312 | efx->type->mcdi_read_response(efx, &hdr, |
|---|
| 314 | 313 | mcdi->resp_hdr_len + (i * 4), 4); |
|---|
| 315 | | - bytes += snprintf(buf + bytes, PAGE_SIZE - bytes, |
|---|
| 316 | | - " %08x", le32_to_cpu(hdr.u32[0])); |
|---|
| 314 | + bytes += scnprintf(buf + bytes, PAGE_SIZE - bytes, |
|---|
| 315 | + " %08x", le32_to_cpu(hdr.u32[0])); |
|---|
| 317 | 316 | } |
|---|
| 318 | 317 | |
|---|
| 319 | 318 | netif_info(efx, hw, efx->net_dev, "MCDI RPC RESP:%s\n", buf); |
|---|
| .. | .. |
|---|
| 1300 | 1299 | efx_schedule_reset(efx, RESET_TYPE_MCDI_TIMEOUT); |
|---|
| 1301 | 1300 | } |
|---|
| 1302 | 1301 | |
|---|
| 1302 | +static void efx_handle_drain_event(struct efx_nic *efx) |
|---|
| 1303 | +{ |
|---|
| 1304 | + if (atomic_dec_and_test(&efx->active_queues)) |
|---|
| 1305 | + wake_up(&efx->flush_wq); |
|---|
| 1306 | + |
|---|
| 1307 | + WARN_ON(atomic_read(&efx->active_queues) < 0); |
|---|
| 1308 | +} |
|---|
| 1309 | + |
|---|
| 1303 | 1310 | /* Called from efx_farch_ev_process and efx_ef10_ev_process for MCDI events */ |
|---|
| 1304 | 1311 | void efx_mcdi_process_event(struct efx_channel *channel, |
|---|
| 1305 | 1312 | efx_qword_t *event) |
|---|
| .. | .. |
|---|
| 1330 | 1337 | efx_mcdi_process_link_change(efx, event); |
|---|
| 1331 | 1338 | break; |
|---|
| 1332 | 1339 | case MCDI_EVENT_CODE_SENSOREVT: |
|---|
| 1333 | | - efx_mcdi_sensor_event(efx, event); |
|---|
| 1340 | + efx_sensor_event(efx, event); |
|---|
| 1334 | 1341 | break; |
|---|
| 1335 | 1342 | case MCDI_EVENT_CODE_SCHEDERR: |
|---|
| 1336 | 1343 | netif_dbg(efx, hw, efx->net_dev, |
|---|
| .. | .. |
|---|
| 1372 | 1379 | BUILD_BUG_ON(MCDI_EVENT_TX_FLUSH_TO_DRIVER_LBN != |
|---|
| 1373 | 1380 | MCDI_EVENT_RX_FLUSH_TO_DRIVER_LBN); |
|---|
| 1374 | 1381 | if (!MCDI_EVENT_FIELD(*event, TX_FLUSH_TO_DRIVER)) |
|---|
| 1375 | | - efx_ef10_handle_drain_event(efx); |
|---|
| 1382 | + efx_handle_drain_event(efx); |
|---|
| 1376 | 1383 | break; |
|---|
| 1377 | 1384 | case MCDI_EVENT_CODE_TX_ERR: |
|---|
| 1378 | 1385 | case MCDI_EVENT_CODE_RX_ERR: |
|---|
| .. | .. |
|---|
| 1420 | 1427 | } |
|---|
| 1421 | 1428 | |
|---|
| 1422 | 1429 | ver_words = (__le16 *)MCDI_PTR(outbuf, GET_VERSION_OUT_VERSION); |
|---|
| 1423 | | - offset = snprintf(buf, len, "%u.%u.%u.%u", |
|---|
| 1424 | | - le16_to_cpu(ver_words[0]), le16_to_cpu(ver_words[1]), |
|---|
| 1425 | | - le16_to_cpu(ver_words[2]), le16_to_cpu(ver_words[3])); |
|---|
| 1430 | + offset = scnprintf(buf, len, "%u.%u.%u.%u", |
|---|
| 1431 | + le16_to_cpu(ver_words[0]), |
|---|
| 1432 | + le16_to_cpu(ver_words[1]), |
|---|
| 1433 | + le16_to_cpu(ver_words[2]), |
|---|
| 1434 | + le16_to_cpu(ver_words[3])); |
|---|
| 1426 | 1435 | |
|---|
| 1427 | | - /* EF10 may have multiple datapath firmware variants within a |
|---|
| 1428 | | - * single version. Report which variants are running. |
|---|
| 1436 | + if (efx->type->print_additional_fwver) |
|---|
| 1437 | + offset += efx->type->print_additional_fwver(efx, buf + offset, |
|---|
| 1438 | + len - offset); |
|---|
| 1439 | + |
|---|
| 1440 | + /* It's theoretically possible for the string to exceed 31 |
|---|
| 1441 | + * characters, though in practice the first three version |
|---|
| 1442 | + * components are short enough that this doesn't happen. |
|---|
| 1429 | 1443 | */ |
|---|
| 1430 | | - if (efx_nic_rev(efx) >= EFX_REV_HUNT_A0) { |
|---|
| 1431 | | - struct efx_ef10_nic_data *nic_data = efx->nic_data; |
|---|
| 1432 | | - |
|---|
| 1433 | | - offset += snprintf(buf + offset, len - offset, " rx%x tx%x", |
|---|
| 1434 | | - nic_data->rx_dpcpu_fw_id, |
|---|
| 1435 | | - nic_data->tx_dpcpu_fw_id); |
|---|
| 1436 | | - |
|---|
| 1437 | | - /* It's theoretically possible for the string to exceed 31 |
|---|
| 1438 | | - * characters, though in practice the first three version |
|---|
| 1439 | | - * components are short enough that this doesn't happen. |
|---|
| 1440 | | - */ |
|---|
| 1441 | | - if (WARN_ON(offset >= len)) |
|---|
| 1442 | | - buf[0] = 0; |
|---|
| 1443 | | - } |
|---|
| 1444 | + if (WARN_ON(offset >= len)) |
|---|
| 1445 | + buf[0] = 0; |
|---|
| 1444 | 1446 | |
|---|
| 1445 | 1447 | return; |
|---|
| 1446 | 1448 | |
|---|
| .. | .. |
|---|
| 1619 | 1621 | return rc; |
|---|
| 1620 | 1622 | } |
|---|
| 1621 | 1623 | |
|---|
| 1624 | +/* This function finds types using the new NVRAM_PARTITIONS mcdi. */ |
|---|
| 1625 | +static int efx_new_mcdi_nvram_types(struct efx_nic *efx, u32 *number, |
|---|
| 1626 | + u32 *nvram_types) |
|---|
| 1627 | +{ |
|---|
| 1628 | + efx_dword_t *outbuf = kzalloc(MC_CMD_NVRAM_PARTITIONS_OUT_LENMAX_MCDI2, |
|---|
| 1629 | + GFP_KERNEL); |
|---|
| 1630 | + size_t outlen; |
|---|
| 1631 | + int rc; |
|---|
| 1632 | + |
|---|
| 1633 | + if (!outbuf) |
|---|
| 1634 | + return -ENOMEM; |
|---|
| 1635 | + |
|---|
| 1636 | + BUILD_BUG_ON(MC_CMD_NVRAM_PARTITIONS_IN_LEN != 0); |
|---|
| 1637 | + |
|---|
| 1638 | + rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_PARTITIONS, NULL, 0, |
|---|
| 1639 | + outbuf, MC_CMD_NVRAM_PARTITIONS_OUT_LENMAX_MCDI2, &outlen); |
|---|
| 1640 | + if (rc) |
|---|
| 1641 | + goto fail; |
|---|
| 1642 | + |
|---|
| 1643 | + *number = MCDI_DWORD(outbuf, NVRAM_PARTITIONS_OUT_NUM_PARTITIONS); |
|---|
| 1644 | + |
|---|
| 1645 | + memcpy(nvram_types, MCDI_PTR(outbuf, NVRAM_PARTITIONS_OUT_TYPE_ID), |
|---|
| 1646 | + *number * sizeof(u32)); |
|---|
| 1647 | + |
|---|
| 1648 | +fail: |
|---|
| 1649 | + kfree(outbuf); |
|---|
| 1650 | + return rc; |
|---|
| 1651 | +} |
|---|
| 1652 | + |
|---|
| 1622 | 1653 | int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type, |
|---|
| 1623 | 1654 | size_t *size_out, size_t *erase_size_out, |
|---|
| 1624 | 1655 | bool *protected_out) |
|---|
| .. | .. |
|---|
| 1670 | 1701 | default: |
|---|
| 1671 | 1702 | return -EIO; |
|---|
| 1672 | 1703 | } |
|---|
| 1704 | +} |
|---|
| 1705 | + |
|---|
| 1706 | +/* This function tests nvram partitions using the new mcdi partition lookup scheme */ |
|---|
| 1707 | +int efx_new_mcdi_nvram_test_all(struct efx_nic *efx) |
|---|
| 1708 | +{ |
|---|
| 1709 | + u32 *nvram_types = kzalloc(MC_CMD_NVRAM_PARTITIONS_OUT_LENMAX_MCDI2, |
|---|
| 1710 | + GFP_KERNEL); |
|---|
| 1711 | + unsigned int number; |
|---|
| 1712 | + int rc, i; |
|---|
| 1713 | + |
|---|
| 1714 | + if (!nvram_types) |
|---|
| 1715 | + return -ENOMEM; |
|---|
| 1716 | + |
|---|
| 1717 | + rc = efx_new_mcdi_nvram_types(efx, &number, nvram_types); |
|---|
| 1718 | + if (rc) |
|---|
| 1719 | + goto fail; |
|---|
| 1720 | + |
|---|
| 1721 | + /* Require at least one check */ |
|---|
| 1722 | + rc = -EAGAIN; |
|---|
| 1723 | + |
|---|
| 1724 | + for (i = 0; i < number; i++) { |
|---|
| 1725 | + if (nvram_types[i] == NVRAM_PARTITION_TYPE_PARTITION_MAP || |
|---|
| 1726 | + nvram_types[i] == NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG) |
|---|
| 1727 | + continue; |
|---|
| 1728 | + |
|---|
| 1729 | + rc = efx_mcdi_nvram_test(efx, nvram_types[i]); |
|---|
| 1730 | + if (rc) |
|---|
| 1731 | + goto fail; |
|---|
| 1732 | + } |
|---|
| 1733 | + |
|---|
| 1734 | +fail: |
|---|
| 1735 | + kfree(nvram_types); |
|---|
| 1736 | + return rc; |
|---|
| 1673 | 1737 | } |
|---|
| 1674 | 1738 | |
|---|
| 1675 | 1739 | int efx_mcdi_nvram_test_all(struct efx_nic *efx) |
|---|
| .. | .. |
|---|
| 1804 | 1868 | return efx_mcdi_exit_assertion(efx); |
|---|
| 1805 | 1869 | } |
|---|
| 1806 | 1870 | |
|---|
| 1807 | | -void efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) |
|---|
| 1871 | +int efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) |
|---|
| 1808 | 1872 | { |
|---|
| 1809 | 1873 | MCDI_DECLARE_BUF(inbuf, MC_CMD_SET_ID_LED_IN_LEN); |
|---|
| 1810 | | - int rc; |
|---|
| 1811 | 1874 | |
|---|
| 1812 | 1875 | BUILD_BUG_ON(EFX_LED_OFF != MC_CMD_LED_OFF); |
|---|
| 1813 | 1876 | BUILD_BUG_ON(EFX_LED_ON != MC_CMD_LED_ON); |
|---|
| .. | .. |
|---|
| 1817 | 1880 | |
|---|
| 1818 | 1881 | MCDI_SET_DWORD(inbuf, SET_ID_LED_IN_STATE, mode); |
|---|
| 1819 | 1882 | |
|---|
| 1820 | | - rc = efx_mcdi_rpc(efx, MC_CMD_SET_ID_LED, inbuf, sizeof(inbuf), |
|---|
| 1821 | | - NULL, 0, NULL); |
|---|
| 1883 | + return efx_mcdi_rpc(efx, MC_CMD_SET_ID_LED, inbuf, sizeof(inbuf), NULL, 0, NULL); |
|---|
| 1822 | 1884 | } |
|---|
| 1823 | 1885 | |
|---|
| 1824 | 1886 | static int efx_mcdi_reset_func(struct efx_nic *efx) |
|---|
| .. | .. |
|---|
| 2074 | 2136 | |
|---|
| 2075 | 2137 | static int efx_mcdi_nvram_update_start(struct efx_nic *efx, unsigned int type) |
|---|
| 2076 | 2138 | { |
|---|
| 2077 | | - MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_UPDATE_START_IN_LEN); |
|---|
| 2139 | + MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_UPDATE_START_V2_IN_LEN); |
|---|
| 2078 | 2140 | int rc; |
|---|
| 2079 | 2141 | |
|---|
| 2080 | 2142 | MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_START_IN_TYPE, type); |
|---|
| 2143 | + MCDI_POPULATE_DWORD_1(inbuf, NVRAM_UPDATE_START_V2_IN_FLAGS, |
|---|
| 2144 | + NVRAM_UPDATE_START_V2_IN_FLAG_REPORT_VERIFY_RESULT, |
|---|
| 2145 | + 1); |
|---|
| 2081 | 2146 | |
|---|
| 2082 | 2147 | BUILD_BUG_ON(MC_CMD_NVRAM_UPDATE_START_OUT_LEN != 0); |
|---|
| 2083 | 2148 | |
|---|
| 2084 | 2149 | rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_START, inbuf, sizeof(inbuf), |
|---|
| 2085 | 2150 | NULL, 0, NULL); |
|---|
| 2151 | + |
|---|
| 2086 | 2152 | return rc; |
|---|
| 2087 | 2153 | } |
|---|
| 2088 | 2154 | |
|---|
| 2089 | 2155 | static int efx_mcdi_nvram_read(struct efx_nic *efx, unsigned int type, |
|---|
| 2090 | 2156 | loff_t offset, u8 *buffer, size_t length) |
|---|
| 2091 | 2157 | { |
|---|
| 2092 | | - MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_READ_IN_LEN); |
|---|
| 2158 | + MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_READ_IN_V2_LEN); |
|---|
| 2093 | 2159 | MCDI_DECLARE_BUF(outbuf, |
|---|
| 2094 | 2160 | MC_CMD_NVRAM_READ_OUT_LEN(EFX_MCDI_NVRAM_LEN_MAX)); |
|---|
| 2095 | 2161 | size_t outlen; |
|---|
| .. | .. |
|---|
| 2098 | 2164 | MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_TYPE, type); |
|---|
| 2099 | 2165 | MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_OFFSET, offset); |
|---|
| 2100 | 2166 | MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_LENGTH, length); |
|---|
| 2167 | + MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_V2_MODE, |
|---|
| 2168 | + MC_CMD_NVRAM_READ_IN_V2_DEFAULT); |
|---|
| 2101 | 2169 | |
|---|
| 2102 | 2170 | rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_READ, inbuf, sizeof(inbuf), |
|---|
| 2103 | 2171 | outbuf, sizeof(outbuf), &outlen); |
|---|
| .. | .. |
|---|
| 2147 | 2215 | |
|---|
| 2148 | 2216 | static int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type) |
|---|
| 2149 | 2217 | { |
|---|
| 2150 | | - MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_UPDATE_FINISH_IN_LEN); |
|---|
| 2151 | | - int rc; |
|---|
| 2218 | + MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_UPDATE_FINISH_V2_IN_LEN); |
|---|
| 2219 | + MCDI_DECLARE_BUF(outbuf, MC_CMD_NVRAM_UPDATE_FINISH_V2_OUT_LEN); |
|---|
| 2220 | + size_t outlen; |
|---|
| 2221 | + int rc, rc2; |
|---|
| 2152 | 2222 | |
|---|
| 2153 | 2223 | MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_FINISH_IN_TYPE, type); |
|---|
| 2154 | | - |
|---|
| 2155 | | - BUILD_BUG_ON(MC_CMD_NVRAM_UPDATE_FINISH_OUT_LEN != 0); |
|---|
| 2224 | + /* Always set this flag. Old firmware ignores it */ |
|---|
| 2225 | + MCDI_POPULATE_DWORD_1(inbuf, NVRAM_UPDATE_FINISH_V2_IN_FLAGS, |
|---|
| 2226 | + NVRAM_UPDATE_FINISH_V2_IN_FLAG_REPORT_VERIFY_RESULT, |
|---|
| 2227 | + 1); |
|---|
| 2156 | 2228 | |
|---|
| 2157 | 2229 | rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_FINISH, inbuf, sizeof(inbuf), |
|---|
| 2158 | | - NULL, 0, NULL); |
|---|
| 2230 | + outbuf, sizeof(outbuf), &outlen); |
|---|
| 2231 | + if (!rc && outlen >= MC_CMD_NVRAM_UPDATE_FINISH_V2_OUT_LEN) { |
|---|
| 2232 | + rc2 = MCDI_DWORD(outbuf, NVRAM_UPDATE_FINISH_V2_OUT_RESULT_CODE); |
|---|
| 2233 | + if (rc2 != MC_CMD_NVRAM_VERIFY_RC_SUCCESS) |
|---|
| 2234 | + netif_err(efx, drv, efx->net_dev, |
|---|
| 2235 | + "NVRAM update failed verification with code 0x%x\n", |
|---|
| 2236 | + rc2); |
|---|
| 2237 | + switch (rc2) { |
|---|
| 2238 | + case MC_CMD_NVRAM_VERIFY_RC_SUCCESS: |
|---|
| 2239 | + break; |
|---|
| 2240 | + case MC_CMD_NVRAM_VERIFY_RC_CMS_CHECK_FAILED: |
|---|
| 2241 | + case MC_CMD_NVRAM_VERIFY_RC_MESSAGE_DIGEST_CHECK_FAILED: |
|---|
| 2242 | + case MC_CMD_NVRAM_VERIFY_RC_SIGNATURE_CHECK_FAILED: |
|---|
| 2243 | + case MC_CMD_NVRAM_VERIFY_RC_TRUSTED_APPROVERS_CHECK_FAILED: |
|---|
| 2244 | + case MC_CMD_NVRAM_VERIFY_RC_SIGNATURE_CHAIN_CHECK_FAILED: |
|---|
| 2245 | + rc = -EIO; |
|---|
| 2246 | + break; |
|---|
| 2247 | + case MC_CMD_NVRAM_VERIFY_RC_INVALID_CMS_FORMAT: |
|---|
| 2248 | + case MC_CMD_NVRAM_VERIFY_RC_BAD_MESSAGE_DIGEST: |
|---|
| 2249 | + rc = -EINVAL; |
|---|
| 2250 | + break; |
|---|
| 2251 | + case MC_CMD_NVRAM_VERIFY_RC_NO_VALID_SIGNATURES: |
|---|
| 2252 | + case MC_CMD_NVRAM_VERIFY_RC_NO_TRUSTED_APPROVERS: |
|---|
| 2253 | + case MC_CMD_NVRAM_VERIFY_RC_NO_SIGNATURE_MATCH: |
|---|
| 2254 | + rc = -EPERM; |
|---|
| 2255 | + break; |
|---|
| 2256 | + default: |
|---|
| 2257 | + netif_err(efx, drv, efx->net_dev, |
|---|
| 2258 | + "Unknown response to NVRAM_UPDATE_FINISH\n"); |
|---|
| 2259 | + rc = -EIO; |
|---|
| 2260 | + } |
|---|
| 2261 | + } |
|---|
| 2262 | + |
|---|
| 2159 | 2263 | return rc; |
|---|
| 2160 | 2264 | } |
|---|
| 2161 | 2265 | |
|---|