• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/drivers/s390/scsi/
1/*
2 * zfcp device driver
3 *
4 * Userspace interface for accessing the
5 * Access Control Lists / Control File Data Channel
6 *
7 * Copyright IBM Corporation 2008, 2009
8 */
9
10#define KMSG_COMPONENT "zfcp"
11#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
12
13#include <linux/slab.h>
14#include <linux/types.h>
15#include <linux/miscdevice.h>
16#include <asm/compat.h>
17#include <asm/ccwdev.h>
18#include "zfcp_def.h"
19#include "zfcp_ext.h"
20#include "zfcp_fsf.h"
21
22#define ZFCP_CFDC_CMND_DOWNLOAD_NORMAL		0x00010001
23#define ZFCP_CFDC_CMND_DOWNLOAD_FORCE		0x00010101
24#define ZFCP_CFDC_CMND_FULL_ACCESS		0x00000201
25#define ZFCP_CFDC_CMND_RESTRICTED_ACCESS	0x00000401
26#define ZFCP_CFDC_CMND_UPLOAD			0x00010002
27
28#define ZFCP_CFDC_DOWNLOAD			0x00000001
29#define ZFCP_CFDC_UPLOAD			0x00000002
30#define ZFCP_CFDC_WITH_CONTROL_FILE		0x00010000
31
32#define ZFCP_CFDC_IOC_MAGIC                     0xDD
33#define ZFCP_CFDC_IOC \
34	_IOWR(ZFCP_CFDC_IOC_MAGIC, 0, struct zfcp_cfdc_data)
35
36/**
37 * struct zfcp_cfdc_data - data for ioctl cfdc interface
38 * @signature: request signature
39 * @devno: FCP adapter device number
40 * @command: command code
41 * @fsf_status: returns status of FSF command to userspace
42 * @fsf_status_qual: returned to userspace
43 * @payloads: access conflicts list
44 * @control_file: access control table
45 */
46struct zfcp_cfdc_data {
47	u32 signature;
48	u32 devno;
49	u32 command;
50	u32 fsf_status;
51	u8  fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE];
52	u8  payloads[256];
53	u8  control_file[0];
54};
55
56static int zfcp_cfdc_copy_from_user(struct scatterlist *sg,
57				    void __user *user_buffer)
58{
59	unsigned int length;
60	unsigned int size = ZFCP_CFDC_MAX_SIZE;
61
62	while (size) {
63		length = min((unsigned int)size, sg->length);
64		if (copy_from_user(sg_virt(sg++), user_buffer, length))
65			return -EFAULT;
66		user_buffer += length;
67		size -= length;
68	}
69	return 0;
70}
71
72static int zfcp_cfdc_copy_to_user(void __user  *user_buffer,
73				  struct scatterlist *sg)
74{
75	unsigned int length;
76	unsigned int size = ZFCP_CFDC_MAX_SIZE;
77
78	while (size) {
79		length = min((unsigned int) size, sg->length);
80		if (copy_to_user(user_buffer, sg_virt(sg++), length))
81			return -EFAULT;
82		user_buffer += length;
83		size -= length;
84	}
85	return 0;
86}
87
88static struct zfcp_adapter *zfcp_cfdc_get_adapter(u32 devno)
89{
90	char busid[9];
91	struct ccw_device *cdev;
92	struct zfcp_adapter *adapter;
93
94	snprintf(busid, sizeof(busid), "0.0.%04x", devno);
95	cdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid);
96	if (!cdev)
97		return NULL;
98
99	adapter = zfcp_ccw_adapter_by_cdev(cdev);
100
101	put_device(&cdev->dev);
102	return adapter;
103}
104
105static int zfcp_cfdc_set_fsf(struct zfcp_fsf_cfdc *fsf_cfdc, int command)
106{
107	switch (command) {
108	case ZFCP_CFDC_CMND_DOWNLOAD_NORMAL:
109		fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
110		fsf_cfdc->option = FSF_CFDC_OPTION_NORMAL_MODE;
111		break;
112	case ZFCP_CFDC_CMND_DOWNLOAD_FORCE:
113		fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
114		fsf_cfdc->option = FSF_CFDC_OPTION_FORCE;
115		break;
116	case ZFCP_CFDC_CMND_FULL_ACCESS:
117		fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
118		fsf_cfdc->option = FSF_CFDC_OPTION_FULL_ACCESS;
119		break;
120	case ZFCP_CFDC_CMND_RESTRICTED_ACCESS:
121		fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
122		fsf_cfdc->option = FSF_CFDC_OPTION_RESTRICTED_ACCESS;
123		break;
124	case ZFCP_CFDC_CMND_UPLOAD:
125		fsf_cfdc->command = FSF_QTCB_UPLOAD_CONTROL_FILE;
126		fsf_cfdc->option = 0;
127		break;
128	default:
129		return -EINVAL;
130	}
131
132	return 0;
133}
134
135static int zfcp_cfdc_sg_setup(int command, struct scatterlist *sg,
136			      u8 __user *control_file)
137{
138	int retval;
139	retval = zfcp_sg_setup_table(sg, ZFCP_CFDC_PAGES);
140	if (retval)
141		return retval;
142
143	sg[ZFCP_CFDC_PAGES - 1].length = ZFCP_CFDC_MAX_SIZE % PAGE_SIZE;
144
145	if (command & ZFCP_CFDC_WITH_CONTROL_FILE &&
146	    command & ZFCP_CFDC_DOWNLOAD) {
147		retval = zfcp_cfdc_copy_from_user(sg, control_file);
148		if (retval) {
149			zfcp_sg_free_table(sg, ZFCP_CFDC_PAGES);
150			return -EFAULT;
151		}
152	}
153
154	return 0;
155}
156
157static void zfcp_cfdc_req_to_sense(struct zfcp_cfdc_data *data,
158				   struct zfcp_fsf_req *req)
159{
160	data->fsf_status = req->qtcb->header.fsf_status;
161	memcpy(&data->fsf_status_qual, &req->qtcb->header.fsf_status_qual,
162	       sizeof(union fsf_status_qual));
163	memcpy(&data->payloads, &req->qtcb->bottom.support.els,
164	       sizeof(req->qtcb->bottom.support.els));
165}
166
167static long zfcp_cfdc_dev_ioctl(struct file *file, unsigned int command,
168				unsigned long arg)
169{
170	struct zfcp_cfdc_data *data;
171	struct zfcp_cfdc_data __user *data_user;
172	struct zfcp_adapter *adapter;
173	struct zfcp_fsf_req *req;
174	struct zfcp_fsf_cfdc *fsf_cfdc;
175	int retval;
176
177	if (command != ZFCP_CFDC_IOC)
178		return -ENOTTY;
179
180	if (is_compat_task())
181		data_user = compat_ptr(arg);
182	else
183		data_user = (void __user *)arg;
184
185	if (!data_user)
186		return -EINVAL;
187
188	fsf_cfdc = kmalloc(sizeof(struct zfcp_fsf_cfdc), GFP_KERNEL);
189	if (!fsf_cfdc)
190		return -ENOMEM;
191
192	data = memdup_user(data_user, sizeof(*data_user));
193	if (IS_ERR(data)) {
194		retval = PTR_ERR(data);
195		goto no_mem_sense;
196	}
197
198	if (data->signature != 0xCFDCACDF) {
199		retval = -EINVAL;
200		goto free_buffer;
201	}
202
203	retval = zfcp_cfdc_set_fsf(fsf_cfdc, data->command);
204
205	adapter = zfcp_cfdc_get_adapter(data->devno);
206	if (!adapter) {
207		retval = -ENXIO;
208		goto free_buffer;
209	}
210
211	retval = zfcp_cfdc_sg_setup(data->command, fsf_cfdc->sg,
212				    data_user->control_file);
213	if (retval)
214		goto adapter_put;
215	req = zfcp_fsf_control_file(adapter, fsf_cfdc);
216	if (IS_ERR(req)) {
217		retval = PTR_ERR(req);
218		goto free_sg;
219	}
220
221	if (req->status & ZFCP_STATUS_FSFREQ_ERROR) {
222		retval = -ENXIO;
223		goto free_fsf;
224	}
225
226	zfcp_cfdc_req_to_sense(data, req);
227	retval = copy_to_user(data_user, data, sizeof(*data_user));
228	if (retval) {
229		retval = -EFAULT;
230		goto free_fsf;
231	}
232
233	if (data->command & ZFCP_CFDC_UPLOAD)
234		retval = zfcp_cfdc_copy_to_user(&data_user->control_file,
235						fsf_cfdc->sg);
236
237 free_fsf:
238	zfcp_fsf_req_free(req);
239 free_sg:
240	zfcp_sg_free_table(fsf_cfdc->sg, ZFCP_CFDC_PAGES);
241 adapter_put:
242	zfcp_ccw_adapter_put(adapter);
243 free_buffer:
244	kfree(data);
245 no_mem_sense:
246	kfree(fsf_cfdc);
247	return retval;
248}
249
250static const struct file_operations zfcp_cfdc_fops = {
251	.open = nonseekable_open,
252	.unlocked_ioctl = zfcp_cfdc_dev_ioctl,
253#ifdef CONFIG_COMPAT
254	.compat_ioctl = zfcp_cfdc_dev_ioctl
255#endif
256};
257
258struct miscdevice zfcp_cfdc_misc = {
259	.minor = MISC_DYNAMIC_MINOR,
260	.name = "zfcp_cfdc",
261	.fops = &zfcp_cfdc_fops,
262};
263