18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * HMC Drive FTP Services 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2013 68c2ecf20Sopenharmony_ci * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "hmcdrv" 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 158c2ecf20Sopenharmony_ci#include <linux/export.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/ctype.h> 188c2ecf20Sopenharmony_ci#include <linux/crc16.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "hmcdrv_ftp.h" 218c2ecf20Sopenharmony_ci#include "hmcdrv_cache.h" 228c2ecf20Sopenharmony_ci#include "sclp_ftp.h" 238c2ecf20Sopenharmony_ci#include "diag_ftp.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/** 268c2ecf20Sopenharmony_ci * struct hmcdrv_ftp_ops - HMC drive FTP operations 278c2ecf20Sopenharmony_ci * @startup: startup function 288c2ecf20Sopenharmony_ci * @shutdown: shutdown function 298c2ecf20Sopenharmony_ci * @cmd: FTP transfer function 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_cistruct hmcdrv_ftp_ops { 328c2ecf20Sopenharmony_ci int (*startup)(void); 338c2ecf20Sopenharmony_ci void (*shutdown)(void); 348c2ecf20Sopenharmony_ci ssize_t (*transfer)(const struct hmcdrv_ftp_cmdspec *ftp, 358c2ecf20Sopenharmony_ci size_t *fsize); 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic enum hmcdrv_ftp_cmdid hmcdrv_ftp_cmd_getid(const char *cmd, int len); 398c2ecf20Sopenharmony_cistatic int hmcdrv_ftp_parse(char *cmd, struct hmcdrv_ftp_cmdspec *ftp); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic const struct hmcdrv_ftp_ops *hmcdrv_ftp_funcs; /* current operations */ 428c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(hmcdrv_ftp_mutex); /* mutex for hmcdrv_ftp_funcs */ 438c2ecf20Sopenharmony_cistatic unsigned hmcdrv_ftp_refcnt; /* start/shutdown reference counter */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/** 468c2ecf20Sopenharmony_ci * hmcdrv_ftp_cmd_getid() - determine FTP command ID from a command string 478c2ecf20Sopenharmony_ci * @cmd: FTP command string (NOT zero-terminated) 488c2ecf20Sopenharmony_ci * @len: length of FTP command string in @cmd 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_cistatic enum hmcdrv_ftp_cmdid hmcdrv_ftp_cmd_getid(const char *cmd, int len) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci /* HMC FTP command descriptor */ 538c2ecf20Sopenharmony_ci struct hmcdrv_ftp_cmd_desc { 548c2ecf20Sopenharmony_ci const char *str; /* command string */ 558c2ecf20Sopenharmony_ci enum hmcdrv_ftp_cmdid cmd; /* associated command as enum */ 568c2ecf20Sopenharmony_ci }; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci /* Description of all HMC drive FTP commands 598c2ecf20Sopenharmony_ci * 608c2ecf20Sopenharmony_ci * Notes: 618c2ecf20Sopenharmony_ci * 1. Array size should be a prime number. 628c2ecf20Sopenharmony_ci * 2. Do not change the order of commands in table (because the 638c2ecf20Sopenharmony_ci * index is determined by CRC % ARRAY_SIZE). 648c2ecf20Sopenharmony_ci * 3. Original command 'nlist' was renamed, else the CRC would 658c2ecf20Sopenharmony_ci * collide with 'append' (see point 2). 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_ci static const struct hmcdrv_ftp_cmd_desc ftpcmds[7] = { 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci {.str = "get", /* [0] get (CRC = 0x68eb) */ 708c2ecf20Sopenharmony_ci .cmd = HMCDRV_FTP_GET}, 718c2ecf20Sopenharmony_ci {.str = "dir", /* [1] dir (CRC = 0x6a9e) */ 728c2ecf20Sopenharmony_ci .cmd = HMCDRV_FTP_DIR}, 738c2ecf20Sopenharmony_ci {.str = "delete", /* [2] delete (CRC = 0x53ae) */ 748c2ecf20Sopenharmony_ci .cmd = HMCDRV_FTP_DELETE}, 758c2ecf20Sopenharmony_ci {.str = "nls", /* [3] nls (CRC = 0xf87c) */ 768c2ecf20Sopenharmony_ci .cmd = HMCDRV_FTP_NLIST}, 778c2ecf20Sopenharmony_ci {.str = "put", /* [4] put (CRC = 0xac56) */ 788c2ecf20Sopenharmony_ci .cmd = HMCDRV_FTP_PUT}, 798c2ecf20Sopenharmony_ci {.str = "append", /* [5] append (CRC = 0xf56e) */ 808c2ecf20Sopenharmony_ci .cmd = HMCDRV_FTP_APPEND}, 818c2ecf20Sopenharmony_ci {.str = NULL} /* [6] unused */ 828c2ecf20Sopenharmony_ci }; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci const struct hmcdrv_ftp_cmd_desc *pdesc; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci u16 crc = 0xffffU; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (len == 0) 898c2ecf20Sopenharmony_ci return HMCDRV_FTP_NOOP; /* error indiactor */ 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci crc = crc16(crc, cmd, len); 928c2ecf20Sopenharmony_ci pdesc = ftpcmds + (crc % ARRAY_SIZE(ftpcmds)); 938c2ecf20Sopenharmony_ci pr_debug("FTP command '%s' has CRC 0x%04x, at table pos. %lu\n", 948c2ecf20Sopenharmony_ci cmd, crc, (crc % ARRAY_SIZE(ftpcmds))); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (!pdesc->str || strncmp(pdesc->str, cmd, len)) 978c2ecf20Sopenharmony_ci return HMCDRV_FTP_NOOP; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci pr_debug("FTP command '%s' found, with ID %d\n", 1008c2ecf20Sopenharmony_ci pdesc->str, pdesc->cmd); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci return pdesc->cmd; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/** 1068c2ecf20Sopenharmony_ci * hmcdrv_ftp_parse() - HMC drive FTP command parser 1078c2ecf20Sopenharmony_ci * @cmd: FTP command string "<cmd> <filename>" 1088c2ecf20Sopenharmony_ci * @ftp: Pointer to FTP command specification buffer (output) 1098c2ecf20Sopenharmony_ci * 1108c2ecf20Sopenharmony_ci * Return: 0 on success, else a (negative) error code 1118c2ecf20Sopenharmony_ci */ 1128c2ecf20Sopenharmony_cistatic int hmcdrv_ftp_parse(char *cmd, struct hmcdrv_ftp_cmdspec *ftp) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci char *start; 1158c2ecf20Sopenharmony_ci int argc = 0; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci ftp->id = HMCDRV_FTP_NOOP; 1188c2ecf20Sopenharmony_ci ftp->fname = NULL; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci while (*cmd != '\0') { 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci while (isspace(*cmd)) 1238c2ecf20Sopenharmony_ci ++cmd; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (*cmd == '\0') 1268c2ecf20Sopenharmony_ci break; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci start = cmd; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci switch (argc) { 1318c2ecf20Sopenharmony_ci case 0: /* 1st argument (FTP command) */ 1328c2ecf20Sopenharmony_ci while ((*cmd != '\0') && !isspace(*cmd)) 1338c2ecf20Sopenharmony_ci ++cmd; 1348c2ecf20Sopenharmony_ci ftp->id = hmcdrv_ftp_cmd_getid(start, cmd - start); 1358c2ecf20Sopenharmony_ci break; 1368c2ecf20Sopenharmony_ci case 1: /* 2nd / last argument (rest of line) */ 1378c2ecf20Sopenharmony_ci while ((*cmd != '\0') && !iscntrl(*cmd)) 1388c2ecf20Sopenharmony_ci ++cmd; 1398c2ecf20Sopenharmony_ci ftp->fname = start; 1408c2ecf20Sopenharmony_ci fallthrough; 1418c2ecf20Sopenharmony_ci default: 1428c2ecf20Sopenharmony_ci *cmd = '\0'; 1438c2ecf20Sopenharmony_ci break; 1448c2ecf20Sopenharmony_ci } /* switch */ 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci ++argc; 1478c2ecf20Sopenharmony_ci } /* while */ 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (!ftp->fname || (ftp->id == HMCDRV_FTP_NOOP)) 1508c2ecf20Sopenharmony_ci return -EINVAL; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/** 1568c2ecf20Sopenharmony_ci * hmcdrv_ftp_do() - perform a HMC drive FTP, with data from kernel-space 1578c2ecf20Sopenharmony_ci * @ftp: pointer to FTP command specification 1588c2ecf20Sopenharmony_ci * 1598c2ecf20Sopenharmony_ci * Return: number of bytes read/written or a negative error code 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_cissize_t hmcdrv_ftp_do(const struct hmcdrv_ftp_cmdspec *ftp) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci ssize_t len; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci mutex_lock(&hmcdrv_ftp_mutex); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (hmcdrv_ftp_funcs && hmcdrv_ftp_refcnt) { 1688c2ecf20Sopenharmony_ci pr_debug("starting transfer, cmd %d for '%s' at %lld with %zd bytes\n", 1698c2ecf20Sopenharmony_ci ftp->id, ftp->fname, (long long) ftp->ofs, ftp->len); 1708c2ecf20Sopenharmony_ci len = hmcdrv_cache_cmd(ftp, hmcdrv_ftp_funcs->transfer); 1718c2ecf20Sopenharmony_ci } else { 1728c2ecf20Sopenharmony_ci len = -ENXIO; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci mutex_unlock(&hmcdrv_ftp_mutex); 1768c2ecf20Sopenharmony_ci return len; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hmcdrv_ftp_do); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/** 1818c2ecf20Sopenharmony_ci * hmcdrv_ftp_probe() - probe for the HMC drive FTP service 1828c2ecf20Sopenharmony_ci * 1838c2ecf20Sopenharmony_ci * Return: 0 if service is available, else an (negative) error code 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_ciint hmcdrv_ftp_probe(void) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci int rc; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci struct hmcdrv_ftp_cmdspec ftp = { 1908c2ecf20Sopenharmony_ci .id = HMCDRV_FTP_NOOP, 1918c2ecf20Sopenharmony_ci .ofs = 0, 1928c2ecf20Sopenharmony_ci .fname = "", 1938c2ecf20Sopenharmony_ci .len = PAGE_SIZE 1948c2ecf20Sopenharmony_ci }; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci ftp.buf = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (!ftp.buf) 1998c2ecf20Sopenharmony_ci return -ENOMEM; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci rc = hmcdrv_ftp_startup(); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (rc) 2048c2ecf20Sopenharmony_ci goto out; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci rc = hmcdrv_ftp_do(&ftp); 2078c2ecf20Sopenharmony_ci hmcdrv_ftp_shutdown(); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci switch (rc) { 2108c2ecf20Sopenharmony_ci case -ENOENT: /* no such file/media or currently busy, */ 2118c2ecf20Sopenharmony_ci case -EBUSY: /* but service seems to be available */ 2128c2ecf20Sopenharmony_ci rc = 0; 2138c2ecf20Sopenharmony_ci break; 2148c2ecf20Sopenharmony_ci default: /* leave 'rc' as it is for [0, -EPERM, -E...] */ 2158c2ecf20Sopenharmony_ci if (rc > 0) 2168c2ecf20Sopenharmony_ci rc = 0; /* clear length (success) */ 2178c2ecf20Sopenharmony_ci break; 2188c2ecf20Sopenharmony_ci } /* switch */ 2198c2ecf20Sopenharmony_ciout: 2208c2ecf20Sopenharmony_ci free_page((unsigned long) ftp.buf); 2218c2ecf20Sopenharmony_ci return rc; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hmcdrv_ftp_probe); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci/** 2268c2ecf20Sopenharmony_ci * hmcdrv_ftp_cmd() - Perform a HMC drive FTP, with data from user-space 2278c2ecf20Sopenharmony_ci * 2288c2ecf20Sopenharmony_ci * @cmd: FTP command string "<cmd> <filename>" 2298c2ecf20Sopenharmony_ci * @offset: file position to read/write 2308c2ecf20Sopenharmony_ci * @buf: user-space buffer for read/written directory/file 2318c2ecf20Sopenharmony_ci * @len: size of @buf (read/dir) or number of bytes to write 2328c2ecf20Sopenharmony_ci * 2338c2ecf20Sopenharmony_ci * This function must not be called before hmcdrv_ftp_startup() was called. 2348c2ecf20Sopenharmony_ci * 2358c2ecf20Sopenharmony_ci * Return: number of bytes read/written or a negative error code 2368c2ecf20Sopenharmony_ci */ 2378c2ecf20Sopenharmony_cissize_t hmcdrv_ftp_cmd(char __kernel *cmd, loff_t offset, 2388c2ecf20Sopenharmony_ci char __user *buf, size_t len) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci int order; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci struct hmcdrv_ftp_cmdspec ftp = {.len = len, .ofs = offset}; 2438c2ecf20Sopenharmony_ci ssize_t retlen = hmcdrv_ftp_parse(cmd, &ftp); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (retlen) 2468c2ecf20Sopenharmony_ci return retlen; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci order = get_order(ftp.len); 2498c2ecf20Sopenharmony_ci ftp.buf = (void *) __get_free_pages(GFP_KERNEL | GFP_DMA, order); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (!ftp.buf) 2528c2ecf20Sopenharmony_ci return -ENOMEM; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci switch (ftp.id) { 2558c2ecf20Sopenharmony_ci case HMCDRV_FTP_DIR: 2568c2ecf20Sopenharmony_ci case HMCDRV_FTP_NLIST: 2578c2ecf20Sopenharmony_ci case HMCDRV_FTP_GET: 2588c2ecf20Sopenharmony_ci retlen = hmcdrv_ftp_do(&ftp); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if ((retlen >= 0) && 2618c2ecf20Sopenharmony_ci copy_to_user(buf, ftp.buf, retlen)) 2628c2ecf20Sopenharmony_ci retlen = -EFAULT; 2638c2ecf20Sopenharmony_ci break; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci case HMCDRV_FTP_PUT: 2668c2ecf20Sopenharmony_ci case HMCDRV_FTP_APPEND: 2678c2ecf20Sopenharmony_ci if (!copy_from_user(ftp.buf, buf, ftp.len)) 2688c2ecf20Sopenharmony_ci retlen = hmcdrv_ftp_do(&ftp); 2698c2ecf20Sopenharmony_ci else 2708c2ecf20Sopenharmony_ci retlen = -EFAULT; 2718c2ecf20Sopenharmony_ci break; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci case HMCDRV_FTP_DELETE: 2748c2ecf20Sopenharmony_ci retlen = hmcdrv_ftp_do(&ftp); 2758c2ecf20Sopenharmony_ci break; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci default: 2788c2ecf20Sopenharmony_ci retlen = -EOPNOTSUPP; 2798c2ecf20Sopenharmony_ci break; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci free_pages((unsigned long) ftp.buf, order); 2838c2ecf20Sopenharmony_ci return retlen; 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci/** 2878c2ecf20Sopenharmony_ci * hmcdrv_ftp_startup() - startup of HMC drive FTP functionality for a 2888c2ecf20Sopenharmony_ci * dedicated (owner) instance 2898c2ecf20Sopenharmony_ci * 2908c2ecf20Sopenharmony_ci * Return: 0 on success, else an (negative) error code 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_ciint hmcdrv_ftp_startup(void) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci static const struct hmcdrv_ftp_ops hmcdrv_ftp_zvm = { 2958c2ecf20Sopenharmony_ci .startup = diag_ftp_startup, 2968c2ecf20Sopenharmony_ci .shutdown = diag_ftp_shutdown, 2978c2ecf20Sopenharmony_ci .transfer = diag_ftp_cmd 2988c2ecf20Sopenharmony_ci }; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci static const struct hmcdrv_ftp_ops hmcdrv_ftp_lpar = { 3018c2ecf20Sopenharmony_ci .startup = sclp_ftp_startup, 3028c2ecf20Sopenharmony_ci .shutdown = sclp_ftp_shutdown, 3038c2ecf20Sopenharmony_ci .transfer = sclp_ftp_cmd 3048c2ecf20Sopenharmony_ci }; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci int rc = 0; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci mutex_lock(&hmcdrv_ftp_mutex); /* block transfers while start-up */ 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (hmcdrv_ftp_refcnt == 0) { 3118c2ecf20Sopenharmony_ci if (MACHINE_IS_VM) 3128c2ecf20Sopenharmony_ci hmcdrv_ftp_funcs = &hmcdrv_ftp_zvm; 3138c2ecf20Sopenharmony_ci else if (MACHINE_IS_LPAR || MACHINE_IS_KVM) 3148c2ecf20Sopenharmony_ci hmcdrv_ftp_funcs = &hmcdrv_ftp_lpar; 3158c2ecf20Sopenharmony_ci else 3168c2ecf20Sopenharmony_ci rc = -EOPNOTSUPP; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (hmcdrv_ftp_funcs) 3198c2ecf20Sopenharmony_ci rc = hmcdrv_ftp_funcs->startup(); 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (!rc) 3238c2ecf20Sopenharmony_ci ++hmcdrv_ftp_refcnt; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci mutex_unlock(&hmcdrv_ftp_mutex); 3268c2ecf20Sopenharmony_ci return rc; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hmcdrv_ftp_startup); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci/** 3318c2ecf20Sopenharmony_ci * hmcdrv_ftp_shutdown() - shutdown of HMC drive FTP functionality for a 3328c2ecf20Sopenharmony_ci * dedicated (owner) instance 3338c2ecf20Sopenharmony_ci */ 3348c2ecf20Sopenharmony_civoid hmcdrv_ftp_shutdown(void) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci mutex_lock(&hmcdrv_ftp_mutex); 3378c2ecf20Sopenharmony_ci --hmcdrv_ftp_refcnt; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if ((hmcdrv_ftp_refcnt == 0) && hmcdrv_ftp_funcs) 3408c2ecf20Sopenharmony_ci hmcdrv_ftp_funcs->shutdown(); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci mutex_unlock(&hmcdrv_ftp_mutex); 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hmcdrv_ftp_shutdown); 345