aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRalf Hoppe <rhoppe@de.ibm.com>2013-04-08 03:52:57 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2014-09-25 04:52:02 -0400
commit8f933b1043e1e51f4776fc1ffe86752c7785fd4e (patch)
tree9549e4659cd027279b213bab4e735e1ccd5b15d7
parentea61a579ab87f1620b14777afc32cf3827f07bc8 (diff)
s390/hmcdrv: HMC drive CD/DVD access
This device driver allows accessing a HMC drive CD/DVD-ROM. It can be used in a LPAR and z/VM environment. Reviewed-by: Martin Schwidefsky <schwidefsky@de.ibm.com> Reviewed-by: Heiko Carstens <heiko.carstens@de.ibm.com> Signed-off-by: Ralf Hoppe <rhoppe@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r--arch/s390/include/asm/irq.h1
-rw-r--r--arch/s390/kernel/irq.c1
-rw-r--r--drivers/s390/char/Kconfig13
-rw-r--r--drivers/s390/char/Makefile3
-rw-r--r--drivers/s390/char/diag_ftp.c237
-rw-r--r--drivers/s390/char/diag_ftp.h21
-rw-r--r--drivers/s390/char/hmcdrv_cache.c252
-rw-r--r--drivers/s390/char/hmcdrv_cache.h24
-rw-r--r--drivers/s390/char/hmcdrv_dev.c370
-rw-r--r--drivers/s390/char/hmcdrv_dev.h14
-rw-r--r--drivers/s390/char/hmcdrv_ftp.c343
-rw-r--r--drivers/s390/char/hmcdrv_ftp.h63
-rw-r--r--drivers/s390/char/hmcdrv_mod.c64
-rw-r--r--drivers/s390/char/sclp.h2
-rw-r--r--drivers/s390/char/sclp_diag.h89
-rw-r--r--drivers/s390/char/sclp_ftp.c275
-rw-r--r--drivers/s390/char/sclp_ftp.h21
17 files changed, 1793 insertions, 0 deletions
diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h
index c4dd400a2791..e787cc1bff8f 100644
--- a/arch/s390/include/asm/irq.h
+++ b/arch/s390/include/asm/irq.h
@@ -51,6 +51,7 @@ enum interruption_class {
51 IRQEXT_CMS, 51 IRQEXT_CMS,
52 IRQEXT_CMC, 52 IRQEXT_CMC,
53 IRQEXT_CMR, 53 IRQEXT_CMR,
54 IRQEXT_FTP,
54 IRQIO_CIO, 55 IRQIO_CIO,
55 IRQIO_QAI, 56 IRQIO_QAI,
56 IRQIO_DAS, 57 IRQIO_DAS,
diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c
index 8eb82443cfbd..051574ee5366 100644
--- a/arch/s390/kernel/irq.c
+++ b/arch/s390/kernel/irq.c
@@ -70,6 +70,7 @@ static const struct irq_class irqclass_sub_desc[NR_ARCH_IRQS] = {
70 {.irq = IRQEXT_CMS, .name = "CMS", .desc = "[EXT] CPU-Measurement: Sampling"}, 70 {.irq = IRQEXT_CMS, .name = "CMS", .desc = "[EXT] CPU-Measurement: Sampling"},
71 {.irq = IRQEXT_CMC, .name = "CMC", .desc = "[EXT] CPU-Measurement: Counter"}, 71 {.irq = IRQEXT_CMC, .name = "CMC", .desc = "[EXT] CPU-Measurement: Counter"},
72 {.irq = IRQEXT_CMR, .name = "CMR", .desc = "[EXT] CPU-Measurement: RI"}, 72 {.irq = IRQEXT_CMR, .name = "CMR", .desc = "[EXT] CPU-Measurement: RI"},
73 {.irq = IRQEXT_FTP, .name = "FTP", .desc = "[EXT] HMC FTP Service"},
73 {.irq = IRQIO_CIO, .name = "CIO", .desc = "[I/O] Common I/O Layer Interrupt"}, 74 {.irq = IRQIO_CIO, .name = "CIO", .desc = "[I/O] Common I/O Layer Interrupt"},
74 {.irq = IRQIO_QAI, .name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt"}, 75 {.irq = IRQIO_QAI, .name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt"},
75 {.irq = IRQIO_DAS, .name = "DAS", .desc = "[I/O] DASD"}, 76 {.irq = IRQIO_DAS, .name = "DAS", .desc = "[I/O] DASD"},
diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig
index 71bf959732fe..dc24ecfac2d1 100644
--- a/drivers/s390/char/Kconfig
+++ b/drivers/s390/char/Kconfig
@@ -102,6 +102,19 @@ config SCLP_ASYNC
102 want for inform other people about your kernel panics, 102 want for inform other people about your kernel panics,
103 need this feature and intend to run your kernel in LPAR. 103 need this feature and intend to run your kernel in LPAR.
104 104
105config HMC_DRV
106 def_tristate m
107 prompt "Support for file transfers from HMC drive CD/DVD-ROM"
108 depends on 64BIT
109 select CRC16
110 help
111 This option enables support for file transfers from a Hardware
112 Management Console (HMC) drive CD/DVD-ROM. It is available as a
113 module, called 'hmcdrv', and also as kernel built-in. There is one
114 optional parameter for this module: cachesize=N, which modifies the
115 transfer cache size from it's default value 0.5MB to N bytes. If N
116 is zero, then no caching is performed.
117
105config S390_TAPE 118config S390_TAPE
106 def_tristate m 119 def_tristate m
107 prompt "S/390 tape device support" 120 prompt "S/390 tape device support"
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
index 78b6ace7edcb..6fa9364d1c07 100644
--- a/drivers/s390/char/Makefile
+++ b/drivers/s390/char/Makefile
@@ -33,3 +33,6 @@ obj-$(CONFIG_S390_VMUR) += vmur.o
33 33
34zcore_mod-objs := sclp_sdias.o zcore.o 34zcore_mod-objs := sclp_sdias.o zcore.o
35obj-$(CONFIG_CRASH_DUMP) += zcore_mod.o 35obj-$(CONFIG_CRASH_DUMP) += zcore_mod.o
36
37hmcdrv-objs := hmcdrv_mod.o hmcdrv_dev.o hmcdrv_ftp.o hmcdrv_cache.o diag_ftp.o sclp_ftp.o
38obj-$(CONFIG_HMC_DRV) += hmcdrv.o
diff --git a/drivers/s390/char/diag_ftp.c b/drivers/s390/char/diag_ftp.c
new file mode 100644
index 000000000000..93889632fdf9
--- /dev/null
+++ b/drivers/s390/char/diag_ftp.c
@@ -0,0 +1,237 @@
1/*
2 * DIAGNOSE X'2C4' instruction based HMC FTP services, useable on z/VM
3 *
4 * Copyright IBM Corp. 2013
5 * Author(s): Ralf Hoppe (rhoppe@de.ibm.com)
6 *
7 */
8
9#define KMSG_COMPONENT "hmcdrv"
10#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
11
12#include <linux/kernel.h>
13#include <linux/mm.h>
14#include <linux/irq.h>
15#include <linux/wait.h>
16#include <linux/string.h>
17#include <asm/ctl_reg.h>
18
19#include "hmcdrv_ftp.h"
20#include "diag_ftp.h"
21
22/* DIAGNOSE X'2C4' return codes in Ry */
23#define DIAG_FTP_RET_OK 0 /* HMC FTP started successfully */
24#define DIAG_FTP_RET_EBUSY 4 /* HMC FTP service currently busy */
25#define DIAG_FTP_RET_EIO 8 /* HMC FTP service I/O error */
26/* and an artificial extension */
27#define DIAG_FTP_RET_EPERM 2 /* HMC FTP service privilege error */
28
29/* FTP service status codes (after INTR at guest real location 133) */
30#define DIAG_FTP_STAT_OK 0U /* request completed successfully */
31#define DIAG_FTP_STAT_PGCC 4U /* program check condition */
32#define DIAG_FTP_STAT_PGIOE 8U /* paging I/O error */
33#define DIAG_FTP_STAT_TIMEOUT 12U /* timeout */
34#define DIAG_FTP_STAT_EBASE 16U /* base of error codes from SCLP */
35#define DIAG_FTP_STAT_LDFAIL (DIAG_FTP_STAT_EBASE + 1U) /* failed */
36#define DIAG_FTP_STAT_LDNPERM (DIAG_FTP_STAT_EBASE + 2U) /* not allowed */
37#define DIAG_FTP_STAT_LDRUNS (DIAG_FTP_STAT_EBASE + 3U) /* runs */
38#define DIAG_FTP_STAT_LDNRUNS (DIAG_FTP_STAT_EBASE + 4U) /* not runs */
39
40/**
41 * struct diag_ftp_ldfpl - load file FTP parameter list (LDFPL)
42 * @bufaddr: real buffer address (at 4k boundary)
43 * @buflen: length of buffer
44 * @offset: dir/file offset
45 * @intparm: interruption parameter (unused)
46 * @transferred: bytes transferred
47 * @fsize: file size, filled on GET
48 * @failaddr: failing address
49 * @spare: padding
50 * @fident: file name - ASCII
51 */
52struct diag_ftp_ldfpl {
53 u64 bufaddr;
54 u64 buflen;
55 u64 offset;
56 u64 intparm;
57 u64 transferred;
58 u64 fsize;
59 u64 failaddr;
60 u64 spare;
61 u8 fident[HMCDRV_FTP_FIDENT_MAX];
62} __packed;
63
64static DECLARE_COMPLETION(diag_ftp_rx_complete);
65static int diag_ftp_subcode;
66
67/**
68 * diag_ftp_handler() - FTP services IRQ handler
69 * @extirq: external interrupt (sub-) code
70 * @param32: 32-bit interruption parameter from &struct diag_ftp_ldfpl
71 * @param64: unused (for 64-bit interrupt parameters)
72 */
73static void diag_ftp_handler(struct ext_code extirq,
74 unsigned int param32,
75 unsigned long param64)
76{
77 if ((extirq.subcode >> 8) != 8)
78 return; /* not a FTP services sub-code */
79
80 inc_irq_stat(IRQEXT_FTP);
81 diag_ftp_subcode = extirq.subcode & 0xffU;
82 complete(&diag_ftp_rx_complete);
83}
84
85/**
86 * diag_ftp_2c4() - DIAGNOSE X'2C4' service call
87 * @fpl: pointer to prepared LDFPL
88 * @cmd: FTP command to be executed
89 *
90 * Performs a DIAGNOSE X'2C4' call with (input/output) FTP parameter list
91 * @fpl and FTP function code @cmd. In case of an error the function does
92 * nothing and returns an (negative) error code.
93 *
94 * Notes:
95 * 1. This function only initiates a transfer, so the caller must wait
96 * for completion (asynchronous execution).
97 * 2. The FTP parameter list @fpl must be aligned to a double-word boundary.
98 * 3. fpl->bufaddr must be a real address, 4k aligned
99 */
100static int diag_ftp_2c4(struct diag_ftp_ldfpl *fpl,
101 enum hmcdrv_ftp_cmdid cmd)
102{
103 int rc;
104
105 asm volatile(
106 " diag %[addr],%[cmd],0x2c4\n"
107 "0: j 2f\n"
108 "1: la %[rc],%[err]\n"
109 "2:\n"
110 EX_TABLE(0b, 1b)
111 : [rc] "=d" (rc), "+m" (*fpl)
112 : [cmd] "0" (cmd), [addr] "d" (virt_to_phys(fpl)),
113 [err] "i" (DIAG_FTP_RET_EPERM)
114 : "cc");
115
116 switch (rc) {
117 case DIAG_FTP_RET_OK:
118 return 0;
119 case DIAG_FTP_RET_EBUSY:
120 return -EBUSY;
121 case DIAG_FTP_RET_EPERM:
122 return -EPERM;
123 case DIAG_FTP_RET_EIO:
124 default:
125 return -EIO;
126 }
127}
128
129/**
130 * diag_ftp_cmd() - executes a DIAG X'2C4' FTP command, targeting a HMC
131 * @ftp: pointer to FTP command specification
132 * @fsize: return of file size (or NULL if undesirable)
133 *
134 * Attention: Notice that this function is not reentrant - so the caller
135 * must ensure locking.
136 *
137 * Return: number of bytes read/written or a (negative) error code
138 */
139ssize_t diag_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize)
140{
141 struct diag_ftp_ldfpl *ldfpl;
142 ssize_t len;
143#ifdef DEBUG
144 unsigned long start_jiffies;
145
146 pr_debug("starting DIAG X'2C4' on '%s', requesting %zd bytes\n",
147 ftp->fname, ftp->len);
148 start_jiffies = jiffies;
149#endif
150 init_completion(&diag_ftp_rx_complete);
151
152 ldfpl = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
153 if (!ldfpl) {
154 len = -ENOMEM;
155 goto out;
156 }
157
158 len = strlcpy(ldfpl->fident, ftp->fname, sizeof(ldfpl->fident));
159 if (len >= HMCDRV_FTP_FIDENT_MAX) {
160 len = -EINVAL;
161 goto out_free;
162 }
163
164 ldfpl->transferred = 0;
165 ldfpl->fsize = 0;
166 ldfpl->offset = ftp->ofs;
167 ldfpl->buflen = ftp->len;
168 ldfpl->bufaddr = virt_to_phys(ftp->buf);
169
170 len = diag_ftp_2c4(ldfpl, ftp->id);
171 if (len)
172 goto out_free;
173
174 /*
175 * There is no way to cancel the running diag X'2C4', the code
176 * needs to wait unconditionally until the transfer is complete.
177 */
178 wait_for_completion(&diag_ftp_rx_complete);
179
180#ifdef DEBUG
181 pr_debug("completed DIAG X'2C4' after %lu ms\n",
182 (jiffies - start_jiffies) * 1000 / HZ);
183 pr_debug("status of DIAG X'2C4' is %u, with %lld/%lld bytes\n",
184 diag_ftp_subcode, ldfpl->transferred, ldfpl->fsize);
185#endif
186
187 switch (diag_ftp_subcode) {
188 case DIAG_FTP_STAT_OK: /* success */
189 len = ldfpl->transferred;
190 if (fsize)
191 *fsize = ldfpl->fsize;
192 break;
193 case DIAG_FTP_STAT_LDNPERM:
194 len = -EPERM;
195 break;
196 case DIAG_FTP_STAT_LDRUNS:
197 len = -EBUSY;
198 break;
199 case DIAG_FTP_STAT_LDFAIL:
200 len = -ENOENT; /* no such file or media */
201 break;
202 default:
203 len = -EIO;
204 break;
205 }
206
207out_free:
208 free_page((unsigned long) ldfpl);
209out:
210 return len;
211}
212
213/**
214 * diag_ftp_startup() - startup of FTP services, when running on z/VM
215 *
216 * Return: 0 on success, else an (negative) error code
217 */
218int diag_ftp_startup(void)
219{
220 int rc;
221
222 rc = register_external_irq(EXT_IRQ_CP_SERVICE, diag_ftp_handler);
223 if (rc)
224 return rc;
225
226 ctl_set_bit(0, 63 - 22);
227 return 0;
228}
229
230/**
231 * diag_ftp_shutdown() - shutdown of FTP services, when running on z/VM
232 */
233void diag_ftp_shutdown(void)
234{
235 ctl_clear_bit(0, 63 - 22);
236 unregister_external_irq(EXT_IRQ_CP_SERVICE, diag_ftp_handler);
237}
diff --git a/drivers/s390/char/diag_ftp.h b/drivers/s390/char/diag_ftp.h
new file mode 100644
index 000000000000..3abd2614053a
--- /dev/null
+++ b/drivers/s390/char/diag_ftp.h
@@ -0,0 +1,21 @@
1/*
2 * DIAGNOSE X'2C4' instruction based SE/HMC FTP Services, useable on z/VM
3 *
4 * Notice that all functions exported here are not reentrant.
5 * So usage should be exclusive, ensured by the caller (e.g. using a
6 * mutex).
7 *
8 * Copyright IBM Corp. 2013
9 * Author(s): Ralf Hoppe (rhoppe@de.ibm.com)
10 */
11
12#ifndef __DIAG_FTP_H__
13#define __DIAG_FTP_H__
14
15#include "hmcdrv_ftp.h"
16
17int diag_ftp_startup(void);
18void diag_ftp_shutdown(void);
19ssize_t diag_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize);
20
21#endif /* __DIAG_FTP_H__ */
diff --git a/drivers/s390/char/hmcdrv_cache.c b/drivers/s390/char/hmcdrv_cache.c
new file mode 100644
index 000000000000..4cda5ada143a
--- /dev/null
+++ b/drivers/s390/char/hmcdrv_cache.c
@@ -0,0 +1,252 @@
1/*
2 * SE/HMC Drive (Read) Cache Functions
3 *
4 * Copyright IBM Corp. 2013
5 * Author(s): Ralf Hoppe (rhoppe@de.ibm.com)
6 *
7 */
8
9#define KMSG_COMPONENT "hmcdrv"
10#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
11
12#include <linux/kernel.h>
13#include <linux/mm.h>
14#include <linux/jiffies.h>
15
16#include "hmcdrv_ftp.h"
17#include "hmcdrv_cache.h"
18
19#define HMCDRV_CACHE_TIMEOUT 30 /* aging timeout in seconds */
20
21/**
22 * struct hmcdrv_cache_entry - file cache (only used on read/dir)
23 * @id: FTP command ID
24 * @content: kernel-space buffer, 4k aligned
25 * @len: size of @content cache (0 if caching disabled)
26 * @ofs: start of content within file (-1 if no cached content)
27 * @fname: file name
28 * @fsize: file size
29 * @timeout: cache timeout in jiffies
30 *
31 * Notice that the first three members (id, fname, fsize) are cached on all
32 * read/dir requests. But content is cached only under some preconditions.
33 * Uncached content is signalled by a negative value of @ofs.
34 */
35struct hmcdrv_cache_entry {
36 enum hmcdrv_ftp_cmdid id;
37 char fname[HMCDRV_FTP_FIDENT_MAX];
38 size_t fsize;
39 loff_t ofs;
40 unsigned long timeout;
41 void *content;
42 size_t len;
43};
44
45static int hmcdrv_cache_order; /* cache allocated page order */
46
47static struct hmcdrv_cache_entry hmcdrv_cache_file = {
48 .fsize = SIZE_MAX,
49 .ofs = -1,
50 .len = 0,
51 .fname = {'\0'}
52};
53
54/**
55 * hmcdrv_cache_get() - looks for file data/content in read cache
56 * @ftp: pointer to FTP command specification
57 *
58 * Return: number of bytes read from cache or a negative number if nothing
59 * in content cache (for the file/cmd specified in @ftp)
60 */
61static ssize_t hmcdrv_cache_get(const struct hmcdrv_ftp_cmdspec *ftp)
62{
63 loff_t pos; /* position in cache (signed) */
64 ssize_t len;
65
66 if ((ftp->id != hmcdrv_cache_file.id) ||
67 strcmp(hmcdrv_cache_file.fname, ftp->fname))
68 return -1;
69
70 if (ftp->ofs >= hmcdrv_cache_file.fsize) /* EOF ? */
71 return 0;
72
73 if ((hmcdrv_cache_file.ofs < 0) || /* has content? */
74 time_after(jiffies, hmcdrv_cache_file.timeout))
75 return -1;
76
77 /* there seems to be cached content - calculate the maximum number
78 * of bytes that can be returned (regarding file size and offset)
79 */
80 len = hmcdrv_cache_file.fsize - ftp->ofs;
81
82 if (len > ftp->len)
83 len = ftp->len;
84
85 /* check if the requested chunk falls into our cache (which starts
86 * at offset 'hmcdrv_cache_file.ofs' in the file of interest)
87 */
88 pos = ftp->ofs - hmcdrv_cache_file.ofs;
89
90 if ((pos >= 0) &&
91 ((pos + len) <= hmcdrv_cache_file.len)) {
92
93 memcpy(ftp->buf,
94 hmcdrv_cache_file.content + pos,
95 len);
96 pr_debug("using cached content of '%s', returning %zd/%zd bytes\n",
97 hmcdrv_cache_file.fname, len,
98 hmcdrv_cache_file.fsize);
99
100 return len;
101 }
102
103 return -1;
104}
105
106/**
107 * hmcdrv_cache_do() - do a HMC drive CD/DVD transfer with cache update
108 * @ftp: pointer to FTP command specification
109 * @func: FTP transfer function to be used
110 *
111 * Return: number of bytes read/written or a (negative) error code
112 */
113static ssize_t hmcdrv_cache_do(const struct hmcdrv_ftp_cmdspec *ftp,
114 hmcdrv_cache_ftpfunc func)
115{
116 ssize_t len;
117
118 /* only cache content if the read/dir cache really exists
119 * (hmcdrv_cache_file.len > 0), is large enough to handle the
120 * request (hmcdrv_cache_file.len >= ftp->len) and there is a need
121 * to do so (ftp->len > 0)
122 */
123 if ((ftp->len > 0) && (hmcdrv_cache_file.len >= ftp->len)) {
124
125 /* because the cache is not located at ftp->buf, we have to
126 * assemble a new HMC drive FTP cmd specification (pointing
127 * to our cache, and using the increased size)
128 */
129 struct hmcdrv_ftp_cmdspec cftp = *ftp; /* make a copy */
130 cftp.buf = hmcdrv_cache_file.content; /* and update */
131 cftp.len = hmcdrv_cache_file.len; /* buffer data */
132
133 len = func(&cftp, &hmcdrv_cache_file.fsize); /* now do */
134
135 if (len > 0) {
136 pr_debug("caching %zd bytes content for '%s'\n",
137 len, ftp->fname);
138
139 if (len > ftp->len)
140 len = ftp->len;
141
142 hmcdrv_cache_file.ofs = ftp->ofs;
143 hmcdrv_cache_file.timeout = jiffies +
144 HMCDRV_CACHE_TIMEOUT * HZ;
145 memcpy(ftp->buf, hmcdrv_cache_file.content, len);
146 }
147 } else {
148 len = func(ftp, &hmcdrv_cache_file.fsize);
149 hmcdrv_cache_file.ofs = -1; /* invalidate content */
150 }
151
152 if (len > 0) {
153 /* cache some file info (FTP command, file name and file
154 * size) unconditionally
155 */
156 strlcpy(hmcdrv_cache_file.fname, ftp->fname,
157 HMCDRV_FTP_FIDENT_MAX);
158 hmcdrv_cache_file.id = ftp->id;
159 pr_debug("caching cmd %d, file size %zu for '%s'\n",
160 ftp->id, hmcdrv_cache_file.fsize, ftp->fname);
161 }
162
163 return len;
164}
165
166/**
167 * hmcdrv_cache_cmd() - perform a cached HMC drive CD/DVD transfer
168 * @ftp: pointer to FTP command specification
169 * @func: FTP transfer function to be used
170 *
171 * Attention: Notice that this function is not reentrant - so the caller
172 * must ensure exclusive execution.
173 *
174 * Return: number of bytes read/written or a (negative) error code
175 */
176ssize_t hmcdrv_cache_cmd(const struct hmcdrv_ftp_cmdspec *ftp,
177 hmcdrv_cache_ftpfunc func)
178{
179 ssize_t len;
180
181 if ((ftp->id == HMCDRV_FTP_DIR) || /* read cache */
182 (ftp->id == HMCDRV_FTP_NLIST) ||
183 (ftp->id == HMCDRV_FTP_GET)) {
184
185 len = hmcdrv_cache_get(ftp);
186
187 if (len >= 0) /* got it from cache ? */
188 return len; /* yes */
189
190 len = hmcdrv_cache_do(ftp, func);
191
192 if (len >= 0)
193 return len;
194
195 } else {
196 len = func(ftp, NULL); /* simply do original command */
197 }
198
199 /* invalidate the (read) cache in case there was a write operation
200 * or an error on read/dir
201 */
202 hmcdrv_cache_file.id = HMCDRV_FTP_NOOP;
203 hmcdrv_cache_file.fsize = LLONG_MAX;
204 hmcdrv_cache_file.ofs = -1;
205
206 return len;
207}
208
209/**
210 * hmcdrv_cache_startup() - startup of HMC drive cache
211 * @cachesize: cache size
212 *
213 * Return: 0 on success, else a (negative) error code
214 */
215int hmcdrv_cache_startup(size_t cachesize)
216{
217 if (cachesize > 0) { /* perform caching ? */
218 hmcdrv_cache_order = get_order(cachesize);
219 hmcdrv_cache_file.content =
220 (void *) __get_free_pages(GFP_KERNEL | GFP_DMA,
221 hmcdrv_cache_order);
222
223 if (!hmcdrv_cache_file.content) {
224 pr_err("Allocating the requested cache size of %zu bytes failed\n",
225 cachesize);
226 return -ENOMEM;
227 }
228
229 pr_debug("content cache enabled, size is %zu bytes\n",
230 cachesize);
231 }
232
233 hmcdrv_cache_file.len = cachesize;
234 return 0;
235}
236
237/**
238 * hmcdrv_cache_shutdown() - shutdown of HMC drive cache
239 */
240void hmcdrv_cache_shutdown(void)
241{
242 if (hmcdrv_cache_file.content) {
243 free_pages((unsigned long) hmcdrv_cache_file.content,
244 hmcdrv_cache_order);
245 hmcdrv_cache_file.content = NULL;
246 }
247
248 hmcdrv_cache_file.id = HMCDRV_FTP_NOOP;
249 hmcdrv_cache_file.fsize = LLONG_MAX;
250 hmcdrv_cache_file.ofs = -1;
251 hmcdrv_cache_file.len = 0; /* no cache */
252}
diff --git a/drivers/s390/char/hmcdrv_cache.h b/drivers/s390/char/hmcdrv_cache.h
new file mode 100644
index 000000000000..a14b57526781
--- /dev/null
+++ b/drivers/s390/char/hmcdrv_cache.h
@@ -0,0 +1,24 @@
1/*
2 * SE/HMC Drive (Read) Cache Functions
3 *
4 * Copyright IBM Corp. 2013
5 * Author(s): Ralf Hoppe (rhoppe@de.ibm.com)
6 */
7
8#ifndef __HMCDRV_CACHE_H__
9#define __HMCDRV_CACHE_H__
10
11#include <linux/mmzone.h>
12#include "hmcdrv_ftp.h"
13
14#define HMCDRV_CACHE_SIZE_DFLT (MAX_ORDER_NR_PAGES * PAGE_SIZE / 2UL)
15
16typedef ssize_t (*hmcdrv_cache_ftpfunc)(const struct hmcdrv_ftp_cmdspec *ftp,
17 size_t *fsize);
18
19ssize_t hmcdrv_cache_cmd(const struct hmcdrv_ftp_cmdspec *ftp,
20 hmcdrv_cache_ftpfunc func);
21int hmcdrv_cache_startup(size_t cachesize);
22void hmcdrv_cache_shutdown(void);
23
24#endif /* __HMCDRV_CACHE_H__ */
diff --git a/drivers/s390/char/hmcdrv_dev.c b/drivers/s390/char/hmcdrv_dev.c
new file mode 100644
index 000000000000..0c5176179c17
--- /dev/null
+++ b/drivers/s390/char/hmcdrv_dev.c
@@ -0,0 +1,370 @@
1/*
2 * HMC Drive CD/DVD Device
3 *
4 * Copyright IBM Corp. 2013
5 * Author(s): Ralf Hoppe (rhoppe@de.ibm.com)
6 *
7 * This file provides a Linux "misc" character device for access to an
8 * assigned HMC drive CD/DVD-ROM. It works as follows: First create the
9 * device by calling hmcdrv_dev_init(). After open() a lseek(fd, 0,
10 * SEEK_END) indicates that a new FTP command follows (not needed on the
11 * first command after open). Then write() the FTP command ASCII string
12 * to it, e.g. "dir /" or "nls <directory>" or "get <filename>". At the
13 * end read() the response.
14 */
15
16#define KMSG_COMPONENT "hmcdrv"
17#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
18
19#include <linux/kernel.h>
20#include <linux/module.h>
21#include <linux/slab.h>
22#include <linux/fs.h>
23#include <linux/cdev.h>
24#include <linux/miscdevice.h>
25#include <linux/device.h>
26#include <linux/capability.h>
27#include <linux/delay.h>
28#include <linux/uaccess.h>
29
30#include "hmcdrv_dev.h"
31#include "hmcdrv_ftp.h"
32
33/* If the following macro is defined, then the HMC device creates it's own
34 * separated device class (and dynamically assigns a major number). If not
35 * defined then the HMC device is assigned to the "misc" class devices.
36 *
37#define HMCDRV_DEV_CLASS "hmcftp"
38 */
39
40#define HMCDRV_DEV_NAME "hmcdrv"
41#define HMCDRV_DEV_BUSY_DELAY 500 /* delay between -EBUSY trials in ms */
42#define HMCDRV_DEV_BUSY_RETRIES 3 /* number of retries on -EBUSY */
43
44struct hmcdrv_dev_node {
45
46#ifdef HMCDRV_DEV_CLASS
47 struct cdev dev; /* character device structure */
48 umode_t mode; /* mode of device node (unused, zero) */
49#else
50 struct miscdevice dev; /* "misc" device structure */
51#endif
52
53};
54
55static int hmcdrv_dev_open(struct inode *inode, struct file *fp);
56static int hmcdrv_dev_release(struct inode *inode, struct file *fp);
57static loff_t hmcdrv_dev_seek(struct file *fp, loff_t pos, int whence);
58static ssize_t hmcdrv_dev_read(struct file *fp, char __user *ubuf,
59 size_t len, loff_t *pos);
60static ssize_t hmcdrv_dev_write(struct file *fp, const char __user *ubuf,
61 size_t len, loff_t *pos);
62static ssize_t hmcdrv_dev_transfer(char __kernel *cmd, loff_t offset,
63 char __user *buf, size_t len);
64
65/*
66 * device operations
67 */
68static const struct file_operations hmcdrv_dev_fops = {
69 .open = hmcdrv_dev_open,
70 .llseek = hmcdrv_dev_seek,
71 .release = hmcdrv_dev_release,
72 .read = hmcdrv_dev_read,
73 .write = hmcdrv_dev_write,
74};
75
76static struct hmcdrv_dev_node hmcdrv_dev; /* HMC device struct (static) */
77
78#ifdef HMCDRV_DEV_CLASS
79
80static struct class *hmcdrv_dev_class; /* device class pointer */
81static dev_t hmcdrv_dev_no; /* device number (major/minor) */
82
83/**
84 * hmcdrv_dev_name() - provides a naming hint for a device node in /dev
85 * @dev: device for which the naming/mode hint is
86 * @mode: file mode for device node created in /dev
87 *
88 * See: devtmpfs.c, function devtmpfs_create_node()
89 *
90 * Return: recommended device file name in /dev
91 */
92static char *hmcdrv_dev_name(struct device *dev, umode_t *mode)
93{
94 char *nodename = NULL;
95 const char *devname = dev_name(dev); /* kernel device name */
96
97 if (devname)
98 nodename = kasprintf(GFP_KERNEL, "%s", devname);
99
100 /* on device destroy (rmmod) the mode pointer may be NULL
101 */
102 if (mode)
103 *mode = hmcdrv_dev.mode;
104
105 return nodename;
106}
107
108#endif /* HMCDRV_DEV_CLASS */
109
110/*
111 * open()
112 */
113static int hmcdrv_dev_open(struct inode *inode, struct file *fp)
114{
115 int rc;
116
117 /* check for non-blocking access, which is really unsupported
118 */
119 if (fp->f_flags & O_NONBLOCK)
120 return -EINVAL;
121
122 /* Because it makes no sense to open this device read-only (then a
123 * FTP command cannot be emitted), we respond with an error.
124 */
125 if ((fp->f_flags & O_ACCMODE) == O_RDONLY)
126 return -EINVAL;
127
128 /* prevent unloading this module as long as anyone holds the
129 * device file open - so increment the reference count here
130 */
131 if (!try_module_get(THIS_MODULE))
132 return -ENODEV;
133
134 fp->private_data = NULL; /* no command yet */
135 rc = hmcdrv_ftp_startup();
136 if (rc)
137 module_put(THIS_MODULE);
138
139 pr_debug("open file '/dev/%s' with return code %d\n",
140 fp->f_dentry->d_name.name, rc);
141 return rc;
142}
143
144/*
145 * release()
146 */
147static int hmcdrv_dev_release(struct inode *inode, struct file *fp)
148{
149 pr_debug("closing file '/dev/%s'\n", fp->f_dentry->d_name.name);
150 kfree(fp->private_data);
151 fp->private_data = NULL;
152 hmcdrv_ftp_shutdown();
153 module_put(THIS_MODULE);
154 return 0;
155}
156
157/*
158 * lseek()
159 */
160static loff_t hmcdrv_dev_seek(struct file *fp, loff_t pos, int whence)
161{
162 switch (whence) {
163 case SEEK_CUR: /* relative to current file position */
164 pos += fp->f_pos; /* new position stored in 'pos' */
165 break;
166
167 case SEEK_SET: /* absolute (relative to beginning of file) */
168 break; /* SEEK_SET */
169
170 /* We use SEEK_END as a special indicator for a SEEK_SET
171 * (set absolute position), combined with a FTP command
172 * clear.
173 */
174 case SEEK_END:
175 if (fp->private_data) {
176 kfree(fp->private_data);
177 fp->private_data = NULL;
178 }
179
180 break; /* SEEK_END */
181
182 default: /* SEEK_DATA, SEEK_HOLE: unsupported */
183 return -EINVAL;
184 }
185
186 if (pos < 0)
187 return -EINVAL;
188
189 if (fp->f_pos != pos)
190 ++fp->f_version;
191
192 fp->f_pos = pos;
193 return pos;
194}
195
196/*
197 * transfer (helper function)
198 */
199static ssize_t hmcdrv_dev_transfer(char __kernel *cmd, loff_t offset,
200 char __user *buf, size_t len)
201{
202 ssize_t retlen;
203 unsigned trials = HMCDRV_DEV_BUSY_RETRIES;
204
205 do {
206 retlen = hmcdrv_ftp_cmd(cmd, offset, buf, len);
207
208 if (retlen != -EBUSY)
209 break;
210
211 msleep(HMCDRV_DEV_BUSY_DELAY);
212
213 } while (--trials > 0);
214
215 return retlen;
216}
217
218/*
219 * read()
220 */
221static ssize_t hmcdrv_dev_read(struct file *fp, char __user *ubuf,
222 size_t len, loff_t *pos)
223{
224 ssize_t retlen;
225
226 if (((fp->f_flags & O_ACCMODE) == O_WRONLY) ||
227 (fp->private_data == NULL)) { /* no FTP cmd defined ? */
228 return -EBADF;
229 }
230
231 retlen = hmcdrv_dev_transfer((char *) fp->private_data,
232 *pos, ubuf, len);
233
234 pr_debug("read from file '/dev/%s' at %lld returns %zd/%zu\n",
235 fp->f_dentry->d_name.name, (long long) *pos, retlen, len);
236
237 if (retlen > 0)
238 *pos += retlen;
239
240 return retlen;
241}
242
243/*
244 * write()
245 */
246static ssize_t hmcdrv_dev_write(struct file *fp, const char __user *ubuf,
247 size_t len, loff_t *pos)
248{
249 ssize_t retlen;
250
251 pr_debug("writing file '/dev/%s' at pos. %lld with length %zd\n",
252 fp->f_dentry->d_name.name, (long long) *pos, len);
253
254 if (!fp->private_data) { /* first expect a cmd write */
255 fp->private_data = kmalloc(len + 1, GFP_KERNEL);
256
257 if (!fp->private_data)
258 return -ENOMEM;
259
260 if (!copy_from_user(fp->private_data, ubuf, len)) {
261 ((char *)fp->private_data)[len] = '\0';
262 return len;
263 }
264
265 kfree(fp->private_data);
266 fp->private_data = NULL;
267 return -EFAULT;
268 }
269
270 retlen = hmcdrv_dev_transfer((char *) fp->private_data,
271 *pos, (char __user *) ubuf, len);
272 if (retlen > 0)
273 *pos += retlen;
274
275 pr_debug("write to file '/dev/%s' returned %zd\n",
276 fp->f_dentry->d_name.name, retlen);
277
278 return retlen;
279}
280
281/**
282 * hmcdrv_dev_init() - creates a HMC drive CD/DVD device
283 *
284 * This function creates a HMC drive CD/DVD kernel device and an associated
285 * device under /dev, using a dynamically allocated major number.
286 *
287 * Return: 0 on success, else an error code.
288 */
289int hmcdrv_dev_init(void)
290{
291 int rc;
292
293#ifdef HMCDRV_DEV_CLASS
294 struct device *dev;
295
296 rc = alloc_chrdev_region(&hmcdrv_dev_no, 0, 1, HMCDRV_DEV_NAME);
297
298 if (rc)
299 goto out_err;
300
301 cdev_init(&hmcdrv_dev.dev, &hmcdrv_dev_fops);
302 hmcdrv_dev.dev.owner = THIS_MODULE;
303 rc = cdev_add(&hmcdrv_dev.dev, hmcdrv_dev_no, 1);
304
305 if (rc)
306 goto out_unreg;
307
308 /* At this point the character device exists in the kernel (see
309 * /proc/devices), but not under /dev nor /sys/devices/virtual. So
310 * we have to create an associated class (see /sys/class).
311 */
312 hmcdrv_dev_class = class_create(THIS_MODULE, HMCDRV_DEV_CLASS);
313
314 if (IS_ERR(hmcdrv_dev_class)) {
315 rc = PTR_ERR(hmcdrv_dev_class);
316 goto out_devdel;
317 }
318
319 /* Finally a device node in /dev has to be established (as 'mkdev'
320 * does from the command line). Notice that assignment of a device
321 * node name/mode function is optional (only for mode != 0600).
322 */
323 hmcdrv_dev.mode = 0; /* "unset" */
324 hmcdrv_dev_class->devnode = hmcdrv_dev_name;
325
326 dev = device_create(hmcdrv_dev_class, NULL, hmcdrv_dev_no, NULL,
327 "%s", HMCDRV_DEV_NAME);
328 if (!IS_ERR(dev))
329 return 0;
330
331 rc = PTR_ERR(dev);
332 class_destroy(hmcdrv_dev_class);
333 hmcdrv_dev_class = NULL;
334
335out_devdel:
336 cdev_del(&hmcdrv_dev.dev);
337
338out_unreg:
339 unregister_chrdev_region(hmcdrv_dev_no, 1);
340
341out_err:
342
343#else /* !HMCDRV_DEV_CLASS */
344 hmcdrv_dev.dev.minor = MISC_DYNAMIC_MINOR;
345 hmcdrv_dev.dev.name = HMCDRV_DEV_NAME;
346 hmcdrv_dev.dev.fops = &hmcdrv_dev_fops;
347 hmcdrv_dev.dev.mode = 0; /* finally produces 0600 */
348 rc = misc_register(&hmcdrv_dev.dev);
349#endif /* HMCDRV_DEV_CLASS */
350
351 return rc;
352}
353
354/**
355 * hmcdrv_dev_exit() - destroys a HMC drive CD/DVD device
356 */
357void hmcdrv_dev_exit(void)
358{
359#ifdef HMCDRV_DEV_CLASS
360 if (!IS_ERR_OR_NULL(hmcdrv_dev_class)) {
361 device_destroy(hmcdrv_dev_class, hmcdrv_dev_no);
362 class_destroy(hmcdrv_dev_class);
363 }
364
365 cdev_del(&hmcdrv_dev.dev);
366 unregister_chrdev_region(hmcdrv_dev_no, 1);
367#else /* !HMCDRV_DEV_CLASS */
368 misc_deregister(&hmcdrv_dev.dev);
369#endif /* HMCDRV_DEV_CLASS */
370}
diff --git a/drivers/s390/char/hmcdrv_dev.h b/drivers/s390/char/hmcdrv_dev.h
new file mode 100644
index 000000000000..cb17f07e02de
--- /dev/null
+++ b/drivers/s390/char/hmcdrv_dev.h
@@ -0,0 +1,14 @@
1/*
2 * SE/HMC Drive FTP Device
3 *
4 * Copyright IBM Corp. 2013
5 * Author(s): Ralf Hoppe (rhoppe@de.ibm.com)
6 */
7
8#ifndef __HMCDRV_DEV_H__
9#define __HMCDRV_DEV_H__
10
11int hmcdrv_dev_init(void);
12void hmcdrv_dev_exit(void);
13
14#endif /* __HMCDRV_DEV_H__ */
diff --git a/drivers/s390/char/hmcdrv_ftp.c b/drivers/s390/char/hmcdrv_ftp.c
new file mode 100644
index 000000000000..4bd63322fc29
--- /dev/null
+++ b/drivers/s390/char/hmcdrv_ftp.c
@@ -0,0 +1,343 @@
1/*
2 * HMC Drive FTP Services
3 *
4 * Copyright IBM Corp. 2013
5 * Author(s): Ralf Hoppe (rhoppe@de.ibm.com)
6 */
7
8#define KMSG_COMPONENT "hmcdrv"
9#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
10
11#include <linux/kernel.h>
12#include <linux/slab.h>
13#include <linux/uaccess.h>
14#include <linux/export.h>
15
16#include <linux/ctype.h>
17#include <linux/crc16.h>
18
19#include "hmcdrv_ftp.h"
20#include "hmcdrv_cache.h"
21#include "sclp_ftp.h"
22#include "diag_ftp.h"
23
24/**
25 * struct hmcdrv_ftp_ops - HMC drive FTP operations
26 * @startup: startup function
27 * @shutdown: shutdown function
28 * @cmd: FTP transfer function
29 */
30struct hmcdrv_ftp_ops {
31 int (*startup)(void);
32 void (*shutdown)(void);
33 ssize_t (*transfer)(const struct hmcdrv_ftp_cmdspec *ftp,
34 size_t *fsize);
35};
36
37static enum hmcdrv_ftp_cmdid hmcdrv_ftp_cmd_getid(const char *cmd, int len);
38static int hmcdrv_ftp_parse(char *cmd, struct hmcdrv_ftp_cmdspec *ftp);
39
40static struct hmcdrv_ftp_ops *hmcdrv_ftp_funcs; /* current operations */
41static DEFINE_MUTEX(hmcdrv_ftp_mutex); /* mutex for hmcdrv_ftp_funcs */
42static unsigned hmcdrv_ftp_refcnt; /* start/shutdown reference counter */
43
44/**
45 * hmcdrv_ftp_cmd_getid() - determine FTP command ID from a command string
46 * @cmd: FTP command string (NOT zero-terminated)
47 * @len: length of FTP command string in @cmd
48 */
49static enum hmcdrv_ftp_cmdid hmcdrv_ftp_cmd_getid(const char *cmd, int len)
50{
51 /* HMC FTP command descriptor */
52 struct hmcdrv_ftp_cmd_desc {
53 const char *str; /* command string */
54 enum hmcdrv_ftp_cmdid cmd; /* associated command as enum */
55 };
56
57 /* Description of all HMC drive FTP commands
58 *
59 * Notes:
60 * 1. Array size should be a prime number.
61 * 2. Do not change the order of commands in table (because the
62 * index is determined by CRC % ARRAY_SIZE).
63 * 3. Original command 'nlist' was renamed, else the CRC would
64 * collide with 'append' (see point 2).
65 */
66 static const struct hmcdrv_ftp_cmd_desc ftpcmds[7] = {
67
68 {.str = "get", /* [0] get (CRC = 0x68eb) */
69 .cmd = HMCDRV_FTP_GET},
70 {.str = "dir", /* [1] dir (CRC = 0x6a9e) */
71 .cmd = HMCDRV_FTP_DIR},
72 {.str = "delete", /* [2] delete (CRC = 0x53ae) */
73 .cmd = HMCDRV_FTP_DELETE},
74 {.str = "nls", /* [3] nls (CRC = 0xf87c) */
75 .cmd = HMCDRV_FTP_NLIST},
76 {.str = "put", /* [4] put (CRC = 0xac56) */
77 .cmd = HMCDRV_FTP_PUT},
78 {.str = "append", /* [5] append (CRC = 0xf56e) */
79 .cmd = HMCDRV_FTP_APPEND},
80 {.str = NULL} /* [6] unused */
81 };
82
83 const struct hmcdrv_ftp_cmd_desc *pdesc;
84
85 u16 crc = 0xffffU;
86
87 if (len == 0)
88 return HMCDRV_FTP_NOOP; /* error indiactor */
89
90 crc = crc16(crc, cmd, len);
91 pdesc = ftpcmds + (crc % ARRAY_SIZE(ftpcmds));
92 pr_debug("FTP command '%s' has CRC 0x%04x, at table pos. %lu\n",
93 cmd, crc, (crc % ARRAY_SIZE(ftpcmds)));
94
95 if (!pdesc->str || strncmp(pdesc->str, cmd, len))
96 return HMCDRV_FTP_NOOP;
97
98 pr_debug("FTP command '%s' found, with ID %d\n",
99 pdesc->str, pdesc->cmd);
100
101 return pdesc->cmd;
102}
103
104/**
105 * hmcdrv_ftp_parse() - HMC drive FTP command parser
106 * @cmd: FTP command string "<cmd> <filename>"
107 * @ftp: Pointer to FTP command specification buffer (output)
108 *
109 * Return: 0 on success, else a (negative) error code
110 */
111static int hmcdrv_ftp_parse(char *cmd, struct hmcdrv_ftp_cmdspec *ftp)
112{
113 char *start;
114 int argc = 0;
115
116 ftp->id = HMCDRV_FTP_NOOP;
117 ftp->fname = NULL;
118
119 while (*cmd != '\0') {
120
121 while (isspace(*cmd))
122 ++cmd;
123
124 if (*cmd == '\0')
125 break;
126
127 start = cmd;
128
129 switch (argc) {
130 case 0: /* 1st argument (FTP command) */
131 while ((*cmd != '\0') && !isspace(*cmd))
132 ++cmd;
133 ftp->id = hmcdrv_ftp_cmd_getid(start, cmd - start);
134 break;
135 case 1: /* 2nd / last argument (rest of line) */
136 while ((*cmd != '\0') && !iscntrl(*cmd))
137 ++cmd;
138 ftp->fname = start;
139 /* fall through */
140 default:
141 *cmd = '\0';
142 break;
143 } /* switch */
144
145 ++argc;
146 } /* while */
147
148 if (!ftp->fname || (ftp->id == HMCDRV_FTP_NOOP))
149 return -EINVAL;
150
151 return 0;
152}
153
154/**
155 * hmcdrv_ftp_do() - perform a HMC drive FTP, with data from kernel-space
156 * @ftp: pointer to FTP command specification
157 *
158 * Return: number of bytes read/written or a negative error code
159 */
160ssize_t hmcdrv_ftp_do(const struct hmcdrv_ftp_cmdspec *ftp)
161{
162 ssize_t len;
163
164 mutex_lock(&hmcdrv_ftp_mutex);
165
166 if (hmcdrv_ftp_funcs && hmcdrv_ftp_refcnt) {
167 pr_debug("starting transfer, cmd %d for '%s' at %lld with %zd bytes\n",
168 ftp->id, ftp->fname, (long long) ftp->ofs, ftp->len);
169 len = hmcdrv_cache_cmd(ftp, hmcdrv_ftp_funcs->transfer);
170 } else {
171 len = -ENXIO;
172 }
173
174 mutex_unlock(&hmcdrv_ftp_mutex);
175 return len;
176}
177EXPORT_SYMBOL(hmcdrv_ftp_do);
178
179/**
180 * hmcdrv_ftp_probe() - probe for the HMC drive FTP service
181 *
182 * Return: 0 if service is available, else an (negative) error code
183 */
184int hmcdrv_ftp_probe(void)
185{
186 int rc;
187
188 struct hmcdrv_ftp_cmdspec ftp = {
189 .id = HMCDRV_FTP_NOOP,
190 .ofs = 0,
191 .fname = "",
192 .len = PAGE_SIZE
193 };
194
195 ftp.buf = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
196
197 if (!ftp.buf)
198 return -ENOMEM;
199
200 rc = hmcdrv_ftp_startup();
201
202 if (rc)
203 return rc;
204
205 rc = hmcdrv_ftp_do(&ftp);
206 free_page((unsigned long) ftp.buf);
207 hmcdrv_ftp_shutdown();
208
209 switch (rc) {
210 case -ENOENT: /* no such file/media or currently busy, */
211 case -EBUSY: /* but service seems to be available */
212 rc = 0;
213 break;
214 default: /* leave 'rc' as it is for [0, -EPERM, -E...] */
215 if (rc > 0)
216 rc = 0; /* clear length (success) */
217 break;
218 } /* switch */
219
220 return rc;
221}
222EXPORT_SYMBOL(hmcdrv_ftp_probe);
223
224/**
225 * hmcdrv_ftp_cmd() - Perform a HMC drive FTP, with data from user-space
226 *
227 * @cmd: FTP command string "<cmd> <filename>"
228 * @offset: file position to read/write
229 * @buf: user-space buffer for read/written directory/file
230 * @len: size of @buf (read/dir) or number of bytes to write
231 *
232 * This function must not be called before hmcdrv_ftp_startup() was called.
233 *
234 * Return: number of bytes read/written or a negative error code
235 */
236ssize_t hmcdrv_ftp_cmd(char __kernel *cmd, loff_t offset,
237 char __user *buf, size_t len)
238{
239 int order;
240
241 struct hmcdrv_ftp_cmdspec ftp = {.len = len, .ofs = offset};
242 ssize_t retlen = hmcdrv_ftp_parse(cmd, &ftp);
243
244 if (retlen)
245 return retlen;
246
247 order = get_order(ftp.len);
248 ftp.buf = (void *) __get_free_pages(GFP_KERNEL | GFP_DMA, order);
249
250 if (!ftp.buf)
251 return -ENOMEM;
252
253 switch (ftp.id) {
254 case HMCDRV_FTP_DIR:
255 case HMCDRV_FTP_NLIST:
256 case HMCDRV_FTP_GET:
257 retlen = hmcdrv_ftp_do(&ftp);
258
259 if ((retlen >= 0) &&
260 copy_to_user(buf, ftp.buf, retlen))
261 retlen = -EFAULT;
262 break;
263
264 case HMCDRV_FTP_PUT:
265 case HMCDRV_FTP_APPEND:
266 if (!copy_from_user(ftp.buf, buf, ftp.len))
267 retlen = hmcdrv_ftp_do(&ftp);
268 else
269 retlen = -EFAULT;
270 break;
271
272 case HMCDRV_FTP_DELETE:
273 retlen = hmcdrv_ftp_do(&ftp);
274 break;
275
276 default:
277 retlen = -EOPNOTSUPP;
278 break;
279 }
280
281 free_pages((unsigned long) ftp.buf, order);
282 return retlen;
283}
284
285/**
286 * hmcdrv_ftp_startup() - startup of HMC drive FTP functionality for a
287 * dedicated (owner) instance
288 *
289 * Return: 0 on success, else an (negative) error code
290 */
291int hmcdrv_ftp_startup(void)
292{
293 static struct hmcdrv_ftp_ops hmcdrv_ftp_zvm = {
294 .startup = diag_ftp_startup,
295 .shutdown = diag_ftp_shutdown,
296 .transfer = diag_ftp_cmd
297 };
298
299 static struct hmcdrv_ftp_ops hmcdrv_ftp_lpar = {
300 .startup = sclp_ftp_startup,
301 .shutdown = sclp_ftp_shutdown,
302 .transfer = sclp_ftp_cmd
303 };
304
305 int rc = 0;
306
307 mutex_lock(&hmcdrv_ftp_mutex); /* block transfers while start-up */
308
309 if (hmcdrv_ftp_refcnt == 0) {
310 if (MACHINE_IS_VM)
311 hmcdrv_ftp_funcs = &hmcdrv_ftp_zvm;
312 else if (MACHINE_IS_LPAR || MACHINE_IS_KVM)
313 hmcdrv_ftp_funcs = &hmcdrv_ftp_lpar;
314 else
315 rc = -EOPNOTSUPP;
316
317 if (hmcdrv_ftp_funcs)
318 rc = hmcdrv_ftp_funcs->startup();
319 }
320
321 if (!rc)
322 ++hmcdrv_ftp_refcnt;
323
324 mutex_unlock(&hmcdrv_ftp_mutex);
325 return rc;
326}
327EXPORT_SYMBOL(hmcdrv_ftp_startup);
328
329/**
330 * hmcdrv_ftp_shutdown() - shutdown of HMC drive FTP functionality for a
331 * dedicated (owner) instance
332 */
333void hmcdrv_ftp_shutdown(void)
334{
335 mutex_lock(&hmcdrv_ftp_mutex);
336 --hmcdrv_ftp_refcnt;
337
338 if ((hmcdrv_ftp_refcnt == 0) && hmcdrv_ftp_funcs)
339 hmcdrv_ftp_funcs->shutdown();
340
341 mutex_unlock(&hmcdrv_ftp_mutex);
342}
343EXPORT_SYMBOL(hmcdrv_ftp_shutdown);
diff --git a/drivers/s390/char/hmcdrv_ftp.h b/drivers/s390/char/hmcdrv_ftp.h
new file mode 100644
index 000000000000..f3643a7b3676
--- /dev/null
+++ b/drivers/s390/char/hmcdrv_ftp.h
@@ -0,0 +1,63 @@
1/*
2 * SE/HMC Drive FTP Services
3 *
4 * Copyright IBM Corp. 2013
5 * Author(s): Ralf Hoppe (rhoppe@de.ibm.com)
6 */
7
8#ifndef __HMCDRV_FTP_H__
9#define __HMCDRV_FTP_H__
10
11#include <linux/types.h> /* size_t, loff_t */
12
13/*
14 * HMC drive FTP Service max. length of path (w/ EOS)
15 */
16#define HMCDRV_FTP_FIDENT_MAX 192
17
18/**
19 * enum hmcdrv_ftp_cmdid - HMC drive FTP commands
20 * @HMCDRV_FTP_NOOP: do nothing (only for probing)
21 * @HMCDRV_FTP_GET: read a file
22 * @HMCDRV_FTP_PUT: (over-) write a file
23 * @HMCDRV_FTP_APPEND: append to a file
24 * @HMCDRV_FTP_DIR: list directory long (ls -l)
25 * @HMCDRV_FTP_NLIST: list files, no directories (name list)
26 * @HMCDRV_FTP_DELETE: delete a file
27 * @HMCDRV_FTP_CANCEL: cancel operation (SCLP/LPAR only)
28 */
29enum hmcdrv_ftp_cmdid {
30 HMCDRV_FTP_NOOP = 0,
31 HMCDRV_FTP_GET = 1,
32 HMCDRV_FTP_PUT = 2,
33 HMCDRV_FTP_APPEND = 3,
34 HMCDRV_FTP_DIR = 4,
35 HMCDRV_FTP_NLIST = 5,
36 HMCDRV_FTP_DELETE = 6,
37 HMCDRV_FTP_CANCEL = 7
38};
39
40/**
41 * struct hmcdrv_ftp_cmdspec - FTP command specification
42 * @id: FTP command ID
43 * @ofs: offset in file
44 * @fname: filename (ASCII), null-terminated
45 * @buf: kernel-space transfer data buffer, 4k aligned
46 * @len: (max) number of bytes to transfer from/to @buf
47 */
48struct hmcdrv_ftp_cmdspec {
49 enum hmcdrv_ftp_cmdid id;
50 loff_t ofs;
51 const char *fname;
52 void __kernel *buf;
53 size_t len;
54};
55
56int hmcdrv_ftp_startup(void);
57void hmcdrv_ftp_shutdown(void);
58int hmcdrv_ftp_probe(void);
59ssize_t hmcdrv_ftp_do(const struct hmcdrv_ftp_cmdspec *ftp);
60ssize_t hmcdrv_ftp_cmd(char __kernel *cmd, loff_t offset,
61 char __user *buf, size_t len);
62
63#endif /* __HMCDRV_FTP_H__ */
diff --git a/drivers/s390/char/hmcdrv_mod.c b/drivers/s390/char/hmcdrv_mod.c
new file mode 100644
index 000000000000..505c6a78ee1a
--- /dev/null
+++ b/drivers/s390/char/hmcdrv_mod.c
@@ -0,0 +1,64 @@
1/*
2 * HMC Drive DVD Module
3 *
4 * Copyright IBM Corp. 2013
5 * Author(s): Ralf Hoppe (rhoppe@de.ibm.com)
6 */
7
8#define KMSG_COMPONENT "hmcdrv"
9#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
10
11#include <linux/kernel.h>
12#include <linux/module.h>
13#include <linux/moduleparam.h>
14#include <linux/version.h>
15#include <linux/stat.h>
16
17#include "hmcdrv_ftp.h"
18#include "hmcdrv_dev.h"
19#include "hmcdrv_cache.h"
20
21MODULE_LICENSE("GPL");
22MODULE_AUTHOR("Copyright 2013 IBM Corporation");
23MODULE_DESCRIPTION("HMC drive DVD access");
24
25/*
26 * module parameter 'cachesize'
27 */
28static size_t hmcdrv_mod_cachesize = HMCDRV_CACHE_SIZE_DFLT;
29module_param_named(cachesize, hmcdrv_mod_cachesize, ulong, S_IRUGO);
30
31/**
32 * hmcdrv_mod_init() - module init function
33 */
34static int __init hmcdrv_mod_init(void)
35{
36 int rc = hmcdrv_ftp_probe(); /* perform w/o cache */
37
38 if (rc)
39 return rc;
40
41 rc = hmcdrv_cache_startup(hmcdrv_mod_cachesize);
42
43 if (rc)
44 return rc;
45
46 rc = hmcdrv_dev_init();
47
48 if (rc)
49 hmcdrv_cache_shutdown();
50
51 return rc;
52}
53
54/**
55 * hmcdrv_mod_exit() - module exit function
56 */
57static void __exit hmcdrv_mod_exit(void)
58{
59 hmcdrv_dev_exit();
60 hmcdrv_cache_shutdown();
61}
62
63module_init(hmcdrv_mod_init);
64module_exit(hmcdrv_mod_exit);
diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h
index a68b5ec7d042..a88069f8c677 100644
--- a/drivers/s390/char/sclp.h
+++ b/drivers/s390/char/sclp.h
@@ -19,6 +19,7 @@
19 19
20#define EVTYP_OPCMD 0x01 20#define EVTYP_OPCMD 0x01
21#define EVTYP_MSG 0x02 21#define EVTYP_MSG 0x02
22#define EVTYP_DIAG_TEST 0x07
22#define EVTYP_STATECHANGE 0x08 23#define EVTYP_STATECHANGE 0x08
23#define EVTYP_PMSGCMD 0x09 24#define EVTYP_PMSGCMD 0x09
24#define EVTYP_CNTLPROGOPCMD 0x20 25#define EVTYP_CNTLPROGOPCMD 0x20
@@ -32,6 +33,7 @@
32 33
33#define EVTYP_OPCMD_MASK 0x80000000 34#define EVTYP_OPCMD_MASK 0x80000000
34#define EVTYP_MSG_MASK 0x40000000 35#define EVTYP_MSG_MASK 0x40000000
36#define EVTYP_DIAG_TEST_MASK 0x02000000
35#define EVTYP_STATECHANGE_MASK 0x01000000 37#define EVTYP_STATECHANGE_MASK 0x01000000
36#define EVTYP_PMSGCMD_MASK 0x00800000 38#define EVTYP_PMSGCMD_MASK 0x00800000
37#define EVTYP_CTLPROGOPCMD_MASK 0x00000001 39#define EVTYP_CTLPROGOPCMD_MASK 0x00000001
diff --git a/drivers/s390/char/sclp_diag.h b/drivers/s390/char/sclp_diag.h
new file mode 100644
index 000000000000..59c4afa5e670
--- /dev/null
+++ b/drivers/s390/char/sclp_diag.h
@@ -0,0 +1,89 @@
1/*
2 * Copyright IBM Corp. 2013
3 * Author(s): Ralf Hoppe (rhoppe@de.ibm.com)
4 */
5
6#ifndef _SCLP_DIAG_H
7#define _SCLP_DIAG_H
8
9#include <linux/types.h>
10
11/* return codes for Diagnostic Test FTP Service, as indicated in member
12 * sclp_diag_ftp::ldflg
13 */
14#define SCLP_DIAG_FTP_OK 0x80U /* success */
15#define SCLP_DIAG_FTP_LDFAIL 0x01U /* load failed */
16#define SCLP_DIAG_FTP_LDNPERM 0x02U /* not allowed */
17#define SCLP_DIAG_FTP_LDRUNS 0x03U /* LD runs */
18#define SCLP_DIAG_FTP_LDNRUNS 0x04U /* LD does not run */
19
20#define SCLP_DIAG_FTP_XPCX 0x80 /* PCX communication code */
21#define SCLP_DIAG_FTP_ROUTE 4 /* routing code for new FTP service */
22
23/*
24 * length of Diagnostic Test FTP Service event buffer
25 */
26#define SCLP_DIAG_FTP_EVBUF_LEN \
27 (offsetof(struct sclp_diag_evbuf, mdd) + \
28 sizeof(struct sclp_diag_ftp))
29
30/**
31 * struct sclp_diag_ftp - Diagnostic Test FTP Service model-dependent data
32 * @pcx: code for PCX communication (should be 0x80)
33 * @ldflg: load flag (see defines above)
34 * @cmd: FTP command
35 * @pgsize: page size (0 = 4kB, 1 = large page size)
36 * @srcflg: source flag
37 * @spare: reserved (zeroes)
38 * @offset: file offset
39 * @fsize: file size
40 * @length: buffer size resp. bytes transferred
41 * @failaddr: failing address
42 * @bufaddr: buffer address, virtual
43 * @asce: region or segment table designation
44 * @fident: file name (ASCII, zero-terminated)
45 */
46struct sclp_diag_ftp {
47 u8 pcx;
48 u8 ldflg;
49 u8 cmd;
50 u8 pgsize;
51 u8 srcflg;
52 u8 spare;
53 u64 offset;
54 u64 fsize;
55 u64 length;
56 u64 failaddr;
57 u64 bufaddr;
58 u64 asce;
59
60 u8 fident[256];
61} __packed;
62
63/**
64 * struct sclp_diag_evbuf - Diagnostic Test (ET7) Event Buffer
65 * @hdr: event buffer header
66 * @route: diagnostic route
67 * @mdd: model-dependent data (@route dependent)
68 */
69struct sclp_diag_evbuf {
70 struct evbuf_header hdr;
71 u16 route;
72
73 union {
74 struct sclp_diag_ftp ftp;
75 } mdd;
76} __packed;
77
78/**
79 * struct sclp_diag_sccb - Diagnostic Test (ET7) SCCB
80 * @hdr: SCCB header
81 * @evbuf: event buffer
82 */
83struct sclp_diag_sccb {
84
85 struct sccb_header hdr;
86 struct sclp_diag_evbuf evbuf;
87} __packed;
88
89#endif /* _SCLP_DIAG_H */
diff --git a/drivers/s390/char/sclp_ftp.c b/drivers/s390/char/sclp_ftp.c
new file mode 100644
index 000000000000..6561cc5b2d5d
--- /dev/null
+++ b/drivers/s390/char/sclp_ftp.c
@@ -0,0 +1,275 @@
1/*
2 * SCLP Event Type (ET) 7 - Diagnostic Test FTP Services, useable on LPAR
3 *
4 * Copyright IBM Corp. 2013
5 * Author(s): Ralf Hoppe (rhoppe@de.ibm.com)
6 *
7 */
8
9#define KMSG_COMPONENT "hmcdrv"
10#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
11
12#include <linux/kernel.h>
13#include <linux/mm.h>
14#include <linux/slab.h>
15#include <linux/io.h>
16#include <linux/wait.h>
17#include <linux/string.h>
18#include <linux/jiffies.h>
19#include <asm/sysinfo.h>
20#include <asm/ebcdic.h>
21
22#include "sclp.h"
23#include "sclp_diag.h"
24#include "sclp_ftp.h"
25
26static DECLARE_COMPLETION(sclp_ftp_rx_complete);
27static u8 sclp_ftp_ldflg;
28static u64 sclp_ftp_fsize;
29static u64 sclp_ftp_length;
30
31/**
32 * sclp_ftp_txcb() - Diagnostic Test FTP services SCLP command callback
33 */
34static void sclp_ftp_txcb(struct sclp_req *req, void *data)
35{
36 struct completion *completion = data;
37
38#ifdef DEBUG
39 pr_debug("SCLP (ET7) TX-IRQ, SCCB @ 0x%p: %*phN\n",
40 req->sccb, 24, req->sccb);
41#endif
42 complete(completion);
43}
44
45/**
46 * sclp_ftp_rxcb() - Diagnostic Test FTP services receiver event callback
47 */
48static void sclp_ftp_rxcb(struct evbuf_header *evbuf)
49{
50 struct sclp_diag_evbuf *diag = (struct sclp_diag_evbuf *) evbuf;
51
52 /*
53 * Check for Diagnostic Test FTP Service
54 */
55 if (evbuf->type != EVTYP_DIAG_TEST ||
56 diag->route != SCLP_DIAG_FTP_ROUTE ||
57 diag->mdd.ftp.pcx != SCLP_DIAG_FTP_XPCX ||
58 evbuf->length < SCLP_DIAG_FTP_EVBUF_LEN)
59 return;
60
61#ifdef DEBUG
62 pr_debug("SCLP (ET7) RX-IRQ, Event @ 0x%p: %*phN\n",
63 evbuf, 24, evbuf);
64#endif
65
66 /*
67 * Because the event buffer is located in a page which is owned
68 * by the SCLP core, all data of interest must be copied. The
69 * error indication is in 'sclp_ftp_ldflg'
70 */
71 sclp_ftp_ldflg = diag->mdd.ftp.ldflg;
72 sclp_ftp_fsize = diag->mdd.ftp.fsize;
73 sclp_ftp_length = diag->mdd.ftp.length;
74
75 complete(&sclp_ftp_rx_complete);
76}
77
78/**
79 * sclp_ftp_et7() - start a Diagnostic Test FTP Service SCLP request
80 * @ftp: pointer to FTP descriptor
81 *
82 * Return: 0 on success, else a (negative) error code
83 */
84static int sclp_ftp_et7(const struct hmcdrv_ftp_cmdspec *ftp)
85{
86 struct completion completion;
87 struct sclp_diag_sccb *sccb;
88 struct sclp_req *req;
89 size_t len;
90 int rc;
91
92 req = kzalloc(sizeof(*req), GFP_KERNEL);
93 sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
94 if (!req || !sccb) {
95 rc = -ENOMEM;
96 goto out_free;
97 }
98
99 sccb->hdr.length = SCLP_DIAG_FTP_EVBUF_LEN +
100 sizeof(struct sccb_header);
101 sccb->evbuf.hdr.type = EVTYP_DIAG_TEST;
102 sccb->evbuf.hdr.length = SCLP_DIAG_FTP_EVBUF_LEN;
103 sccb->evbuf.hdr.flags = 0; /* clear processed-buffer */
104 sccb->evbuf.route = SCLP_DIAG_FTP_ROUTE;
105 sccb->evbuf.mdd.ftp.pcx = SCLP_DIAG_FTP_XPCX;
106 sccb->evbuf.mdd.ftp.srcflg = 0;
107 sccb->evbuf.mdd.ftp.pgsize = 0;
108 sccb->evbuf.mdd.ftp.asce = _ASCE_REAL_SPACE;
109 sccb->evbuf.mdd.ftp.ldflg = SCLP_DIAG_FTP_LDFAIL;
110 sccb->evbuf.mdd.ftp.fsize = 0;
111 sccb->evbuf.mdd.ftp.cmd = ftp->id;
112 sccb->evbuf.mdd.ftp.offset = ftp->ofs;
113 sccb->evbuf.mdd.ftp.length = ftp->len;
114 sccb->evbuf.mdd.ftp.bufaddr = virt_to_phys(ftp->buf);
115
116 len = strlcpy(sccb->evbuf.mdd.ftp.fident, ftp->fname,
117 HMCDRV_FTP_FIDENT_MAX);
118 if (len >= HMCDRV_FTP_FIDENT_MAX) {
119 rc = -EINVAL;
120 goto out_free;
121 }
122
123 req->command = SCLP_CMDW_WRITE_EVENT_DATA;
124 req->sccb = sccb;
125 req->status = SCLP_REQ_FILLED;
126 req->callback = sclp_ftp_txcb;
127 req->callback_data = &completion;
128
129 init_completion(&completion);
130
131 rc = sclp_add_request(req);
132 if (rc)
133 goto out_free;
134
135 /* Wait for end of ftp sclp command. */
136 wait_for_completion(&completion);
137
138#ifdef DEBUG
139 pr_debug("status of SCLP (ET7) request is 0x%04x (0x%02x)\n",
140 sccb->hdr.response_code, sccb->evbuf.hdr.flags);
141#endif
142
143 /*
144 * Check if sclp accepted the request. The data transfer runs
145 * asynchronously and the completion is indicated with an
146 * sclp ET7 event.
147 */
148 if (req->status != SCLP_REQ_DONE ||
149 (sccb->evbuf.hdr.flags & 0x80) == 0 || /* processed-buffer */
150 (sccb->hdr.response_code & 0xffU) != 0x20U) {
151 rc = -EIO;
152 }
153
154out_free:
155 free_page((unsigned long) sccb);
156 kfree(req);
157 return rc;
158}
159
160/**
161 * sclp_ftp_cmd() - executes a HMC related SCLP Diagnose (ET7) FTP command
162 * @ftp: pointer to FTP command specification
163 * @fsize: return of file size (or NULL if undesirable)
164 *
165 * Attention: Notice that this function is not reentrant - so the caller
166 * must ensure locking.
167 *
168 * Return: number of bytes read/written or a (negative) error code
169 */
170ssize_t sclp_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize)
171{
172 ssize_t len;
173#ifdef DEBUG
174 unsigned long start_jiffies;
175
176 pr_debug("starting SCLP (ET7), cmd %d for '%s' at %lld with %zd bytes\n",
177 ftp->id, ftp->fname, (long long) ftp->ofs, ftp->len);
178 start_jiffies = jiffies;
179#endif
180
181 init_completion(&sclp_ftp_rx_complete);
182
183 /* Start ftp sclp command. */
184 len = sclp_ftp_et7(ftp);
185 if (len)
186 goto out_unlock;
187
188 /*
189 * There is no way to cancel the sclp ET7 request, the code
190 * needs to wait unconditionally until the transfer is complete.
191 */
192 wait_for_completion(&sclp_ftp_rx_complete);
193
194#ifdef DEBUG
195 pr_debug("completed SCLP (ET7) request after %lu ms (all)\n",
196 (jiffies - start_jiffies) * 1000 / HZ);
197 pr_debug("return code of SCLP (ET7) FTP Service is 0x%02x, with %lld/%lld bytes\n",
198 sclp_ftp_ldflg, sclp_ftp_length, sclp_ftp_fsize);
199#endif
200
201 switch (sclp_ftp_ldflg) {
202 case SCLP_DIAG_FTP_OK:
203 len = sclp_ftp_length;
204 if (fsize)
205 *fsize = sclp_ftp_fsize;
206 break;
207 case SCLP_DIAG_FTP_LDNPERM:
208 len = -EPERM;
209 break;
210 case SCLP_DIAG_FTP_LDRUNS:
211 len = -EBUSY;
212 break;
213 case SCLP_DIAG_FTP_LDFAIL:
214 len = -ENOENT;
215 break;
216 default:
217 len = -EIO;
218 break;
219 }
220
221out_unlock:
222 return len;
223}
224
225/*
226 * ET7 event listener
227 */
228static struct sclp_register sclp_ftp_event = {
229 .send_mask = EVTYP_DIAG_TEST_MASK, /* want tx events */
230 .receive_mask = EVTYP_DIAG_TEST_MASK, /* want rx events */
231 .receiver_fn = sclp_ftp_rxcb, /* async callback (rx) */
232 .state_change_fn = NULL,
233 .pm_event_fn = NULL,
234};
235
236/**
237 * sclp_ftp_startup() - startup of FTP services, when running on LPAR
238 */
239int sclp_ftp_startup(void)
240{
241#ifdef DEBUG
242 unsigned long info;
243#endif
244 int rc;
245
246 rc = sclp_register(&sclp_ftp_event);
247 if (rc)
248 return rc;
249
250#ifdef DEBUG
251 info = get_zeroed_page(GFP_KERNEL);
252
253 if (info != 0) {
254 struct sysinfo_2_2_2 *info222 = (struct sysinfo_2_2_2 *)info;
255
256 if (!stsi(info222, 2, 2, 2)) { /* get SYSIB 2.2.2 */
257 info222->name[sizeof(info222->name) - 1] = '\0';
258 EBCASC_500(info222->name, sizeof(info222->name) - 1);
259 pr_debug("SCLP (ET7) FTP Service working on LPAR %u (%s)\n",
260 info222->lpar_number, info222->name);
261 }
262
263 free_page(info);
264 }
265#endif /* DEBUG */
266 return 0;
267}
268
269/**
270 * sclp_ftp_shutdown() - shutdown of FTP services, when running on LPAR
271 */
272void sclp_ftp_shutdown(void)
273{
274 sclp_unregister(&sclp_ftp_event);
275}
diff --git a/drivers/s390/char/sclp_ftp.h b/drivers/s390/char/sclp_ftp.h
new file mode 100644
index 000000000000..98ba3183e7d9
--- /dev/null
+++ b/drivers/s390/char/sclp_ftp.h
@@ -0,0 +1,21 @@
1/*
2 * SCLP Event Type (ET) 7 - Diagnostic Test FTP Services, useable on LPAR
3 *
4 * Notice that all functions exported here are not reentrant.
5 * So usage should be exclusive, ensured by the caller (e.g. using a
6 * mutex).
7 *
8 * Copyright IBM Corp. 2013
9 * Author(s): Ralf Hoppe (rhoppe@de.ibm.com)
10 */
11
12#ifndef __SCLP_FTP_H__
13#define __SCLP_FTP_H__
14
15#include "hmcdrv_ftp.h"
16
17int sclp_ftp_startup(void);
18void sclp_ftp_shutdown(void);
19ssize_t sclp_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize);
20
21#endif /* __SCLP_FTP_H__ */