Commit ba7a4822b48fbc7afd6b567c18e316a03f46684d
Committed by
Benjamin Herrenschmidt
1 parent
aba0eb84c8
Exists in
master
and in
20 other branches
powerpc: Remove some of the legacy iSeries specific device drivers
These drivers are specific to the PowerPC legacy iSeries platform and their Kconfig is specified in arch/powerpc. Legacy iSeries is being removed, so these drivers can no longer be selected. Cc: Jens Axboe <axboe@kernel.dk> Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Showing 3 changed files with 0 additions and 2589 deletions Side-by-side Diff
drivers/block/viodasd.c
1 | -/* -*- linux-c -*- | |
2 | - * viodasd.c | |
3 | - * Authors: Dave Boutcher <boutcher@us.ibm.com> | |
4 | - * Ryan Arnold <ryanarn@us.ibm.com> | |
5 | - * Colin Devilbiss <devilbis@us.ibm.com> | |
6 | - * Stephen Rothwell | |
7 | - * | |
8 | - * (C) Copyright 2000-2004 IBM Corporation | |
9 | - * | |
10 | - * This program is free software; you can redistribute it and/or | |
11 | - * modify it under the terms of the GNU General Public License as | |
12 | - * published by the Free Software Foundation; either version 2 of the | |
13 | - * License, or (at your option) any later version. | |
14 | - * | |
15 | - * This program is distributed in the hope that it will be useful, | |
16 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | - * GNU General Public License for more details. | |
19 | - * | |
20 | - * You should have received a copy of the GNU General Public License | |
21 | - * along with this program; if not, write to the Free Software | |
22 | - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
23 | - * | |
24 | - * This routine provides access to disk space (termed "DASD" in historical | |
25 | - * IBM terms) owned and managed by an OS/400 partition running on the | |
26 | - * same box as this Linux partition. | |
27 | - * | |
28 | - * All disk operations are performed by sending messages back and forth to | |
29 | - * the OS/400 partition. | |
30 | - */ | |
31 | - | |
32 | -#define pr_fmt(fmt) "viod: " fmt | |
33 | - | |
34 | -#include <linux/major.h> | |
35 | -#include <linux/fs.h> | |
36 | -#include <linux/module.h> | |
37 | -#include <linux/kernel.h> | |
38 | -#include <linux/blkdev.h> | |
39 | -#include <linux/genhd.h> | |
40 | -#include <linux/hdreg.h> | |
41 | -#include <linux/errno.h> | |
42 | -#include <linux/init.h> | |
43 | -#include <linux/string.h> | |
44 | -#include <linux/mutex.h> | |
45 | -#include <linux/dma-mapping.h> | |
46 | -#include <linux/completion.h> | |
47 | -#include <linux/device.h> | |
48 | -#include <linux/scatterlist.h> | |
49 | - | |
50 | -#include <asm/uaccess.h> | |
51 | -#include <asm/vio.h> | |
52 | -#include <asm/iseries/hv_types.h> | |
53 | -#include <asm/iseries/hv_lp_event.h> | |
54 | -#include <asm/iseries/hv_lp_config.h> | |
55 | -#include <asm/iseries/vio.h> | |
56 | -#include <asm/firmware.h> | |
57 | - | |
58 | -MODULE_DESCRIPTION("iSeries Virtual DASD"); | |
59 | -MODULE_AUTHOR("Dave Boutcher"); | |
60 | -MODULE_LICENSE("GPL"); | |
61 | - | |
62 | -/* | |
63 | - * We only support 7 partitions per physical disk....so with minor | |
64 | - * numbers 0-255 we get a maximum of 32 disks. | |
65 | - */ | |
66 | -#define VIOD_GENHD_NAME "iseries/vd" | |
67 | - | |
68 | -#define VIOD_VERS "1.64" | |
69 | - | |
70 | -enum { | |
71 | - PARTITION_SHIFT = 3, | |
72 | - MAX_DISKNO = HVMAXARCHITECTEDVIRTUALDISKS, | |
73 | - MAX_DISK_NAME = FIELD_SIZEOF(struct gendisk, disk_name) | |
74 | -}; | |
75 | - | |
76 | -static DEFINE_MUTEX(viodasd_mutex); | |
77 | -static DEFINE_SPINLOCK(viodasd_spinlock); | |
78 | - | |
79 | -#define VIOMAXREQ 16 | |
80 | - | |
81 | -#define DEVICE_NO(cell) ((struct viodasd_device *)(cell) - &viodasd_devices[0]) | |
82 | - | |
83 | -struct viodasd_waitevent { | |
84 | - struct completion com; | |
85 | - int rc; | |
86 | - u16 sub_result; | |
87 | - int max_disk; /* open */ | |
88 | -}; | |
89 | - | |
90 | -static const struct vio_error_entry viodasd_err_table[] = { | |
91 | - { 0x0201, EINVAL, "Invalid Range" }, | |
92 | - { 0x0202, EINVAL, "Invalid Token" }, | |
93 | - { 0x0203, EIO, "DMA Error" }, | |
94 | - { 0x0204, EIO, "Use Error" }, | |
95 | - { 0x0205, EIO, "Release Error" }, | |
96 | - { 0x0206, EINVAL, "Invalid Disk" }, | |
97 | - { 0x0207, EBUSY, "Can't Lock" }, | |
98 | - { 0x0208, EIO, "Already Locked" }, | |
99 | - { 0x0209, EIO, "Already Unlocked" }, | |
100 | - { 0x020A, EIO, "Invalid Arg" }, | |
101 | - { 0x020B, EIO, "Bad IFS File" }, | |
102 | - { 0x020C, EROFS, "Read Only Device" }, | |
103 | - { 0x02FF, EIO, "Internal Error" }, | |
104 | - { 0x0000, 0, NULL }, | |
105 | -}; | |
106 | - | |
107 | -/* | |
108 | - * Figure out the biggest I/O request (in sectors) we can accept | |
109 | - */ | |
110 | -#define VIODASD_MAXSECTORS (4096 / 512 * VIOMAXBLOCKDMA) | |
111 | - | |
112 | -/* | |
113 | - * Number of disk I/O requests we've sent to OS/400 | |
114 | - */ | |
115 | -static int num_req_outstanding; | |
116 | - | |
117 | -/* | |
118 | - * This is our internal structure for keeping track of disk devices | |
119 | - */ | |
120 | -struct viodasd_device { | |
121 | - u16 cylinders; | |
122 | - u16 tracks; | |
123 | - u16 sectors; | |
124 | - u16 bytes_per_sector; | |
125 | - u64 size; | |
126 | - int read_only; | |
127 | - spinlock_t q_lock; | |
128 | - struct gendisk *disk; | |
129 | - struct device *dev; | |
130 | -} viodasd_devices[MAX_DISKNO]; | |
131 | - | |
132 | -/* | |
133 | - * External open entry point. | |
134 | - */ | |
135 | -static int viodasd_open(struct block_device *bdev, fmode_t mode) | |
136 | -{ | |
137 | - struct viodasd_device *d = bdev->bd_disk->private_data; | |
138 | - HvLpEvent_Rc hvrc; | |
139 | - struct viodasd_waitevent we; | |
140 | - u16 flags = 0; | |
141 | - | |
142 | - if (d->read_only) { | |
143 | - if (mode & FMODE_WRITE) | |
144 | - return -EROFS; | |
145 | - flags = vioblockflags_ro; | |
146 | - } | |
147 | - | |
148 | - init_completion(&we.com); | |
149 | - | |
150 | - /* Send the open event to OS/400 */ | |
151 | - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | |
152 | - HvLpEvent_Type_VirtualIo, | |
153 | - viomajorsubtype_blockio | vioblockopen, | |
154 | - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, | |
155 | - viopath_sourceinst(viopath_hostLp), | |
156 | - viopath_targetinst(viopath_hostLp), | |
157 | - (u64)(unsigned long)&we, VIOVERSION << 16, | |
158 | - ((u64)DEVICE_NO(d) << 48) | ((u64)flags << 32), | |
159 | - 0, 0, 0); | |
160 | - if (hvrc != 0) { | |
161 | - pr_warning("HV open failed %d\n", (int)hvrc); | |
162 | - return -EIO; | |
163 | - } | |
164 | - | |
165 | - wait_for_completion(&we.com); | |
166 | - | |
167 | - /* Check the return code */ | |
168 | - if (we.rc != 0) { | |
169 | - const struct vio_error_entry *err = | |
170 | - vio_lookup_rc(viodasd_err_table, we.sub_result); | |
171 | - | |
172 | - pr_warning("bad rc opening disk: %d:0x%04x (%s)\n", | |
173 | - (int)we.rc, we.sub_result, err->msg); | |
174 | - return -EIO; | |
175 | - } | |
176 | - | |
177 | - return 0; | |
178 | -} | |
179 | - | |
180 | -static int viodasd_unlocked_open(struct block_device *bdev, fmode_t mode) | |
181 | -{ | |
182 | - int ret; | |
183 | - | |
184 | - mutex_lock(&viodasd_mutex); | |
185 | - ret = viodasd_open(bdev, mode); | |
186 | - mutex_unlock(&viodasd_mutex); | |
187 | - | |
188 | - return ret; | |
189 | -} | |
190 | - | |
191 | - | |
192 | -/* | |
193 | - * External release entry point. | |
194 | - */ | |
195 | -static int viodasd_release(struct gendisk *disk, fmode_t mode) | |
196 | -{ | |
197 | - struct viodasd_device *d = disk->private_data; | |
198 | - HvLpEvent_Rc hvrc; | |
199 | - | |
200 | - mutex_lock(&viodasd_mutex); | |
201 | - /* Send the event to OS/400. We DON'T expect a response */ | |
202 | - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | |
203 | - HvLpEvent_Type_VirtualIo, | |
204 | - viomajorsubtype_blockio | vioblockclose, | |
205 | - HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck, | |
206 | - viopath_sourceinst(viopath_hostLp), | |
207 | - viopath_targetinst(viopath_hostLp), | |
208 | - 0, VIOVERSION << 16, | |
209 | - ((u64)DEVICE_NO(d) << 48) /* | ((u64)flags << 32) */, | |
210 | - 0, 0, 0); | |
211 | - if (hvrc != 0) | |
212 | - pr_warning("HV close call failed %d\n", (int)hvrc); | |
213 | - | |
214 | - mutex_unlock(&viodasd_mutex); | |
215 | - | |
216 | - return 0; | |
217 | -} | |
218 | - | |
219 | - | |
220 | -/* External ioctl entry point. | |
221 | - */ | |
222 | -static int viodasd_getgeo(struct block_device *bdev, struct hd_geometry *geo) | |
223 | -{ | |
224 | - struct gendisk *disk = bdev->bd_disk; | |
225 | - struct viodasd_device *d = disk->private_data; | |
226 | - | |
227 | - geo->sectors = d->sectors ? d->sectors : 32; | |
228 | - geo->heads = d->tracks ? d->tracks : 64; | |
229 | - geo->cylinders = d->cylinders ? d->cylinders : | |
230 | - get_capacity(disk) / (geo->sectors * geo->heads); | |
231 | - | |
232 | - return 0; | |
233 | -} | |
234 | - | |
235 | -/* | |
236 | - * Our file operations table | |
237 | - */ | |
238 | -static const struct block_device_operations viodasd_fops = { | |
239 | - .owner = THIS_MODULE, | |
240 | - .open = viodasd_unlocked_open, | |
241 | - .release = viodasd_release, | |
242 | - .getgeo = viodasd_getgeo, | |
243 | -}; | |
244 | - | |
245 | -/* | |
246 | - * End a request | |
247 | - */ | |
248 | -static void viodasd_end_request(struct request *req, int error, | |
249 | - int num_sectors) | |
250 | -{ | |
251 | - __blk_end_request(req, error, num_sectors << 9); | |
252 | -} | |
253 | - | |
254 | -/* | |
255 | - * Send an actual I/O request to OS/400 | |
256 | - */ | |
257 | -static int send_request(struct request *req) | |
258 | -{ | |
259 | - u64 start; | |
260 | - int direction; | |
261 | - int nsg; | |
262 | - u16 viocmd; | |
263 | - HvLpEvent_Rc hvrc; | |
264 | - struct vioblocklpevent *bevent; | |
265 | - struct HvLpEvent *hev; | |
266 | - struct scatterlist sg[VIOMAXBLOCKDMA]; | |
267 | - int sgindex; | |
268 | - struct viodasd_device *d; | |
269 | - unsigned long flags; | |
270 | - | |
271 | - start = (u64)blk_rq_pos(req) << 9; | |
272 | - | |
273 | - if (rq_data_dir(req) == READ) { | |
274 | - direction = DMA_FROM_DEVICE; | |
275 | - viocmd = viomajorsubtype_blockio | vioblockread; | |
276 | - } else { | |
277 | - direction = DMA_TO_DEVICE; | |
278 | - viocmd = viomajorsubtype_blockio | vioblockwrite; | |
279 | - } | |
280 | - | |
281 | - d = req->rq_disk->private_data; | |
282 | - | |
283 | - /* Now build the scatter-gather list */ | |
284 | - sg_init_table(sg, VIOMAXBLOCKDMA); | |
285 | - nsg = blk_rq_map_sg(req->q, req, sg); | |
286 | - nsg = dma_map_sg(d->dev, sg, nsg, direction); | |
287 | - | |
288 | - spin_lock_irqsave(&viodasd_spinlock, flags); | |
289 | - num_req_outstanding++; | |
290 | - | |
291 | - /* This optimization handles a single DMA block */ | |
292 | - if (nsg == 1) | |
293 | - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | |
294 | - HvLpEvent_Type_VirtualIo, viocmd, | |
295 | - HvLpEvent_AckInd_DoAck, | |
296 | - HvLpEvent_AckType_ImmediateAck, | |
297 | - viopath_sourceinst(viopath_hostLp), | |
298 | - viopath_targetinst(viopath_hostLp), | |
299 | - (u64)(unsigned long)req, VIOVERSION << 16, | |
300 | - ((u64)DEVICE_NO(d) << 48), start, | |
301 | - ((u64)sg_dma_address(&sg[0])) << 32, | |
302 | - sg_dma_len(&sg[0])); | |
303 | - else { | |
304 | - bevent = (struct vioblocklpevent *) | |
305 | - vio_get_event_buffer(viomajorsubtype_blockio); | |
306 | - if (bevent == NULL) { | |
307 | - pr_warning("error allocating disk event buffer\n"); | |
308 | - goto error_ret; | |
309 | - } | |
310 | - | |
311 | - /* | |
312 | - * Now build up the actual request. Note that we store | |
313 | - * the pointer to the request in the correlation | |
314 | - * token so we can match the response up later | |
315 | - */ | |
316 | - memset(bevent, 0, sizeof(struct vioblocklpevent)); | |
317 | - hev = &bevent->event; | |
318 | - hev->flags = HV_LP_EVENT_VALID | HV_LP_EVENT_DO_ACK | | |
319 | - HV_LP_EVENT_INT; | |
320 | - hev->xType = HvLpEvent_Type_VirtualIo; | |
321 | - hev->xSubtype = viocmd; | |
322 | - hev->xSourceLp = HvLpConfig_getLpIndex(); | |
323 | - hev->xTargetLp = viopath_hostLp; | |
324 | - hev->xSizeMinus1 = | |
325 | - offsetof(struct vioblocklpevent, u.rw_data.dma_info) + | |
326 | - (sizeof(bevent->u.rw_data.dma_info[0]) * nsg) - 1; | |
327 | - hev->xSourceInstanceId = viopath_sourceinst(viopath_hostLp); | |
328 | - hev->xTargetInstanceId = viopath_targetinst(viopath_hostLp); | |
329 | - hev->xCorrelationToken = (u64)req; | |
330 | - bevent->version = VIOVERSION; | |
331 | - bevent->disk = DEVICE_NO(d); | |
332 | - bevent->u.rw_data.offset = start; | |
333 | - | |
334 | - /* | |
335 | - * Copy just the dma information from the sg list | |
336 | - * into the request | |
337 | - */ | |
338 | - for (sgindex = 0; sgindex < nsg; sgindex++) { | |
339 | - bevent->u.rw_data.dma_info[sgindex].token = | |
340 | - sg_dma_address(&sg[sgindex]); | |
341 | - bevent->u.rw_data.dma_info[sgindex].len = | |
342 | - sg_dma_len(&sg[sgindex]); | |
343 | - } | |
344 | - | |
345 | - /* Send the request */ | |
346 | - hvrc = HvCallEvent_signalLpEvent(&bevent->event); | |
347 | - vio_free_event_buffer(viomajorsubtype_blockio, bevent); | |
348 | - } | |
349 | - | |
350 | - if (hvrc != HvLpEvent_Rc_Good) { | |
351 | - pr_warning("error sending disk event to OS/400 (rc %d)\n", | |
352 | - (int)hvrc); | |
353 | - goto error_ret; | |
354 | - } | |
355 | - spin_unlock_irqrestore(&viodasd_spinlock, flags); | |
356 | - return 0; | |
357 | - | |
358 | -error_ret: | |
359 | - num_req_outstanding--; | |
360 | - spin_unlock_irqrestore(&viodasd_spinlock, flags); | |
361 | - dma_unmap_sg(d->dev, sg, nsg, direction); | |
362 | - return -1; | |
363 | -} | |
364 | - | |
365 | -/* | |
366 | - * This is the external request processing routine | |
367 | - */ | |
368 | -static void do_viodasd_request(struct request_queue *q) | |
369 | -{ | |
370 | - struct request *req; | |
371 | - | |
372 | - /* | |
373 | - * If we already have the maximum number of requests | |
374 | - * outstanding to OS/400 just bail out. We'll come | |
375 | - * back later. | |
376 | - */ | |
377 | - while (num_req_outstanding < VIOMAXREQ) { | |
378 | - req = blk_fetch_request(q); | |
379 | - if (req == NULL) | |
380 | - return; | |
381 | - /* check that request contains a valid command */ | |
382 | - if (req->cmd_type != REQ_TYPE_FS) { | |
383 | - viodasd_end_request(req, -EIO, blk_rq_sectors(req)); | |
384 | - continue; | |
385 | - } | |
386 | - /* Try sending the request */ | |
387 | - if (send_request(req) != 0) | |
388 | - viodasd_end_request(req, -EIO, blk_rq_sectors(req)); | |
389 | - } | |
390 | -} | |
391 | - | |
392 | -/* | |
393 | - * Probe a single disk and fill in the viodasd_device structure | |
394 | - * for it. | |
395 | - */ | |
396 | -static int probe_disk(struct viodasd_device *d) | |
397 | -{ | |
398 | - HvLpEvent_Rc hvrc; | |
399 | - struct viodasd_waitevent we; | |
400 | - int dev_no = DEVICE_NO(d); | |
401 | - struct gendisk *g; | |
402 | - struct request_queue *q; | |
403 | - u16 flags = 0; | |
404 | - | |
405 | -retry: | |
406 | - init_completion(&we.com); | |
407 | - | |
408 | - /* Send the open event to OS/400 */ | |
409 | - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | |
410 | - HvLpEvent_Type_VirtualIo, | |
411 | - viomajorsubtype_blockio | vioblockopen, | |
412 | - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, | |
413 | - viopath_sourceinst(viopath_hostLp), | |
414 | - viopath_targetinst(viopath_hostLp), | |
415 | - (u64)(unsigned long)&we, VIOVERSION << 16, | |
416 | - ((u64)dev_no << 48) | ((u64)flags<< 32), | |
417 | - 0, 0, 0); | |
418 | - if (hvrc != 0) { | |
419 | - pr_warning("bad rc on HV open %d\n", (int)hvrc); | |
420 | - return 0; | |
421 | - } | |
422 | - | |
423 | - wait_for_completion(&we.com); | |
424 | - | |
425 | - if (we.rc != 0) { | |
426 | - if (flags != 0) | |
427 | - return 0; | |
428 | - /* try again with read only flag set */ | |
429 | - flags = vioblockflags_ro; | |
430 | - goto retry; | |
431 | - } | |
432 | - if (we.max_disk > (MAX_DISKNO - 1)) { | |
433 | - printk_once(KERN_INFO pr_fmt("Only examining the first %d of %d disks connected\n"), | |
434 | - MAX_DISKNO, we.max_disk + 1); | |
435 | - } | |
436 | - | |
437 | - /* Send the close event to OS/400. We DON'T expect a response */ | |
438 | - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | |
439 | - HvLpEvent_Type_VirtualIo, | |
440 | - viomajorsubtype_blockio | vioblockclose, | |
441 | - HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck, | |
442 | - viopath_sourceinst(viopath_hostLp), | |
443 | - viopath_targetinst(viopath_hostLp), | |
444 | - 0, VIOVERSION << 16, | |
445 | - ((u64)dev_no << 48) | ((u64)flags << 32), | |
446 | - 0, 0, 0); | |
447 | - if (hvrc != 0) { | |
448 | - pr_warning("bad rc sending event to OS/400 %d\n", (int)hvrc); | |
449 | - return 0; | |
450 | - } | |
451 | - | |
452 | - if (d->dev == NULL) { | |
453 | - /* this is when we reprobe for new disks */ | |
454 | - if (vio_create_viodasd(dev_no) == NULL) { | |
455 | - pr_warning("cannot allocate virtual device for disk %d\n", | |
456 | - dev_no); | |
457 | - return 0; | |
458 | - } | |
459 | - /* | |
460 | - * The vio_create_viodasd will have recursed into this | |
461 | - * routine with d->dev set to the new vio device and | |
462 | - * will finish the setup of the disk below. | |
463 | - */ | |
464 | - return 1; | |
465 | - } | |
466 | - | |
467 | - /* create the request queue for the disk */ | |
468 | - spin_lock_init(&d->q_lock); | |
469 | - q = blk_init_queue(do_viodasd_request, &d->q_lock); | |
470 | - if (q == NULL) { | |
471 | - pr_warning("cannot allocate queue for disk %d\n", dev_no); | |
472 | - return 0; | |
473 | - } | |
474 | - g = alloc_disk(1 << PARTITION_SHIFT); | |
475 | - if (g == NULL) { | |
476 | - pr_warning("cannot allocate disk structure for disk %d\n", | |
477 | - dev_no); | |
478 | - blk_cleanup_queue(q); | |
479 | - return 0; | |
480 | - } | |
481 | - | |
482 | - d->disk = g; | |
483 | - blk_queue_max_segments(q, VIOMAXBLOCKDMA); | |
484 | - blk_queue_max_hw_sectors(q, VIODASD_MAXSECTORS); | |
485 | - g->major = VIODASD_MAJOR; | |
486 | - g->first_minor = dev_no << PARTITION_SHIFT; | |
487 | - if (dev_no >= 26) | |
488 | - snprintf(g->disk_name, sizeof(g->disk_name), | |
489 | - VIOD_GENHD_NAME "%c%c", | |
490 | - 'a' + (dev_no / 26) - 1, 'a' + (dev_no % 26)); | |
491 | - else | |
492 | - snprintf(g->disk_name, sizeof(g->disk_name), | |
493 | - VIOD_GENHD_NAME "%c", 'a' + (dev_no % 26)); | |
494 | - g->fops = &viodasd_fops; | |
495 | - g->queue = q; | |
496 | - g->private_data = d; | |
497 | - g->driverfs_dev = d->dev; | |
498 | - set_capacity(g, d->size >> 9); | |
499 | - | |
500 | - pr_info("disk %d: %lu sectors (%lu MB) CHS=%d/%d/%d sector size %d%s\n", | |
501 | - dev_no, (unsigned long)(d->size >> 9), | |
502 | - (unsigned long)(d->size >> 20), | |
503 | - (int)d->cylinders, (int)d->tracks, | |
504 | - (int)d->sectors, (int)d->bytes_per_sector, | |
505 | - d->read_only ? " (RO)" : ""); | |
506 | - | |
507 | - /* register us in the global list */ | |
508 | - add_disk(g); | |
509 | - return 1; | |
510 | -} | |
511 | - | |
512 | -/* returns the total number of scatterlist elements converted */ | |
513 | -static int block_event_to_scatterlist(const struct vioblocklpevent *bevent, | |
514 | - struct scatterlist *sg, int *total_len) | |
515 | -{ | |
516 | - int i, numsg; | |
517 | - const struct rw_data *rw_data = &bevent->u.rw_data; | |
518 | - static const int offset = | |
519 | - offsetof(struct vioblocklpevent, u.rw_data.dma_info); | |
520 | - static const int element_size = sizeof(rw_data->dma_info[0]); | |
521 | - | |
522 | - numsg = ((bevent->event.xSizeMinus1 + 1) - offset) / element_size; | |
523 | - if (numsg > VIOMAXBLOCKDMA) | |
524 | - numsg = VIOMAXBLOCKDMA; | |
525 | - | |
526 | - *total_len = 0; | |
527 | - sg_init_table(sg, VIOMAXBLOCKDMA); | |
528 | - for (i = 0; (i < numsg) && (rw_data->dma_info[i].len > 0); ++i) { | |
529 | - sg_dma_address(&sg[i]) = rw_data->dma_info[i].token; | |
530 | - sg_dma_len(&sg[i]) = rw_data->dma_info[i].len; | |
531 | - *total_len += rw_data->dma_info[i].len; | |
532 | - } | |
533 | - return i; | |
534 | -} | |
535 | - | |
536 | -/* | |
537 | - * Restart all queues, starting with the one _after_ the disk given, | |
538 | - * thus reducing the chance of starvation of higher numbered disks. | |
539 | - */ | |
540 | -static void viodasd_restart_all_queues_starting_from(int first_index) | |
541 | -{ | |
542 | - int i; | |
543 | - | |
544 | - for (i = first_index + 1; i < MAX_DISKNO; ++i) | |
545 | - if (viodasd_devices[i].disk) | |
546 | - blk_run_queue(viodasd_devices[i].disk->queue); | |
547 | - for (i = 0; i <= first_index; ++i) | |
548 | - if (viodasd_devices[i].disk) | |
549 | - blk_run_queue(viodasd_devices[i].disk->queue); | |
550 | -} | |
551 | - | |
552 | -/* | |
553 | - * For read and write requests, decrement the number of outstanding requests, | |
554 | - * Free the DMA buffers we allocated. | |
555 | - */ | |
556 | -static int viodasd_handle_read_write(struct vioblocklpevent *bevent) | |
557 | -{ | |
558 | - int num_sg, num_sect, pci_direction, total_len; | |
559 | - struct request *req; | |
560 | - struct scatterlist sg[VIOMAXBLOCKDMA]; | |
561 | - struct HvLpEvent *event = &bevent->event; | |
562 | - unsigned long irq_flags; | |
563 | - struct viodasd_device *d; | |
564 | - int error; | |
565 | - spinlock_t *qlock; | |
566 | - | |
567 | - num_sg = block_event_to_scatterlist(bevent, sg, &total_len); | |
568 | - num_sect = total_len >> 9; | |
569 | - if (event->xSubtype == (viomajorsubtype_blockio | vioblockread)) | |
570 | - pci_direction = DMA_FROM_DEVICE; | |
571 | - else | |
572 | - pci_direction = DMA_TO_DEVICE; | |
573 | - req = (struct request *)bevent->event.xCorrelationToken; | |
574 | - d = req->rq_disk->private_data; | |
575 | - | |
576 | - dma_unmap_sg(d->dev, sg, num_sg, pci_direction); | |
577 | - | |
578 | - /* | |
579 | - * Since this is running in interrupt mode, we need to make sure | |
580 | - * we're not stepping on any global I/O operations | |
581 | - */ | |
582 | - spin_lock_irqsave(&viodasd_spinlock, irq_flags); | |
583 | - num_req_outstanding--; | |
584 | - spin_unlock_irqrestore(&viodasd_spinlock, irq_flags); | |
585 | - | |
586 | - error = (event->xRc == HvLpEvent_Rc_Good) ? 0 : -EIO; | |
587 | - if (error) { | |
588 | - const struct vio_error_entry *err; | |
589 | - err = vio_lookup_rc(viodasd_err_table, bevent->sub_result); | |
590 | - pr_warning("read/write error %d:0x%04x (%s)\n", | |
591 | - event->xRc, bevent->sub_result, err->msg); | |
592 | - num_sect = blk_rq_sectors(req); | |
593 | - } | |
594 | - qlock = req->q->queue_lock; | |
595 | - spin_lock_irqsave(qlock, irq_flags); | |
596 | - viodasd_end_request(req, error, num_sect); | |
597 | - spin_unlock_irqrestore(qlock, irq_flags); | |
598 | - | |
599 | - /* Finally, try to get more requests off of this device's queue */ | |
600 | - viodasd_restart_all_queues_starting_from(DEVICE_NO(d)); | |
601 | - | |
602 | - return 0; | |
603 | -} | |
604 | - | |
605 | -/* This routine handles incoming block LP events */ | |
606 | -static void handle_block_event(struct HvLpEvent *event) | |
607 | -{ | |
608 | - struct vioblocklpevent *bevent = (struct vioblocklpevent *)event; | |
609 | - struct viodasd_waitevent *pwe; | |
610 | - | |
611 | - if (event == NULL) | |
612 | - /* Notification that a partition went away! */ | |
613 | - return; | |
614 | - /* First, we should NEVER get an int here...only acks */ | |
615 | - if (hvlpevent_is_int(event)) { | |
616 | - pr_warning("Yikes! got an int in viodasd event handler!\n"); | |
617 | - if (hvlpevent_need_ack(event)) { | |
618 | - event->xRc = HvLpEvent_Rc_InvalidSubtype; | |
619 | - HvCallEvent_ackLpEvent(event); | |
620 | - } | |
621 | - } | |
622 | - | |
623 | - switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) { | |
624 | - case vioblockopen: | |
625 | - /* | |
626 | - * Handle a response to an open request. We get all the | |
627 | - * disk information in the response, so update it. The | |
628 | - * correlation token contains a pointer to a waitevent | |
629 | - * structure that has a completion in it. update the | |
630 | - * return code in the waitevent structure and post the | |
631 | - * completion to wake up the guy who sent the request | |
632 | - */ | |
633 | - pwe = (struct viodasd_waitevent *)event->xCorrelationToken; | |
634 | - pwe->rc = event->xRc; | |
635 | - pwe->sub_result = bevent->sub_result; | |
636 | - if (event->xRc == HvLpEvent_Rc_Good) { | |
637 | - const struct open_data *data = &bevent->u.open_data; | |
638 | - struct viodasd_device *device = | |
639 | - &viodasd_devices[bevent->disk]; | |
640 | - device->read_only = | |
641 | - bevent->flags & vioblockflags_ro; | |
642 | - device->size = data->disk_size; | |
643 | - device->cylinders = data->cylinders; | |
644 | - device->tracks = data->tracks; | |
645 | - device->sectors = data->sectors; | |
646 | - device->bytes_per_sector = data->bytes_per_sector; | |
647 | - pwe->max_disk = data->max_disk; | |
648 | - } | |
649 | - complete(&pwe->com); | |
650 | - break; | |
651 | - case vioblockclose: | |
652 | - break; | |
653 | - case vioblockread: | |
654 | - case vioblockwrite: | |
655 | - viodasd_handle_read_write(bevent); | |
656 | - break; | |
657 | - | |
658 | - default: | |
659 | - pr_warning("invalid subtype!"); | |
660 | - if (hvlpevent_need_ack(event)) { | |
661 | - event->xRc = HvLpEvent_Rc_InvalidSubtype; | |
662 | - HvCallEvent_ackLpEvent(event); | |
663 | - } | |
664 | - } | |
665 | -} | |
666 | - | |
667 | -/* | |
668 | - * Get the driver to reprobe for more disks. | |
669 | - */ | |
670 | -static ssize_t probe_disks(struct device_driver *drv, const char *buf, | |
671 | - size_t count) | |
672 | -{ | |
673 | - struct viodasd_device *d; | |
674 | - | |
675 | - for (d = viodasd_devices; d < &viodasd_devices[MAX_DISKNO]; d++) { | |
676 | - if (d->disk == NULL) | |
677 | - probe_disk(d); | |
678 | - } | |
679 | - return count; | |
680 | -} | |
681 | -static DRIVER_ATTR(probe, S_IWUSR, NULL, probe_disks); | |
682 | - | |
683 | -static int viodasd_probe(struct vio_dev *vdev, const struct vio_device_id *id) | |
684 | -{ | |
685 | - struct viodasd_device *d = &viodasd_devices[vdev->unit_address]; | |
686 | - | |
687 | - d->dev = &vdev->dev; | |
688 | - if (!probe_disk(d)) | |
689 | - return -ENODEV; | |
690 | - return 0; | |
691 | -} | |
692 | - | |
693 | -static int viodasd_remove(struct vio_dev *vdev) | |
694 | -{ | |
695 | - struct viodasd_device *d; | |
696 | - | |
697 | - d = &viodasd_devices[vdev->unit_address]; | |
698 | - if (d->disk) { | |
699 | - del_gendisk(d->disk); | |
700 | - blk_cleanup_queue(d->disk->queue); | |
701 | - put_disk(d->disk); | |
702 | - d->disk = NULL; | |
703 | - } | |
704 | - d->dev = NULL; | |
705 | - return 0; | |
706 | -} | |
707 | - | |
708 | -/** | |
709 | - * viodasd_device_table: Used by vio.c to match devices that we | |
710 | - * support. | |
711 | - */ | |
712 | -static struct vio_device_id viodasd_device_table[] __devinitdata = { | |
713 | - { "block", "IBM,iSeries-viodasd" }, | |
714 | - { "", "" } | |
715 | -}; | |
716 | -MODULE_DEVICE_TABLE(vio, viodasd_device_table); | |
717 | - | |
718 | -static struct vio_driver viodasd_driver = { | |
719 | - .id_table = viodasd_device_table, | |
720 | - .probe = viodasd_probe, | |
721 | - .remove = viodasd_remove, | |
722 | - .driver = { | |
723 | - .name = "viodasd", | |
724 | - .owner = THIS_MODULE, | |
725 | - } | |
726 | -}; | |
727 | - | |
728 | -static int need_delete_probe; | |
729 | - | |
730 | -/* | |
731 | - * Initialize the whole device driver. Handle module and non-module | |
732 | - * versions | |
733 | - */ | |
734 | -static int __init viodasd_init(void) | |
735 | -{ | |
736 | - int rc; | |
737 | - | |
738 | - if (!firmware_has_feature(FW_FEATURE_ISERIES)) { | |
739 | - rc = -ENODEV; | |
740 | - goto early_fail; | |
741 | - } | |
742 | - | |
743 | - /* Try to open to our host lp */ | |
744 | - if (viopath_hostLp == HvLpIndexInvalid) | |
745 | - vio_set_hostlp(); | |
746 | - | |
747 | - if (viopath_hostLp == HvLpIndexInvalid) { | |
748 | - pr_warning("invalid hosting partition\n"); | |
749 | - rc = -EIO; | |
750 | - goto early_fail; | |
751 | - } | |
752 | - | |
753 | - pr_info("vers " VIOD_VERS ", hosting partition %d\n", viopath_hostLp); | |
754 | - | |
755 | - /* register the block device */ | |
756 | - rc = register_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME); | |
757 | - if (rc) { | |
758 | - pr_warning("Unable to get major number %d for %s\n", | |
759 | - VIODASD_MAJOR, VIOD_GENHD_NAME); | |
760 | - goto early_fail; | |
761 | - } | |
762 | - /* Actually open the path to the hosting partition */ | |
763 | - rc = viopath_open(viopath_hostLp, viomajorsubtype_blockio, | |
764 | - VIOMAXREQ + 2); | |
765 | - if (rc) { | |
766 | - pr_warning("error opening path to host partition %d\n", | |
767 | - viopath_hostLp); | |
768 | - goto unregister_blk; | |
769 | - } | |
770 | - | |
771 | - /* Initialize our request handler */ | |
772 | - vio_setHandler(viomajorsubtype_blockio, handle_block_event); | |
773 | - | |
774 | - rc = vio_register_driver(&viodasd_driver); | |
775 | - if (rc) { | |
776 | - pr_warning("vio_register_driver failed\n"); | |
777 | - goto unset_handler; | |
778 | - } | |
779 | - | |
780 | - /* | |
781 | - * If this call fails, it just means that we cannot dynamically | |
782 | - * add virtual disks, but the driver will still work fine for | |
783 | - * all existing disk, so ignore the failure. | |
784 | - */ | |
785 | - if (!driver_create_file(&viodasd_driver.driver, &driver_attr_probe)) | |
786 | - need_delete_probe = 1; | |
787 | - | |
788 | - return 0; | |
789 | - | |
790 | -unset_handler: | |
791 | - vio_clearHandler(viomajorsubtype_blockio); | |
792 | - viopath_close(viopath_hostLp, viomajorsubtype_blockio, VIOMAXREQ + 2); | |
793 | -unregister_blk: | |
794 | - unregister_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME); | |
795 | -early_fail: | |
796 | - return rc; | |
797 | -} | |
798 | -module_init(viodasd_init); | |
799 | - | |
800 | -void __exit viodasd_exit(void) | |
801 | -{ | |
802 | - if (need_delete_probe) | |
803 | - driver_remove_file(&viodasd_driver.driver, &driver_attr_probe); | |
804 | - vio_unregister_driver(&viodasd_driver); | |
805 | - vio_clearHandler(viomajorsubtype_blockio); | |
806 | - viopath_close(viopath_hostLp, viomajorsubtype_blockio, VIOMAXREQ + 2); | |
807 | - unregister_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME); | |
808 | -} | |
809 | -module_exit(viodasd_exit); |
drivers/cdrom/viocd.c
1 | -/* -*- linux-c -*- | |
2 | - * drivers/cdrom/viocd.c | |
3 | - * | |
4 | - * iSeries Virtual CD Rom | |
5 | - * | |
6 | - * Authors: Dave Boutcher <boutcher@us.ibm.com> | |
7 | - * Ryan Arnold <ryanarn@us.ibm.com> | |
8 | - * Colin Devilbiss <devilbis@us.ibm.com> | |
9 | - * Stephen Rothwell | |
10 | - * | |
11 | - * (C) Copyright 2000-2004 IBM Corporation | |
12 | - * | |
13 | - * This program is free software; you can redistribute it and/or | |
14 | - * modify it under the terms of the GNU General Public License as | |
15 | - * published by the Free Software Foundation; either version 2 of the | |
16 | - * License, or (at your option) anyu later version. | |
17 | - * | |
18 | - * This program is distributed in the hope that it will be useful, but | |
19 | - * WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
21 | - * General Public License for more details. | |
22 | - * | |
23 | - * You should have received a copy of the GNU General Public License | |
24 | - * along with this program; if not, write to the Free Software Foundation, | |
25 | - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
26 | - * | |
27 | - * This routine provides access to CD ROM drives owned and managed by an | |
28 | - * OS/400 partition running on the same box as this Linux partition. | |
29 | - * | |
30 | - * All operations are performed by sending messages back and forth to | |
31 | - * the OS/400 partition. | |
32 | - */ | |
33 | - | |
34 | -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
35 | - | |
36 | -#include <linux/major.h> | |
37 | -#include <linux/blkdev.h> | |
38 | -#include <linux/cdrom.h> | |
39 | -#include <linux/errno.h> | |
40 | -#include <linux/init.h> | |
41 | -#include <linux/dma-mapping.h> | |
42 | -#include <linux/module.h> | |
43 | -#include <linux/completion.h> | |
44 | -#include <linux/proc_fs.h> | |
45 | -#include <linux/mutex.h> | |
46 | -#include <linux/seq_file.h> | |
47 | -#include <linux/scatterlist.h> | |
48 | - | |
49 | -#include <asm/vio.h> | |
50 | -#include <asm/iseries/hv_types.h> | |
51 | -#include <asm/iseries/hv_lp_event.h> | |
52 | -#include <asm/iseries/vio.h> | |
53 | -#include <asm/firmware.h> | |
54 | - | |
55 | -#define VIOCD_DEVICE "iseries/vcd" | |
56 | - | |
57 | -#define VIOCD_VERS "1.06" | |
58 | - | |
59 | -/* | |
60 | - * Should probably make this a module parameter....sigh | |
61 | - */ | |
62 | -#define VIOCD_MAX_CD HVMAXARCHITECTEDVIRTUALCDROMS | |
63 | - | |
64 | -static DEFINE_MUTEX(viocd_mutex); | |
65 | -static const struct vio_error_entry viocd_err_table[] = { | |
66 | - {0x0201, EINVAL, "Invalid Range"}, | |
67 | - {0x0202, EINVAL, "Invalid Token"}, | |
68 | - {0x0203, EIO, "DMA Error"}, | |
69 | - {0x0204, EIO, "Use Error"}, | |
70 | - {0x0205, EIO, "Release Error"}, | |
71 | - {0x0206, EINVAL, "Invalid CD"}, | |
72 | - {0x020C, EROFS, "Read Only Device"}, | |
73 | - {0x020D, ENOMEDIUM, "Changed or Missing Volume (or Varied Off?)"}, | |
74 | - {0x020E, EIO, "Optical System Error (Varied Off?)"}, | |
75 | - {0x02FF, EIO, "Internal Error"}, | |
76 | - {0x3010, EIO, "Changed Volume"}, | |
77 | - {0xC100, EIO, "Optical System Error"}, | |
78 | - {0x0000, 0, NULL}, | |
79 | -}; | |
80 | - | |
81 | -/* | |
82 | - * This is the structure we use to exchange info between driver and interrupt | |
83 | - * handler | |
84 | - */ | |
85 | -struct viocd_waitevent { | |
86 | - struct completion com; | |
87 | - int rc; | |
88 | - u16 sub_result; | |
89 | - int changed; | |
90 | -}; | |
91 | - | |
92 | -/* this is a lookup table for the true capabilities of a device */ | |
93 | -struct capability_entry { | |
94 | - char *type; | |
95 | - int capability; | |
96 | -}; | |
97 | - | |
98 | -static struct capability_entry capability_table[] __initdata = { | |
99 | - { "6330", CDC_LOCK | CDC_DVD_RAM | CDC_RAM }, | |
100 | - { "6331", CDC_LOCK | CDC_DVD_RAM | CDC_RAM }, | |
101 | - { "6333", CDC_LOCK | CDC_DVD_RAM | CDC_RAM }, | |
102 | - { "632A", CDC_LOCK | CDC_DVD_RAM | CDC_RAM }, | |
103 | - { "6321", CDC_LOCK }, | |
104 | - { "632B", 0 }, | |
105 | - { NULL , CDC_LOCK }, | |
106 | -}; | |
107 | - | |
108 | -/* These are our internal structures for keeping track of devices */ | |
109 | -static int viocd_numdev; | |
110 | - | |
111 | -struct disk_info { | |
112 | - struct gendisk *viocd_disk; | |
113 | - struct cdrom_device_info viocd_info; | |
114 | - struct device *dev; | |
115 | - const char *rsrcname; | |
116 | - const char *type; | |
117 | - const char *model; | |
118 | -}; | |
119 | -static struct disk_info viocd_diskinfo[VIOCD_MAX_CD]; | |
120 | - | |
121 | -#define DEVICE_NR(di) ((di) - &viocd_diskinfo[0]) | |
122 | - | |
123 | -static spinlock_t viocd_reqlock; | |
124 | - | |
125 | -#define MAX_CD_REQ 1 | |
126 | - | |
127 | -/* procfs support */ | |
128 | -static int proc_viocd_show(struct seq_file *m, void *v) | |
129 | -{ | |
130 | - int i; | |
131 | - | |
132 | - for (i = 0; i < viocd_numdev; i++) { | |
133 | - seq_printf(m, "viocd device %d is iSeries resource %10.10s" | |
134 | - "type %4.4s, model %3.3s\n", | |
135 | - i, viocd_diskinfo[i].rsrcname, | |
136 | - viocd_diskinfo[i].type, | |
137 | - viocd_diskinfo[i].model); | |
138 | - } | |
139 | - return 0; | |
140 | -} | |
141 | - | |
142 | -static int proc_viocd_open(struct inode *inode, struct file *file) | |
143 | -{ | |
144 | - return single_open(file, proc_viocd_show, NULL); | |
145 | -} | |
146 | - | |
147 | -static const struct file_operations proc_viocd_operations = { | |
148 | - .owner = THIS_MODULE, | |
149 | - .open = proc_viocd_open, | |
150 | - .read = seq_read, | |
151 | - .llseek = seq_lseek, | |
152 | - .release = single_release, | |
153 | -}; | |
154 | - | |
155 | -static int viocd_blk_open(struct block_device *bdev, fmode_t mode) | |
156 | -{ | |
157 | - struct disk_info *di = bdev->bd_disk->private_data; | |
158 | - int ret; | |
159 | - | |
160 | - mutex_lock(&viocd_mutex); | |
161 | - ret = cdrom_open(&di->viocd_info, bdev, mode); | |
162 | - mutex_unlock(&viocd_mutex); | |
163 | - | |
164 | - return ret; | |
165 | -} | |
166 | - | |
167 | -static int viocd_blk_release(struct gendisk *disk, fmode_t mode) | |
168 | -{ | |
169 | - struct disk_info *di = disk->private_data; | |
170 | - mutex_lock(&viocd_mutex); | |
171 | - cdrom_release(&di->viocd_info, mode); | |
172 | - mutex_unlock(&viocd_mutex); | |
173 | - return 0; | |
174 | -} | |
175 | - | |
176 | -static int viocd_blk_ioctl(struct block_device *bdev, fmode_t mode, | |
177 | - unsigned cmd, unsigned long arg) | |
178 | -{ | |
179 | - struct disk_info *di = bdev->bd_disk->private_data; | |
180 | - int ret; | |
181 | - | |
182 | - mutex_lock(&viocd_mutex); | |
183 | - ret = cdrom_ioctl(&di->viocd_info, bdev, mode, cmd, arg); | |
184 | - mutex_unlock(&viocd_mutex); | |
185 | - | |
186 | - return ret; | |
187 | -} | |
188 | - | |
189 | -static unsigned int viocd_blk_check_events(struct gendisk *disk, | |
190 | - unsigned int clearing) | |
191 | -{ | |
192 | - struct disk_info *di = disk->private_data; | |
193 | - return cdrom_check_events(&di->viocd_info, clearing); | |
194 | -} | |
195 | - | |
196 | -static const struct block_device_operations viocd_fops = { | |
197 | - .owner = THIS_MODULE, | |
198 | - .open = viocd_blk_open, | |
199 | - .release = viocd_blk_release, | |
200 | - .ioctl = viocd_blk_ioctl, | |
201 | - .check_events = viocd_blk_check_events, | |
202 | -}; | |
203 | - | |
204 | -static int viocd_open(struct cdrom_device_info *cdi, int purpose) | |
205 | -{ | |
206 | - struct disk_info *diskinfo = cdi->handle; | |
207 | - int device_no = DEVICE_NR(diskinfo); | |
208 | - HvLpEvent_Rc hvrc; | |
209 | - struct viocd_waitevent we; | |
210 | - | |
211 | - init_completion(&we.com); | |
212 | - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | |
213 | - HvLpEvent_Type_VirtualIo, | |
214 | - viomajorsubtype_cdio | viocdopen, | |
215 | - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, | |
216 | - viopath_sourceinst(viopath_hostLp), | |
217 | - viopath_targetinst(viopath_hostLp), | |
218 | - (u64)&we, VIOVERSION << 16, ((u64)device_no << 48), | |
219 | - 0, 0, 0); | |
220 | - if (hvrc != 0) { | |
221 | - pr_warning("bad rc on HvCallEvent_signalLpEventFast %d\n", | |
222 | - (int)hvrc); | |
223 | - return -EIO; | |
224 | - } | |
225 | - | |
226 | - wait_for_completion(&we.com); | |
227 | - | |
228 | - if (we.rc) { | |
229 | - const struct vio_error_entry *err = | |
230 | - vio_lookup_rc(viocd_err_table, we.sub_result); | |
231 | - pr_warning("bad rc %d:0x%04X on open: %s\n", | |
232 | - we.rc, we.sub_result, err->msg); | |
233 | - return -err->errno; | |
234 | - } | |
235 | - | |
236 | - return 0; | |
237 | -} | |
238 | - | |
239 | -static void viocd_release(struct cdrom_device_info *cdi) | |
240 | -{ | |
241 | - int device_no = DEVICE_NR((struct disk_info *)cdi->handle); | |
242 | - HvLpEvent_Rc hvrc; | |
243 | - | |
244 | - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | |
245 | - HvLpEvent_Type_VirtualIo, | |
246 | - viomajorsubtype_cdio | viocdclose, | |
247 | - HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck, | |
248 | - viopath_sourceinst(viopath_hostLp), | |
249 | - viopath_targetinst(viopath_hostLp), 0, | |
250 | - VIOVERSION << 16, ((u64)device_no << 48), 0, 0, 0); | |
251 | - if (hvrc != 0) | |
252 | - pr_warning("bad rc on HvCallEvent_signalLpEventFast %d\n", | |
253 | - (int)hvrc); | |
254 | -} | |
255 | - | |
256 | -/* Send a read or write request to OS/400 */ | |
257 | -static int send_request(struct request *req) | |
258 | -{ | |
259 | - HvLpEvent_Rc hvrc; | |
260 | - struct disk_info *diskinfo = req->rq_disk->private_data; | |
261 | - u64 len; | |
262 | - dma_addr_t dmaaddr; | |
263 | - int direction; | |
264 | - u16 cmd; | |
265 | - struct scatterlist sg; | |
266 | - | |
267 | - BUG_ON(req->nr_phys_segments > 1); | |
268 | - | |
269 | - if (rq_data_dir(req) == READ) { | |
270 | - direction = DMA_FROM_DEVICE; | |
271 | - cmd = viomajorsubtype_cdio | viocdread; | |
272 | - } else { | |
273 | - direction = DMA_TO_DEVICE; | |
274 | - cmd = viomajorsubtype_cdio | viocdwrite; | |
275 | - } | |
276 | - | |
277 | - sg_init_table(&sg, 1); | |
278 | - if (blk_rq_map_sg(req->q, req, &sg) == 0) { | |
279 | - pr_warning("error setting up scatter/gather list\n"); | |
280 | - return -1; | |
281 | - } | |
282 | - | |
283 | - if (dma_map_sg(diskinfo->dev, &sg, 1, direction) == 0) { | |
284 | - pr_warning("error allocating sg tce\n"); | |
285 | - return -1; | |
286 | - } | |
287 | - dmaaddr = sg_dma_address(&sg); | |
288 | - len = sg_dma_len(&sg); | |
289 | - | |
290 | - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | |
291 | - HvLpEvent_Type_VirtualIo, cmd, | |
292 | - HvLpEvent_AckInd_DoAck, | |
293 | - HvLpEvent_AckType_ImmediateAck, | |
294 | - viopath_sourceinst(viopath_hostLp), | |
295 | - viopath_targetinst(viopath_hostLp), | |
296 | - (u64)req, VIOVERSION << 16, | |
297 | - ((u64)DEVICE_NR(diskinfo) << 48) | dmaaddr, | |
298 | - (u64)blk_rq_pos(req) * 512, len, 0); | |
299 | - if (hvrc != HvLpEvent_Rc_Good) { | |
300 | - pr_warning("hv error on op %d\n", (int)hvrc); | |
301 | - return -1; | |
302 | - } | |
303 | - | |
304 | - return 0; | |
305 | -} | |
306 | - | |
307 | -static int rwreq; | |
308 | - | |
309 | -static void do_viocd_request(struct request_queue *q) | |
310 | -{ | |
311 | - struct request *req; | |
312 | - | |
313 | - while ((rwreq == 0) && ((req = blk_fetch_request(q)) != NULL)) { | |
314 | - if (req->cmd_type != REQ_TYPE_FS) | |
315 | - __blk_end_request_all(req, -EIO); | |
316 | - else if (send_request(req) < 0) { | |
317 | - pr_warning("unable to send message to OS/400!\n"); | |
318 | - __blk_end_request_all(req, -EIO); | |
319 | - } else | |
320 | - rwreq++; | |
321 | - } | |
322 | -} | |
323 | - | |
324 | -static unsigned int viocd_check_events(struct cdrom_device_info *cdi, | |
325 | - unsigned int clearing, int disc_nr) | |
326 | -{ | |
327 | - struct viocd_waitevent we; | |
328 | - HvLpEvent_Rc hvrc; | |
329 | - int device_no = DEVICE_NR((struct disk_info *)cdi->handle); | |
330 | - | |
331 | - init_completion(&we.com); | |
332 | - | |
333 | - /* Send the open event to OS/400 */ | |
334 | - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | |
335 | - HvLpEvent_Type_VirtualIo, | |
336 | - viomajorsubtype_cdio | viocdcheck, | |
337 | - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, | |
338 | - viopath_sourceinst(viopath_hostLp), | |
339 | - viopath_targetinst(viopath_hostLp), | |
340 | - (u64)&we, VIOVERSION << 16, ((u64)device_no << 48), | |
341 | - 0, 0, 0); | |
342 | - if (hvrc != 0) { | |
343 | - pr_warning("bad rc on HvCallEvent_signalLpEventFast %d\n", | |
344 | - (int)hvrc); | |
345 | - return 0; | |
346 | - } | |
347 | - | |
348 | - wait_for_completion(&we.com); | |
349 | - | |
350 | - /* Check the return code. If bad, assume no change */ | |
351 | - if (we.rc) { | |
352 | - const struct vio_error_entry *err = | |
353 | - vio_lookup_rc(viocd_err_table, we.sub_result); | |
354 | - pr_warning("bad rc %d:0x%04X on check_change: %s; Assuming no change\n", | |
355 | - we.rc, we.sub_result, err->msg); | |
356 | - return 0; | |
357 | - } | |
358 | - | |
359 | - return we.changed ? DISK_EVENT_MEDIA_CHANGE : 0; | |
360 | -} | |
361 | - | |
362 | -static int viocd_lock_door(struct cdrom_device_info *cdi, int locking) | |
363 | -{ | |
364 | - HvLpEvent_Rc hvrc; | |
365 | - u64 device_no = DEVICE_NR((struct disk_info *)cdi->handle); | |
366 | - /* NOTE: flags is 1 or 0 so it won't overwrite the device_no */ | |
367 | - u64 flags = !!locking; | |
368 | - struct viocd_waitevent we; | |
369 | - | |
370 | - init_completion(&we.com); | |
371 | - | |
372 | - /* Send the lockdoor event to OS/400 */ | |
373 | - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | |
374 | - HvLpEvent_Type_VirtualIo, | |
375 | - viomajorsubtype_cdio | viocdlockdoor, | |
376 | - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, | |
377 | - viopath_sourceinst(viopath_hostLp), | |
378 | - viopath_targetinst(viopath_hostLp), | |
379 | - (u64)&we, VIOVERSION << 16, | |
380 | - (device_no << 48) | (flags << 32), 0, 0, 0); | |
381 | - if (hvrc != 0) { | |
382 | - pr_warning("bad rc on HvCallEvent_signalLpEventFast %d\n", | |
383 | - (int)hvrc); | |
384 | - return -EIO; | |
385 | - } | |
386 | - | |
387 | - wait_for_completion(&we.com); | |
388 | - | |
389 | - if (we.rc != 0) | |
390 | - return -EIO; | |
391 | - return 0; | |
392 | -} | |
393 | - | |
394 | -static int viocd_packet(struct cdrom_device_info *cdi, | |
395 | - struct packet_command *cgc) | |
396 | -{ | |
397 | - unsigned int buflen = cgc->buflen; | |
398 | - int ret = -EIO; | |
399 | - | |
400 | - switch (cgc->cmd[0]) { | |
401 | - case GPCMD_READ_DISC_INFO: | |
402 | - { | |
403 | - disc_information *di = (disc_information *)cgc->buffer; | |
404 | - | |
405 | - if (buflen >= 2) { | |
406 | - di->disc_information_length = cpu_to_be16(1); | |
407 | - ret = 0; | |
408 | - } | |
409 | - if (buflen >= 3) | |
410 | - di->erasable = | |
411 | - (cdi->ops->capability & ~cdi->mask | |
412 | - & (CDC_DVD_RAM | CDC_RAM)) != 0; | |
413 | - } | |
414 | - break; | |
415 | - case GPCMD_GET_CONFIGURATION: | |
416 | - if (cgc->cmd[3] == CDF_RWRT) { | |
417 | - struct rwrt_feature_desc *rfd = (struct rwrt_feature_desc *)(cgc->buffer + sizeof(struct feature_header)); | |
418 | - | |
419 | - if ((buflen >= | |
420 | - (sizeof(struct feature_header) + sizeof(*rfd))) && | |
421 | - (cdi->ops->capability & ~cdi->mask | |
422 | - & (CDC_DVD_RAM | CDC_RAM))) { | |
423 | - rfd->feature_code = cpu_to_be16(CDF_RWRT); | |
424 | - rfd->curr = 1; | |
425 | - ret = 0; | |
426 | - } | |
427 | - } | |
428 | - break; | |
429 | - default: | |
430 | - if (cgc->sense) { | |
431 | - /* indicate Unknown code */ | |
432 | - cgc->sense->sense_key = 0x05; | |
433 | - cgc->sense->asc = 0x20; | |
434 | - cgc->sense->ascq = 0x00; | |
435 | - } | |
436 | - break; | |
437 | - } | |
438 | - | |
439 | - cgc->stat = ret; | |
440 | - return ret; | |
441 | -} | |
442 | - | |
443 | -static void restart_all_queues(int first_index) | |
444 | -{ | |
445 | - int i; | |
446 | - | |
447 | - for (i = first_index + 1; i < viocd_numdev; i++) | |
448 | - if (viocd_diskinfo[i].viocd_disk) | |
449 | - blk_run_queue(viocd_diskinfo[i].viocd_disk->queue); | |
450 | - for (i = 0; i <= first_index; i++) | |
451 | - if (viocd_diskinfo[i].viocd_disk) | |
452 | - blk_run_queue(viocd_diskinfo[i].viocd_disk->queue); | |
453 | -} | |
454 | - | |
455 | -/* This routine handles incoming CD LP events */ | |
456 | -static void vio_handle_cd_event(struct HvLpEvent *event) | |
457 | -{ | |
458 | - struct viocdlpevent *bevent; | |
459 | - struct viocd_waitevent *pwe; | |
460 | - struct disk_info *di; | |
461 | - unsigned long flags; | |
462 | - struct request *req; | |
463 | - | |
464 | - | |
465 | - if (event == NULL) | |
466 | - /* Notification that a partition went away! */ | |
467 | - return; | |
468 | - /* First, we should NEVER get an int here...only acks */ | |
469 | - if (hvlpevent_is_int(event)) { | |
470 | - pr_warning("Yikes! got an int in viocd event handler!\n"); | |
471 | - if (hvlpevent_need_ack(event)) { | |
472 | - event->xRc = HvLpEvent_Rc_InvalidSubtype; | |
473 | - HvCallEvent_ackLpEvent(event); | |
474 | - } | |
475 | - } | |
476 | - | |
477 | - bevent = (struct viocdlpevent *)event; | |
478 | - | |
479 | - switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) { | |
480 | - case viocdopen: | |
481 | - if (event->xRc == 0) { | |
482 | - di = &viocd_diskinfo[bevent->disk]; | |
483 | - blk_queue_logical_block_size(di->viocd_disk->queue, | |
484 | - bevent->block_size); | |
485 | - set_capacity(di->viocd_disk, | |
486 | - bevent->media_size * | |
487 | - bevent->block_size / 512); | |
488 | - } | |
489 | - /* FALLTHROUGH !! */ | |
490 | - case viocdlockdoor: | |
491 | - pwe = (struct viocd_waitevent *)event->xCorrelationToken; | |
492 | -return_complete: | |
493 | - pwe->rc = event->xRc; | |
494 | - pwe->sub_result = bevent->sub_result; | |
495 | - complete(&pwe->com); | |
496 | - break; | |
497 | - | |
498 | - case viocdcheck: | |
499 | - pwe = (struct viocd_waitevent *)event->xCorrelationToken; | |
500 | - pwe->changed = bevent->flags; | |
501 | - goto return_complete; | |
502 | - | |
503 | - case viocdclose: | |
504 | - break; | |
505 | - | |
506 | - case viocdwrite: | |
507 | - case viocdread: | |
508 | - /* | |
509 | - * Since this is running in interrupt mode, we need to | |
510 | - * make sure we're not stepping on any global I/O operations | |
511 | - */ | |
512 | - di = &viocd_diskinfo[bevent->disk]; | |
513 | - spin_lock_irqsave(&viocd_reqlock, flags); | |
514 | - dma_unmap_single(di->dev, bevent->token, bevent->len, | |
515 | - ((event->xSubtype & VIOMINOR_SUBTYPE_MASK) == viocdread) | |
516 | - ? DMA_FROM_DEVICE : DMA_TO_DEVICE); | |
517 | - req = (struct request *)bevent->event.xCorrelationToken; | |
518 | - rwreq--; | |
519 | - | |
520 | - if (event->xRc != HvLpEvent_Rc_Good) { | |
521 | - const struct vio_error_entry *err = | |
522 | - vio_lookup_rc(viocd_err_table, | |
523 | - bevent->sub_result); | |
524 | - pr_warning("request %p failed with rc %d:0x%04X: %s\n", | |
525 | - req, event->xRc, | |
526 | - bevent->sub_result, err->msg); | |
527 | - __blk_end_request_all(req, -EIO); | |
528 | - } else | |
529 | - __blk_end_request_all(req, 0); | |
530 | - | |
531 | - /* restart handling of incoming requests */ | |
532 | - spin_unlock_irqrestore(&viocd_reqlock, flags); | |
533 | - restart_all_queues(bevent->disk); | |
534 | - break; | |
535 | - | |
536 | - default: | |
537 | - pr_warning("message with invalid subtype %0x04X!\n", | |
538 | - event->xSubtype & VIOMINOR_SUBTYPE_MASK); | |
539 | - if (hvlpevent_need_ack(event)) { | |
540 | - event->xRc = HvLpEvent_Rc_InvalidSubtype; | |
541 | - HvCallEvent_ackLpEvent(event); | |
542 | - } | |
543 | - } | |
544 | -} | |
545 | - | |
546 | -static int viocd_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, | |
547 | - void *arg) | |
548 | -{ | |
549 | - return -EINVAL; | |
550 | -} | |
551 | - | |
552 | -static struct cdrom_device_ops viocd_dops = { | |
553 | - .open = viocd_open, | |
554 | - .release = viocd_release, | |
555 | - .check_events = viocd_check_events, | |
556 | - .lock_door = viocd_lock_door, | |
557 | - .generic_packet = viocd_packet, | |
558 | - .audio_ioctl = viocd_audio_ioctl, | |
559 | - .capability = CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_SPEED | CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET | CDC_DRIVE_STATUS | CDC_GENERIC_PACKET | CDC_CD_R | CDC_CD_RW | CDC_DVD | CDC_DVD_R | CDC_DVD_RAM | CDC_RAM | |
560 | -}; | |
561 | - | |
562 | -static int find_capability(const char *type) | |
563 | -{ | |
564 | - struct capability_entry *entry; | |
565 | - | |
566 | - for(entry = capability_table; entry->type; ++entry) | |
567 | - if(!strncmp(entry->type, type, 4)) | |
568 | - break; | |
569 | - return entry->capability; | |
570 | -} | |
571 | - | |
572 | -static int viocd_probe(struct vio_dev *vdev, const struct vio_device_id *id) | |
573 | -{ | |
574 | - struct gendisk *gendisk; | |
575 | - int deviceno; | |
576 | - struct disk_info *d; | |
577 | - struct cdrom_device_info *c; | |
578 | - struct request_queue *q; | |
579 | - struct device_node *node = vdev->dev.of_node; | |
580 | - | |
581 | - deviceno = vdev->unit_address; | |
582 | - if (deviceno >= VIOCD_MAX_CD) | |
583 | - return -ENODEV; | |
584 | - if (!node) | |
585 | - return -ENODEV; | |
586 | - | |
587 | - if (deviceno >= viocd_numdev) | |
588 | - viocd_numdev = deviceno + 1; | |
589 | - | |
590 | - d = &viocd_diskinfo[deviceno]; | |
591 | - d->rsrcname = of_get_property(node, "linux,vio_rsrcname", NULL); | |
592 | - d->type = of_get_property(node, "linux,vio_type", NULL); | |
593 | - d->model = of_get_property(node, "linux,vio_model", NULL); | |
594 | - | |
595 | - c = &d->viocd_info; | |
596 | - | |
597 | - c->ops = &viocd_dops; | |
598 | - c->speed = 4; | |
599 | - c->capacity = 1; | |
600 | - c->handle = d; | |
601 | - c->mask = ~find_capability(d->type); | |
602 | - sprintf(c->name, VIOCD_DEVICE "%c", 'a' + deviceno); | |
603 | - | |
604 | - if (register_cdrom(c) != 0) { | |
605 | - pr_warning("Cannot register viocd CD-ROM %s!\n", c->name); | |
606 | - goto out; | |
607 | - } | |
608 | - pr_info("cd %s is iSeries resource %10.10s type %4.4s, model %3.3s\n", | |
609 | - c->name, d->rsrcname, d->type, d->model); | |
610 | - q = blk_init_queue(do_viocd_request, &viocd_reqlock); | |
611 | - if (q == NULL) { | |
612 | - pr_warning("Cannot allocate queue for %s!\n", c->name); | |
613 | - goto out_unregister_cdrom; | |
614 | - } | |
615 | - gendisk = alloc_disk(1); | |
616 | - if (gendisk == NULL) { | |
617 | - pr_warning("Cannot create gendisk for %s!\n", c->name); | |
618 | - goto out_cleanup_queue; | |
619 | - } | |
620 | - gendisk->major = VIOCD_MAJOR; | |
621 | - gendisk->first_minor = deviceno; | |
622 | - strncpy(gendisk->disk_name, c->name, | |
623 | - sizeof(gendisk->disk_name)); | |
624 | - blk_queue_max_segments(q, 1); | |
625 | - blk_queue_max_hw_sectors(q, 4096 / 512); | |
626 | - gendisk->queue = q; | |
627 | - gendisk->fops = &viocd_fops; | |
628 | - gendisk->flags = GENHD_FL_CD | GENHD_FL_REMOVABLE | | |
629 | - GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE; | |
630 | - set_capacity(gendisk, 0); | |
631 | - gendisk->private_data = d; | |
632 | - d->viocd_disk = gendisk; | |
633 | - d->dev = &vdev->dev; | |
634 | - gendisk->driverfs_dev = d->dev; | |
635 | - add_disk(gendisk); | |
636 | - return 0; | |
637 | - | |
638 | -out_cleanup_queue: | |
639 | - blk_cleanup_queue(q); | |
640 | -out_unregister_cdrom: | |
641 | - unregister_cdrom(c); | |
642 | -out: | |
643 | - return -ENODEV; | |
644 | -} | |
645 | - | |
646 | -static int viocd_remove(struct vio_dev *vdev) | |
647 | -{ | |
648 | - struct disk_info *d = &viocd_diskinfo[vdev->unit_address]; | |
649 | - | |
650 | - unregister_cdrom(&d->viocd_info); | |
651 | - del_gendisk(d->viocd_disk); | |
652 | - blk_cleanup_queue(d->viocd_disk->queue); | |
653 | - put_disk(d->viocd_disk); | |
654 | - return 0; | |
655 | -} | |
656 | - | |
657 | -/** | |
658 | - * viocd_device_table: Used by vio.c to match devices that we | |
659 | - * support. | |
660 | - */ | |
661 | -static struct vio_device_id viocd_device_table[] __devinitdata = { | |
662 | - { "block", "IBM,iSeries-viocd" }, | |
663 | - { "", "" } | |
664 | -}; | |
665 | -MODULE_DEVICE_TABLE(vio, viocd_device_table); | |
666 | - | |
667 | -static struct vio_driver viocd_driver = { | |
668 | - .id_table = viocd_device_table, | |
669 | - .probe = viocd_probe, | |
670 | - .remove = viocd_remove, | |
671 | - .driver = { | |
672 | - .name = "viocd", | |
673 | - .owner = THIS_MODULE, | |
674 | - } | |
675 | -}; | |
676 | - | |
677 | -static int __init viocd_init(void) | |
678 | -{ | |
679 | - int ret = 0; | |
680 | - | |
681 | - if (!firmware_has_feature(FW_FEATURE_ISERIES)) | |
682 | - return -ENODEV; | |
683 | - | |
684 | - if (viopath_hostLp == HvLpIndexInvalid) { | |
685 | - vio_set_hostlp(); | |
686 | - /* If we don't have a host, bail out */ | |
687 | - if (viopath_hostLp == HvLpIndexInvalid) | |
688 | - return -ENODEV; | |
689 | - } | |
690 | - | |
691 | - pr_info("vers " VIOCD_VERS ", hosting partition %d\n", viopath_hostLp); | |
692 | - | |
693 | - if (register_blkdev(VIOCD_MAJOR, VIOCD_DEVICE) != 0) { | |
694 | - pr_warning("Unable to get major %d for %s\n", | |
695 | - VIOCD_MAJOR, VIOCD_DEVICE); | |
696 | - return -EIO; | |
697 | - } | |
698 | - | |
699 | - ret = viopath_open(viopath_hostLp, viomajorsubtype_cdio, | |
700 | - MAX_CD_REQ + 2); | |
701 | - if (ret) { | |
702 | - pr_warning("error opening path to host partition %d\n", | |
703 | - viopath_hostLp); | |
704 | - goto out_unregister; | |
705 | - } | |
706 | - | |
707 | - /* Initialize our request handler */ | |
708 | - vio_setHandler(viomajorsubtype_cdio, vio_handle_cd_event); | |
709 | - | |
710 | - spin_lock_init(&viocd_reqlock); | |
711 | - | |
712 | - ret = vio_register_driver(&viocd_driver); | |
713 | - if (ret) | |
714 | - goto out_free_info; | |
715 | - | |
716 | - proc_create("iSeries/viocd", S_IFREG|S_IRUGO, NULL, | |
717 | - &proc_viocd_operations); | |
718 | - return 0; | |
719 | - | |
720 | -out_free_info: | |
721 | - vio_clearHandler(viomajorsubtype_cdio); | |
722 | - viopath_close(viopath_hostLp, viomajorsubtype_cdio, MAX_CD_REQ + 2); | |
723 | -out_unregister: | |
724 | - unregister_blkdev(VIOCD_MAJOR, VIOCD_DEVICE); | |
725 | - return ret; | |
726 | -} | |
727 | - | |
728 | -static void __exit viocd_exit(void) | |
729 | -{ | |
730 | - remove_proc_entry("iSeries/viocd", NULL); | |
731 | - vio_unregister_driver(&viocd_driver); | |
732 | - viopath_close(viopath_hostLp, viomajorsubtype_cdio, MAX_CD_REQ + 2); | |
733 | - vio_clearHandler(viomajorsubtype_cdio); | |
734 | - unregister_blkdev(VIOCD_MAJOR, VIOCD_DEVICE); | |
735 | -} | |
736 | - | |
737 | -module_init(viocd_init); | |
738 | -module_exit(viocd_exit); | |
739 | -MODULE_LICENSE("GPL"); |
drivers/char/viotape.c
Changes suppressed. Click to show
1 | -/* -*- linux-c -*- | |
2 | - * drivers/char/viotape.c | |
3 | - * | |
4 | - * iSeries Virtual Tape | |
5 | - * | |
6 | - * Authors: Dave Boutcher <boutcher@us.ibm.com> | |
7 | - * Ryan Arnold <ryanarn@us.ibm.com> | |
8 | - * Colin Devilbiss <devilbis@us.ibm.com> | |
9 | - * Stephen Rothwell | |
10 | - * | |
11 | - * (C) Copyright 2000-2004 IBM Corporation | |
12 | - * | |
13 | - * This program is free software; you can redistribute it and/or | |
14 | - * modify it under the terms of the GNU General Public License as | |
15 | - * published by the Free Software Foundation; either version 2 of the | |
16 | - * License, or (at your option) anyu later version. | |
17 | - * | |
18 | - * This program is distributed in the hope that it will be useful, but | |
19 | - * WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
21 | - * General Public License for more details. | |
22 | - * | |
23 | - * You should have received a copy of the GNU General Public License | |
24 | - * along with this program; if not, write to the Free Software Foundation, | |
25 | - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
26 | - * | |
27 | - * This routine provides access to tape drives owned and managed by an OS/400 | |
28 | - * partition running on the same box as this Linux partition. | |
29 | - * | |
30 | - * All tape operations are performed by sending messages back and forth to | |
31 | - * the OS/400 partition. The format of the messages is defined in | |
32 | - * iseries/vio.h | |
33 | - */ | |
34 | -#include <linux/module.h> | |
35 | -#include <linux/kernel.h> | |
36 | -#include <linux/errno.h> | |
37 | -#include <linux/init.h> | |
38 | -#include <linux/wait.h> | |
39 | -#include <linux/spinlock.h> | |
40 | -#include <linux/mtio.h> | |
41 | -#include <linux/device.h> | |
42 | -#include <linux/dma-mapping.h> | |
43 | -#include <linux/fs.h> | |
44 | -#include <linux/cdev.h> | |
45 | -#include <linux/major.h> | |
46 | -#include <linux/completion.h> | |
47 | -#include <linux/proc_fs.h> | |
48 | -#include <linux/seq_file.h> | |
49 | -#include <linux/mutex.h> | |
50 | -#include <linux/slab.h> | |
51 | - | |
52 | -#include <asm/uaccess.h> | |
53 | -#include <asm/ioctls.h> | |
54 | -#include <asm/firmware.h> | |
55 | -#include <asm/vio.h> | |
56 | -#include <asm/iseries/vio.h> | |
57 | -#include <asm/iseries/hv_lp_event.h> | |
58 | -#include <asm/iseries/hv_call_event.h> | |
59 | -#include <asm/iseries/hv_lp_config.h> | |
60 | - | |
61 | -#define VIOTAPE_VERSION "1.2" | |
62 | -#define VIOTAPE_MAXREQ 1 | |
63 | - | |
64 | -#define VIOTAPE_KERN_WARN KERN_WARNING "viotape: " | |
65 | -#define VIOTAPE_KERN_INFO KERN_INFO "viotape: " | |
66 | - | |
67 | -static DEFINE_MUTEX(proc_viotape_mutex); | |
68 | -static int viotape_numdev; | |
69 | - | |
70 | -/* | |
71 | - * The minor number follows the conventions of the SCSI tape drives. The | |
72 | - * rewind and mode are encoded in the minor #. We use this struct to break | |
73 | - * them out | |
74 | - */ | |
75 | -struct viot_devinfo_struct { | |
76 | - int devno; | |
77 | - int mode; | |
78 | - int rewind; | |
79 | -}; | |
80 | - | |
81 | -#define VIOTAPOP_RESET 0 | |
82 | -#define VIOTAPOP_FSF 1 | |
83 | -#define VIOTAPOP_BSF 2 | |
84 | -#define VIOTAPOP_FSR 3 | |
85 | -#define VIOTAPOP_BSR 4 | |
86 | -#define VIOTAPOP_WEOF 5 | |
87 | -#define VIOTAPOP_REW 6 | |
88 | -#define VIOTAPOP_NOP 7 | |
89 | -#define VIOTAPOP_EOM 8 | |
90 | -#define VIOTAPOP_ERASE 9 | |
91 | -#define VIOTAPOP_SETBLK 10 | |
92 | -#define VIOTAPOP_SETDENSITY 11 | |
93 | -#define VIOTAPOP_SETPOS 12 | |
94 | -#define VIOTAPOP_GETPOS 13 | |
95 | -#define VIOTAPOP_SETPART 14 | |
96 | -#define VIOTAPOP_UNLOAD 15 | |
97 | - | |
98 | -enum viotaperc { | |
99 | - viotape_InvalidRange = 0x0601, | |
100 | - viotape_InvalidToken = 0x0602, | |
101 | - viotape_DMAError = 0x0603, | |
102 | - viotape_UseError = 0x0604, | |
103 | - viotape_ReleaseError = 0x0605, | |
104 | - viotape_InvalidTape = 0x0606, | |
105 | - viotape_InvalidOp = 0x0607, | |
106 | - viotape_TapeErr = 0x0608, | |
107 | - | |
108 | - viotape_AllocTimedOut = 0x0640, | |
109 | - viotape_BOTEnc = 0x0641, | |
110 | - viotape_BlankTape = 0x0642, | |
111 | - viotape_BufferEmpty = 0x0643, | |
112 | - viotape_CleanCartFound = 0x0644, | |
113 | - viotape_CmdNotAllowed = 0x0645, | |
114 | - viotape_CmdNotSupported = 0x0646, | |
115 | - viotape_DataCheck = 0x0647, | |
116 | - viotape_DecompressErr = 0x0648, | |
117 | - viotape_DeviceTimeout = 0x0649, | |
118 | - viotape_DeviceUnavail = 0x064a, | |
119 | - viotape_DeviceBusy = 0x064b, | |
120 | - viotape_EndOfMedia = 0x064c, | |
121 | - viotape_EndOfTape = 0x064d, | |
122 | - viotape_EquipCheck = 0x064e, | |
123 | - viotape_InsufficientRs = 0x064f, | |
124 | - viotape_InvalidLogBlk = 0x0650, | |
125 | - viotape_LengthError = 0x0651, | |
126 | - viotape_LibDoorOpen = 0x0652, | |
127 | - viotape_LoadFailure = 0x0653, | |
128 | - viotape_NotCapable = 0x0654, | |
129 | - viotape_NotOperational = 0x0655, | |
130 | - viotape_NotReady = 0x0656, | |
131 | - viotape_OpCancelled = 0x0657, | |
132 | - viotape_PhyLinkErr = 0x0658, | |
133 | - viotape_RdyNotBOT = 0x0659, | |
134 | - viotape_TapeMark = 0x065a, | |
135 | - viotape_WriteProt = 0x065b | |
136 | -}; | |
137 | - | |
138 | -static const struct vio_error_entry viotape_err_table[] = { | |
139 | - { viotape_InvalidRange, EIO, "Internal error" }, | |
140 | - { viotape_InvalidToken, EIO, "Internal error" }, | |
141 | - { viotape_DMAError, EIO, "DMA error" }, | |
142 | - { viotape_UseError, EIO, "Internal error" }, | |
143 | - { viotape_ReleaseError, EIO, "Internal error" }, | |
144 | - { viotape_InvalidTape, EIO, "Invalid tape device" }, | |
145 | - { viotape_InvalidOp, EIO, "Invalid operation" }, | |
146 | - { viotape_TapeErr, EIO, "Tape error" }, | |
147 | - { viotape_AllocTimedOut, EBUSY, "Allocate timed out" }, | |
148 | - { viotape_BOTEnc, EIO, "Beginning of tape encountered" }, | |
149 | - { viotape_BlankTape, EIO, "Blank tape" }, | |
150 | - { viotape_BufferEmpty, EIO, "Buffer empty" }, | |
151 | - { viotape_CleanCartFound, ENOMEDIUM, "Cleaning cartridge found" }, | |
152 | - { viotape_CmdNotAllowed, EIO, "Command not allowed" }, | |
153 | - { viotape_CmdNotSupported, EIO, "Command not supported" }, | |
154 | - { viotape_DataCheck, EIO, "Data check" }, | |
155 | - { viotape_DecompressErr, EIO, "Decompression error" }, | |
156 | - { viotape_DeviceTimeout, EBUSY, "Device timeout" }, | |
157 | - { viotape_DeviceUnavail, EIO, "Device unavailable" }, | |
158 | - { viotape_DeviceBusy, EBUSY, "Device busy" }, | |
159 | - { viotape_EndOfMedia, ENOSPC, "End of media" }, | |
160 | - { viotape_EndOfTape, ENOSPC, "End of tape" }, | |
161 | - { viotape_EquipCheck, EIO, "Equipment check" }, | |
162 | - { viotape_InsufficientRs, EOVERFLOW, "Insufficient tape resources" }, | |
163 | - { viotape_InvalidLogBlk, EIO, "Invalid logical block location" }, | |
164 | - { viotape_LengthError, EOVERFLOW, "Length error" }, | |
165 | - { viotape_LibDoorOpen, EBUSY, "Door open" }, | |
166 | - { viotape_LoadFailure, ENOMEDIUM, "Load failure" }, | |
167 | - { viotape_NotCapable, EIO, "Not capable" }, | |
168 | - { viotape_NotOperational, EIO, "Not operational" }, | |
169 | - { viotape_NotReady, EIO, "Not ready" }, | |
170 | - { viotape_OpCancelled, EIO, "Operation cancelled" }, | |
171 | - { viotape_PhyLinkErr, EIO, "Physical link error" }, | |
172 | - { viotape_RdyNotBOT, EIO, "Ready but not beginning of tape" }, | |
173 | - { viotape_TapeMark, EIO, "Tape mark" }, | |
174 | - { viotape_WriteProt, EROFS, "Write protection error" }, | |
175 | - { 0, 0, NULL }, | |
176 | -}; | |
177 | - | |
178 | -/* Maximum number of tapes we support */ | |
179 | -#define VIOTAPE_MAX_TAPE HVMAXARCHITECTEDVIRTUALTAPES | |
180 | -#define MAX_PARTITIONS 4 | |
181 | - | |
182 | -/* defines for current tape state */ | |
183 | -#define VIOT_IDLE 0 | |
184 | -#define VIOT_READING 1 | |
185 | -#define VIOT_WRITING 2 | |
186 | - | |
187 | -/* Our info on the tapes */ | |
188 | -static struct { | |
189 | - const char *rsrcname; | |
190 | - const char *type; | |
191 | - const char *model; | |
192 | -} viotape_unitinfo[VIOTAPE_MAX_TAPE]; | |
193 | - | |
194 | -static struct mtget viomtget[VIOTAPE_MAX_TAPE]; | |
195 | - | |
196 | -static struct class *tape_class; | |
197 | - | |
198 | -static struct device *tape_device[VIOTAPE_MAX_TAPE]; | |
199 | - | |
200 | -/* | |
201 | - * maintain the current state of each tape (and partition) | |
202 | - * so that we know when to write EOF marks. | |
203 | - */ | |
204 | -static struct { | |
205 | - unsigned char cur_part; | |
206 | - unsigned char part_stat_rwi[MAX_PARTITIONS]; | |
207 | -} state[VIOTAPE_MAX_TAPE]; | |
208 | - | |
209 | -/* We single-thread */ | |
210 | -static struct semaphore reqSem; | |
211 | - | |
212 | -/* | |
213 | - * When we send a request, we use this struct to get the response back | |
214 | - * from the interrupt handler | |
215 | - */ | |
216 | -struct op_struct { | |
217 | - void *buffer; | |
218 | - dma_addr_t dmaaddr; | |
219 | - size_t count; | |
220 | - int rc; | |
221 | - int non_blocking; | |
222 | - struct completion com; | |
223 | - struct device *dev; | |
224 | - struct op_struct *next; | |
225 | -}; | |
226 | - | |
227 | -static spinlock_t op_struct_list_lock; | |
228 | -static struct op_struct *op_struct_list; | |
229 | - | |
230 | -/* forward declaration to resolve interdependence */ | |
231 | -static int chg_state(int index, unsigned char new_state, struct file *file); | |
232 | - | |
233 | -/* procfs support */ | |
234 | -static int proc_viotape_show(struct seq_file *m, void *v) | |
235 | -{ | |
236 | - int i; | |
237 | - | |
238 | - seq_printf(m, "viotape driver version " VIOTAPE_VERSION "\n"); | |
239 | - for (i = 0; i < viotape_numdev; i++) { | |
240 | - seq_printf(m, "viotape device %d is iSeries resource %10.10s" | |
241 | - "type %4.4s, model %3.3s\n", | |
242 | - i, viotape_unitinfo[i].rsrcname, | |
243 | - viotape_unitinfo[i].type, | |
244 | - viotape_unitinfo[i].model); | |
245 | - } | |
246 | - return 0; | |
247 | -} | |
248 | - | |
249 | -static int proc_viotape_open(struct inode *inode, struct file *file) | |
250 | -{ | |
251 | - return single_open(file, proc_viotape_show, NULL); | |
252 | -} | |
253 | - | |
254 | -static const struct file_operations proc_viotape_operations = { | |
255 | - .owner = THIS_MODULE, | |
256 | - .open = proc_viotape_open, | |
257 | - .read = seq_read, | |
258 | - .llseek = seq_lseek, | |
259 | - .release = single_release, | |
260 | -}; | |
261 | - | |
262 | -/* Decode the device minor number into its parts */ | |
263 | -void get_dev_info(struct inode *ino, struct viot_devinfo_struct *devi) | |
264 | -{ | |
265 | - devi->devno = iminor(ino) & 0x1F; | |
266 | - devi->mode = (iminor(ino) & 0x60) >> 5; | |
267 | - /* if bit is set in the minor, do _not_ rewind automatically */ | |
268 | - devi->rewind = (iminor(ino) & 0x80) == 0; | |
269 | -} | |
270 | - | |
271 | -/* This is called only from the exit and init paths, so no need for locking */ | |
272 | -static void clear_op_struct_pool(void) | |
273 | -{ | |
274 | - while (op_struct_list) { | |
275 | - struct op_struct *toFree = op_struct_list; | |
276 | - op_struct_list = op_struct_list->next; | |
277 | - kfree(toFree); | |
278 | - } | |
279 | -} | |
280 | - | |
281 | -/* Likewise, this is only called from the init path */ | |
282 | -static int add_op_structs(int structs) | |
283 | -{ | |
284 | - int i; | |
285 | - | |
286 | - for (i = 0; i < structs; ++i) { | |
287 | - struct op_struct *new_struct = | |
288 | - kmalloc(sizeof(*new_struct), GFP_KERNEL); | |
289 | - if (!new_struct) { | |
290 | - clear_op_struct_pool(); | |
291 | - return -ENOMEM; | |
292 | - } | |
293 | - new_struct->next = op_struct_list; | |
294 | - op_struct_list = new_struct; | |
295 | - } | |
296 | - return 0; | |
297 | -} | |
298 | - | |
299 | -/* Allocate an op structure from our pool */ | |
300 | -static struct op_struct *get_op_struct(void) | |
301 | -{ | |
302 | - struct op_struct *retval; | |
303 | - unsigned long flags; | |
304 | - | |
305 | - spin_lock_irqsave(&op_struct_list_lock, flags); | |
306 | - retval = op_struct_list; | |
307 | - if (retval) | |
308 | - op_struct_list = retval->next; | |
309 | - spin_unlock_irqrestore(&op_struct_list_lock, flags); | |
310 | - if (retval) { | |
311 | - memset(retval, 0, sizeof(*retval)); | |
312 | - init_completion(&retval->com); | |
313 | - } | |
314 | - | |
315 | - return retval; | |
316 | -} | |
317 | - | |
318 | -/* Return an op structure to our pool */ | |
319 | -static void free_op_struct(struct op_struct *op_struct) | |
320 | -{ | |
321 | - unsigned long flags; | |
322 | - | |
323 | - spin_lock_irqsave(&op_struct_list_lock, flags); | |
324 | - op_struct->next = op_struct_list; | |
325 | - op_struct_list = op_struct; | |
326 | - spin_unlock_irqrestore(&op_struct_list_lock, flags); | |
327 | -} | |
328 | - | |
329 | -/* Map our tape return codes to errno values */ | |
330 | -int tape_rc_to_errno(int tape_rc, char *operation, int tapeno) | |
331 | -{ | |
332 | - const struct vio_error_entry *err; | |
333 | - | |
334 | - if (tape_rc == 0) | |
335 | - return 0; | |
336 | - | |
337 | - err = vio_lookup_rc(viotape_err_table, tape_rc); | |
338 | - printk(VIOTAPE_KERN_WARN "error(%s) 0x%04x on Device %d (%-10s): %s\n", | |
339 | - operation, tape_rc, tapeno, | |
340 | - viotape_unitinfo[tapeno].rsrcname, err->msg); | |
341 | - return -err->errno; | |
342 | -} | |
343 | - | |
344 | -/* Write */ | |
345 | -static ssize_t viotap_write(struct file *file, const char *buf, | |
346 | - size_t count, loff_t * ppos) | |
347 | -{ | |
348 | - HvLpEvent_Rc hvrc; | |
349 | - unsigned short flags = file->f_flags; | |
350 | - int noblock = ((flags & O_NONBLOCK) != 0); | |
351 | - ssize_t ret; | |
352 | - struct viot_devinfo_struct devi; | |
353 | - struct op_struct *op = get_op_struct(); | |
354 | - | |
355 | - if (op == NULL) | |
356 | - return -ENOMEM; | |
357 | - | |
358 | - get_dev_info(file->f_path.dentry->d_inode, &devi); | |
359 | - | |
360 | - /* | |
361 | - * We need to make sure we can send a request. We use | |
362 | - * a semaphore to keep track of # requests in use. If | |
363 | - * we are non-blocking, make sure we don't block on the | |
364 | - * semaphore | |
365 | - */ | |
366 | - if (noblock) { | |
367 | - if (down_trylock(&reqSem)) { | |
368 | - ret = -EWOULDBLOCK; | |
369 | - goto free_op; | |
370 | - } | |
371 | - } else | |
372 | - down(&reqSem); | |
373 | - | |
374 | - /* Allocate a DMA buffer */ | |
375 | - op->dev = tape_device[devi.devno]; | |
376 | - op->buffer = dma_alloc_coherent(op->dev, count, &op->dmaaddr, | |
377 | - GFP_ATOMIC); | |
378 | - | |
379 | - if (op->buffer == NULL) { | |
380 | - printk(VIOTAPE_KERN_WARN | |
381 | - "error allocating dma buffer for len %ld\n", | |
382 | - count); | |
383 | - ret = -EFAULT; | |
384 | - goto up_sem; | |
385 | - } | |
386 | - | |
387 | - /* Copy the data into the buffer */ | |
388 | - if (copy_from_user(op->buffer, buf, count)) { | |
389 | - printk(VIOTAPE_KERN_WARN "tape: error on copy from user\n"); | |
390 | - ret = -EFAULT; | |
391 | - goto free_dma; | |
392 | - } | |
393 | - | |
394 | - op->non_blocking = noblock; | |
395 | - init_completion(&op->com); | |
396 | - op->count = count; | |
397 | - | |
398 | - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | |
399 | - HvLpEvent_Type_VirtualIo, | |
400 | - viomajorsubtype_tape | viotapewrite, | |
401 | - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, | |
402 | - viopath_sourceinst(viopath_hostLp), | |
403 | - viopath_targetinst(viopath_hostLp), | |
404 | - (u64)(unsigned long)op, VIOVERSION << 16, | |
405 | - ((u64)devi.devno << 48) | op->dmaaddr, count, 0, 0); | |
406 | - if (hvrc != HvLpEvent_Rc_Good) { | |
407 | - printk(VIOTAPE_KERN_WARN "hv error on op %d\n", | |
408 | - (int)hvrc); | |
409 | - ret = -EIO; | |
410 | - goto free_dma; | |
411 | - } | |
412 | - | |
413 | - if (noblock) | |
414 | - return count; | |
415 | - | |
416 | - wait_for_completion(&op->com); | |
417 | - | |
418 | - if (op->rc) | |
419 | - ret = tape_rc_to_errno(op->rc, "write", devi.devno); | |
420 | - else { | |
421 | - chg_state(devi.devno, VIOT_WRITING, file); | |
422 | - ret = op->count; | |
423 | - } | |
424 | - | |
425 | -free_dma: | |
426 | - dma_free_coherent(op->dev, count, op->buffer, op->dmaaddr); | |
427 | -up_sem: | |
428 | - up(&reqSem); | |
429 | -free_op: | |
430 | - free_op_struct(op); | |
431 | - return ret; | |
432 | -} | |
433 | - | |
434 | -/* read */ | |
435 | -static ssize_t viotap_read(struct file *file, char *buf, size_t count, | |
436 | - loff_t *ptr) | |
437 | -{ | |
438 | - HvLpEvent_Rc hvrc; | |
439 | - unsigned short flags = file->f_flags; | |
440 | - struct op_struct *op = get_op_struct(); | |
441 | - int noblock = ((flags & O_NONBLOCK) != 0); | |
442 | - ssize_t ret; | |
443 | - struct viot_devinfo_struct devi; | |
444 | - | |
445 | - if (op == NULL) | |
446 | - return -ENOMEM; | |
447 | - | |
448 | - get_dev_info(file->f_path.dentry->d_inode, &devi); | |
449 | - | |
450 | - /* | |
451 | - * We need to make sure we can send a request. We use | |
452 | - * a semaphore to keep track of # requests in use. If | |
453 | - * we are non-blocking, make sure we don't block on the | |
454 | - * semaphore | |
455 | - */ | |
456 | - if (noblock) { | |
457 | - if (down_trylock(&reqSem)) { | |
458 | - ret = -EWOULDBLOCK; | |
459 | - goto free_op; | |
460 | - } | |
461 | - } else | |
462 | - down(&reqSem); | |
463 | - | |
464 | - chg_state(devi.devno, VIOT_READING, file); | |
465 | - | |
466 | - /* Allocate a DMA buffer */ | |
467 | - op->dev = tape_device[devi.devno]; | |
468 | - op->buffer = dma_alloc_coherent(op->dev, count, &op->dmaaddr, | |
469 | - GFP_ATOMIC); | |
470 | - if (op->buffer == NULL) { | |
471 | - ret = -EFAULT; | |
472 | - goto up_sem; | |
473 | - } | |
474 | - | |
475 | - op->count = count; | |
476 | - init_completion(&op->com); | |
477 | - | |
478 | - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | |
479 | - HvLpEvent_Type_VirtualIo, | |
480 | - viomajorsubtype_tape | viotaperead, | |
481 | - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, | |
482 | - viopath_sourceinst(viopath_hostLp), | |
483 | - viopath_targetinst(viopath_hostLp), | |
484 | - (u64)(unsigned long)op, VIOVERSION << 16, | |
485 | - ((u64)devi.devno << 48) | op->dmaaddr, count, 0, 0); | |
486 | - if (hvrc != HvLpEvent_Rc_Good) { | |
487 | - printk(VIOTAPE_KERN_WARN "tape hv error on op %d\n", | |
488 | - (int)hvrc); | |
489 | - ret = -EIO; | |
490 | - goto free_dma; | |
491 | - } | |
492 | - | |
493 | - wait_for_completion(&op->com); | |
494 | - | |
495 | - if (op->rc) | |
496 | - ret = tape_rc_to_errno(op->rc, "read", devi.devno); | |
497 | - else { | |
498 | - ret = op->count; | |
499 | - if (ret && copy_to_user(buf, op->buffer, ret)) { | |
500 | - printk(VIOTAPE_KERN_WARN "error on copy_to_user\n"); | |
501 | - ret = -EFAULT; | |
502 | - } | |
503 | - } | |
504 | - | |
505 | -free_dma: | |
506 | - dma_free_coherent(op->dev, count, op->buffer, op->dmaaddr); | |
507 | -up_sem: | |
508 | - up(&reqSem); | |
509 | -free_op: | |
510 | - free_op_struct(op); | |
511 | - return ret; | |
512 | -} | |
513 | - | |
514 | -/* ioctl */ | |
515 | -static int viotap_ioctl(struct inode *inode, struct file *file, | |
516 | - unsigned int cmd, unsigned long arg) | |
517 | -{ | |
518 | - HvLpEvent_Rc hvrc; | |
519 | - int ret; | |
520 | - struct viot_devinfo_struct devi; | |
521 | - struct mtop mtc; | |
522 | - u32 myOp; | |
523 | - struct op_struct *op = get_op_struct(); | |
524 | - | |
525 | - if (op == NULL) | |
526 | - return -ENOMEM; | |
527 | - | |
528 | - get_dev_info(file->f_path.dentry->d_inode, &devi); | |
529 | - | |
530 | - down(&reqSem); | |
531 | - | |
532 | - ret = -EINVAL; | |
533 | - | |
534 | - switch (cmd) { | |
535 | - case MTIOCTOP: | |
536 | - ret = -EFAULT; | |
537 | - /* | |
538 | - * inode is null if and only if we (the kernel) | |
539 | - * made the request | |
540 | - */ | |
541 | - if (inode == NULL) | |
542 | - memcpy(&mtc, (void *) arg, sizeof(struct mtop)); | |
543 | - else if (copy_from_user((char *)&mtc, (char *)arg, | |
544 | - sizeof(struct mtop))) | |
545 | - goto free_op; | |
546 | - | |
547 | - ret = -EIO; | |
548 | - switch (mtc.mt_op) { | |
549 | - case MTRESET: | |
550 | - myOp = VIOTAPOP_RESET; | |
551 | - break; | |
552 | - case MTFSF: | |
553 | - myOp = VIOTAPOP_FSF; | |
554 | - break; | |
555 | - case MTBSF: | |
556 | - myOp = VIOTAPOP_BSF; | |
557 | - break; | |
558 | - case MTFSR: | |
559 | - myOp = VIOTAPOP_FSR; | |
560 | - break; | |
561 | - case MTBSR: | |
562 | - myOp = VIOTAPOP_BSR; | |
563 | - break; | |
564 | - case MTWEOF: | |
565 | - myOp = VIOTAPOP_WEOF; | |
566 | - break; | |
567 | - case MTREW: | |
568 | - myOp = VIOTAPOP_REW; | |
569 | - break; | |
570 | - case MTNOP: | |
571 | - myOp = VIOTAPOP_NOP; | |
572 | - break; | |
573 | - case MTEOM: | |
574 | - myOp = VIOTAPOP_EOM; | |
575 | - break; | |
576 | - case MTERASE: | |
577 | - myOp = VIOTAPOP_ERASE; | |
578 | - break; | |
579 | - case MTSETBLK: | |
580 | - myOp = VIOTAPOP_SETBLK; | |
581 | - break; | |
582 | - case MTSETDENSITY: | |
583 | - myOp = VIOTAPOP_SETDENSITY; | |
584 | - break; | |
585 | - case MTTELL: | |
586 | - myOp = VIOTAPOP_GETPOS; | |
587 | - break; | |
588 | - case MTSEEK: | |
589 | - myOp = VIOTAPOP_SETPOS; | |
590 | - break; | |
591 | - case MTSETPART: | |
592 | - myOp = VIOTAPOP_SETPART; | |
593 | - break; | |
594 | - case MTOFFL: | |
595 | - myOp = VIOTAPOP_UNLOAD; | |
596 | - break; | |
597 | - default: | |
598 | - printk(VIOTAPE_KERN_WARN "MTIOCTOP called " | |
599 | - "with invalid op 0x%x\n", mtc.mt_op); | |
600 | - goto free_op; | |
601 | - } | |
602 | - | |
603 | - /* | |
604 | - * if we moved the head, we are no longer | |
605 | - * reading or writing | |
606 | - */ | |
607 | - switch (mtc.mt_op) { | |
608 | - case MTFSF: | |
609 | - case MTBSF: | |
610 | - case MTFSR: | |
611 | - case MTBSR: | |
612 | - case MTTELL: | |
613 | - case MTSEEK: | |
614 | - case MTREW: | |
615 | - chg_state(devi.devno, VIOT_IDLE, file); | |
616 | - } | |
617 | - | |
618 | - init_completion(&op->com); | |
619 | - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | |
620 | - HvLpEvent_Type_VirtualIo, | |
621 | - viomajorsubtype_tape | viotapeop, | |
622 | - HvLpEvent_AckInd_DoAck, | |
623 | - HvLpEvent_AckType_ImmediateAck, | |
624 | - viopath_sourceinst(viopath_hostLp), | |
625 | - viopath_targetinst(viopath_hostLp), | |
626 | - (u64)(unsigned long)op, | |
627 | - VIOVERSION << 16, | |
628 | - ((u64)devi.devno << 48), 0, | |
629 | - (((u64)myOp) << 32) | mtc.mt_count, 0); | |
630 | - if (hvrc != HvLpEvent_Rc_Good) { | |
631 | - printk(VIOTAPE_KERN_WARN "hv error on op %d\n", | |
632 | - (int)hvrc); | |
633 | - goto free_op; | |
634 | - } | |
635 | - wait_for_completion(&op->com); | |
636 | - ret = tape_rc_to_errno(op->rc, "tape operation", devi.devno); | |
637 | - goto free_op; | |
638 | - | |
639 | - case MTIOCGET: | |
640 | - ret = -EIO; | |
641 | - init_completion(&op->com); | |
642 | - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | |
643 | - HvLpEvent_Type_VirtualIo, | |
644 | - viomajorsubtype_tape | viotapegetstatus, | |
645 | - HvLpEvent_AckInd_DoAck, | |
646 | - HvLpEvent_AckType_ImmediateAck, | |
647 | - viopath_sourceinst(viopath_hostLp), | |
648 | - viopath_targetinst(viopath_hostLp), | |
649 | - (u64)(unsigned long)op, VIOVERSION << 16, | |
650 | - ((u64)devi.devno << 48), 0, 0, 0); | |
651 | - if (hvrc != HvLpEvent_Rc_Good) { | |
652 | - printk(VIOTAPE_KERN_WARN "hv error on op %d\n", | |
653 | - (int)hvrc); | |
654 | - goto free_op; | |
655 | - } | |
656 | - wait_for_completion(&op->com); | |
657 | - | |
658 | - /* Operation is complete - grab the error code */ | |
659 | - ret = tape_rc_to_errno(op->rc, "get status", devi.devno); | |
660 | - free_op_struct(op); | |
661 | - up(&reqSem); | |
662 | - | |
663 | - if ((ret == 0) && copy_to_user((void *)arg, | |
664 | - &viomtget[devi.devno], | |
665 | - sizeof(viomtget[0]))) | |
666 | - ret = -EFAULT; | |
667 | - return ret; | |
668 | - case MTIOCPOS: | |
669 | - printk(VIOTAPE_KERN_WARN "Got an (unsupported) MTIOCPOS\n"); | |
670 | - break; | |
671 | - default: | |
672 | - printk(VIOTAPE_KERN_WARN "got an unsupported ioctl 0x%0x\n", | |
673 | - cmd); | |
674 | - break; | |
675 | - } | |
676 | - | |
677 | -free_op: | |
678 | - free_op_struct(op); | |
679 | - up(&reqSem); | |
680 | - return ret; | |
681 | -} | |
682 | - | |
683 | -static long viotap_unlocked_ioctl(struct file *file, | |
684 | - unsigned int cmd, unsigned long arg) | |
685 | -{ | |
686 | - long rc; | |
687 | - | |
688 | - mutex_lock(&proc_viotape_mutex); | |
689 | - rc = viotap_ioctl(file->f_path.dentry->d_inode, file, cmd, arg); | |
690 | - mutex_unlock(&proc_viotape_mutex); | |
691 | - return rc; | |
692 | -} | |
693 | - | |
694 | -static int viotap_open(struct inode *inode, struct file *file) | |
695 | -{ | |
696 | - HvLpEvent_Rc hvrc; | |
697 | - struct viot_devinfo_struct devi; | |
698 | - int ret; | |
699 | - struct op_struct *op = get_op_struct(); | |
700 | - | |
701 | - if (op == NULL) | |
702 | - return -ENOMEM; | |
703 | - | |
704 | - mutex_lock(&proc_viotape_mutex); | |
705 | - get_dev_info(file->f_path.dentry->d_inode, &devi); | |
706 | - | |
707 | - /* Note: We currently only support one mode! */ | |
708 | - if ((devi.devno >= viotape_numdev) || (devi.mode)) { | |
709 | - ret = -ENODEV; | |
710 | - goto free_op; | |
711 | - } | |
712 | - | |
713 | - init_completion(&op->com); | |
714 | - | |
715 | - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | |
716 | - HvLpEvent_Type_VirtualIo, | |
717 | - viomajorsubtype_tape | viotapeopen, | |
718 | - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, | |
719 | - viopath_sourceinst(viopath_hostLp), | |
720 | - viopath_targetinst(viopath_hostLp), | |
721 | - (u64)(unsigned long)op, VIOVERSION << 16, | |
722 | - ((u64)devi.devno << 48), 0, 0, 0); | |
723 | - if (hvrc != 0) { | |
724 | - printk(VIOTAPE_KERN_WARN "bad rc on signalLpEvent %d\n", | |
725 | - (int) hvrc); | |
726 | - ret = -EIO; | |
727 | - goto free_op; | |
728 | - } | |
729 | - | |
730 | - wait_for_completion(&op->com); | |
731 | - ret = tape_rc_to_errno(op->rc, "open", devi.devno); | |
732 | - | |
733 | -free_op: | |
734 | - free_op_struct(op); | |
735 | - mutex_unlock(&proc_viotape_mutex); | |
736 | - return ret; | |
737 | -} | |
738 | - | |
739 | - | |
740 | -static int viotap_release(struct inode *inode, struct file *file) | |
741 | -{ | |
742 | - HvLpEvent_Rc hvrc; | |
743 | - struct viot_devinfo_struct devi; | |
744 | - int ret = 0; | |
745 | - struct op_struct *op = get_op_struct(); | |
746 | - | |
747 | - if (op == NULL) | |
748 | - return -ENOMEM; | |
749 | - init_completion(&op->com); | |
750 | - | |
751 | - get_dev_info(file->f_path.dentry->d_inode, &devi); | |
752 | - | |
753 | - if (devi.devno >= viotape_numdev) { | |
754 | - ret = -ENODEV; | |
755 | - goto free_op; | |
756 | - } | |
757 | - | |
758 | - chg_state(devi.devno, VIOT_IDLE, file); | |
759 | - | |
760 | - if (devi.rewind) { | |
761 | - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | |
762 | - HvLpEvent_Type_VirtualIo, | |
763 | - viomajorsubtype_tape | viotapeop, | |
764 | - HvLpEvent_AckInd_DoAck, | |
765 | - HvLpEvent_AckType_ImmediateAck, | |
766 | - viopath_sourceinst(viopath_hostLp), | |
767 | - viopath_targetinst(viopath_hostLp), | |
768 | - (u64)(unsigned long)op, VIOVERSION << 16, | |
769 | - ((u64)devi.devno << 48), 0, | |
770 | - ((u64)VIOTAPOP_REW) << 32, 0); | |
771 | - wait_for_completion(&op->com); | |
772 | - | |
773 | - tape_rc_to_errno(op->rc, "rewind", devi.devno); | |
774 | - } | |
775 | - | |
776 | - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | |
777 | - HvLpEvent_Type_VirtualIo, | |
778 | - viomajorsubtype_tape | viotapeclose, | |
779 | - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, | |
780 | - viopath_sourceinst(viopath_hostLp), | |
781 | - viopath_targetinst(viopath_hostLp), | |
782 | - (u64)(unsigned long)op, VIOVERSION << 16, | |
783 | - ((u64)devi.devno << 48), 0, 0, 0); | |
784 | - if (hvrc != 0) { | |
785 | - printk(VIOTAPE_KERN_WARN "bad rc on signalLpEvent %d\n", | |
786 | - (int) hvrc); | |
787 | - ret = -EIO; | |
788 | - goto free_op; | |
789 | - } | |
790 | - | |
791 | - wait_for_completion(&op->com); | |
792 | - | |
793 | - if (op->rc) | |
794 | - printk(VIOTAPE_KERN_WARN "close failed\n"); | |
795 | - | |
796 | -free_op: | |
797 | - free_op_struct(op); | |
798 | - return ret; | |
799 | -} | |
800 | - | |
801 | -const struct file_operations viotap_fops = { | |
802 | - .owner = THIS_MODULE, | |
803 | - .read = viotap_read, | |
804 | - .write = viotap_write, | |
805 | - .unlocked_ioctl = viotap_unlocked_ioctl, | |
806 | - .open = viotap_open, | |
807 | - .release = viotap_release, | |
808 | - .llseek = noop_llseek, | |
809 | -}; | |
810 | - | |
811 | -/* Handle interrupt events for tape */ | |
812 | -static void vioHandleTapeEvent(struct HvLpEvent *event) | |
813 | -{ | |
814 | - int tapeminor; | |
815 | - struct op_struct *op; | |
816 | - struct viotapelpevent *tevent = (struct viotapelpevent *)event; | |
817 | - | |
818 | - if (event == NULL) { | |
819 | - /* Notification that a partition went away! */ | |
820 | - if (!viopath_isactive(viopath_hostLp)) { | |
821 | - /* TODO! Clean up */ | |
822 | - } | |
823 | - return; | |
824 | - } | |
825 | - | |
826 | - tapeminor = event->xSubtype & VIOMINOR_SUBTYPE_MASK; | |
827 | - op = (struct op_struct *)event->xCorrelationToken; | |
828 | - switch (tapeminor) { | |
829 | - case viotapeopen: | |
830 | - case viotapeclose: | |
831 | - op->rc = tevent->sub_type_result; | |
832 | - complete(&op->com); | |
833 | - break; | |
834 | - case viotaperead: | |
835 | - op->rc = tevent->sub_type_result; | |
836 | - op->count = tevent->len; | |
837 | - complete(&op->com); | |
838 | - break; | |
839 | - case viotapewrite: | |
840 | - if (op->non_blocking) { | |
841 | - dma_free_coherent(op->dev, op->count, | |
842 | - op->buffer, op->dmaaddr); | |
843 | - free_op_struct(op); | |
844 | - up(&reqSem); | |
845 | - } else { | |
846 | - op->rc = tevent->sub_type_result; | |
847 | - op->count = tevent->len; | |
848 | - complete(&op->com); | |
849 | - } | |
850 | - break; | |
851 | - case viotapeop: | |
852 | - case viotapegetpos: | |
853 | - case viotapesetpos: | |
854 | - case viotapegetstatus: | |
855 | - if (op) { | |
856 | - op->count = tevent->u.op.count; | |
857 | - op->rc = tevent->sub_type_result; | |
858 | - if (!op->non_blocking) | |
859 | - complete(&op->com); | |
860 | - } | |
861 | - break; | |
862 | - default: | |
863 | - printk(VIOTAPE_KERN_WARN "weird ack\n"); | |
864 | - } | |
865 | -} | |
866 | - | |
867 | -static int viotape_probe(struct vio_dev *vdev, const struct vio_device_id *id) | |
868 | -{ | |
869 | - int i = vdev->unit_address; | |
870 | - int j; | |
871 | - struct device_node *node = vdev->dev.of_node; | |
872 | - | |
873 | - if (i >= VIOTAPE_MAX_TAPE) | |
874 | - return -ENODEV; | |
875 | - if (!node) | |
876 | - return -ENODEV; | |
877 | - | |
878 | - if (i >= viotape_numdev) | |
879 | - viotape_numdev = i + 1; | |
880 | - | |
881 | - tape_device[i] = &vdev->dev; | |
882 | - viotape_unitinfo[i].rsrcname = of_get_property(node, | |
883 | - "linux,vio_rsrcname", NULL); | |
884 | - viotape_unitinfo[i].type = of_get_property(node, "linux,vio_type", | |
885 | - NULL); | |
886 | - viotape_unitinfo[i].model = of_get_property(node, "linux,vio_model", | |
887 | - NULL); | |
888 | - | |
889 | - state[i].cur_part = 0; | |
890 | - for (j = 0; j < MAX_PARTITIONS; ++j) | |
891 | - state[i].part_stat_rwi[j] = VIOT_IDLE; | |
892 | - device_create(tape_class, NULL, MKDEV(VIOTAPE_MAJOR, i), NULL, | |
893 | - "iseries!vt%d", i); | |
894 | - device_create(tape_class, NULL, MKDEV(VIOTAPE_MAJOR, i | 0x80), NULL, | |
895 | - "iseries!nvt%d", i); | |
896 | - printk(VIOTAPE_KERN_INFO "tape iseries/vt%d is iSeries " | |
897 | - "resource %10.10s type %4.4s, model %3.3s\n", | |
898 | - i, viotape_unitinfo[i].rsrcname, | |
899 | - viotape_unitinfo[i].type, viotape_unitinfo[i].model); | |
900 | - return 0; | |
901 | -} | |
902 | - | |
903 | -static int viotape_remove(struct vio_dev *vdev) | |
904 | -{ | |
905 | - int i = vdev->unit_address; | |
906 | - | |
907 | - device_destroy(tape_class, MKDEV(VIOTAPE_MAJOR, i | 0x80)); | |
908 | - device_destroy(tape_class, MKDEV(VIOTAPE_MAJOR, i)); | |
909 | - return 0; | |
910 | -} | |
911 | - | |
912 | -/** | |
913 | - * viotape_device_table: Used by vio.c to match devices that we | |
914 | - * support. | |
915 | - */ | |
916 | -static struct vio_device_id viotape_device_table[] __devinitdata = { | |
917 | - { "byte", "IBM,iSeries-viotape" }, | |
918 | - { "", "" } | |
919 | -}; | |
920 | -MODULE_DEVICE_TABLE(vio, viotape_device_table); | |
921 | - | |
922 | -static struct vio_driver viotape_driver = { | |
923 | - .id_table = viotape_device_table, | |
924 | - .probe = viotape_probe, | |
925 | - .remove = viotape_remove, | |
926 | - .driver = { | |
927 | - .name = "viotape", | |
928 | - .owner = THIS_MODULE, | |
929 | - } | |
930 | -}; | |
931 | - | |
932 | - | |
933 | -int __init viotap_init(void) | |
934 | -{ | |
935 | - int ret; | |
936 | - | |
937 | - if (!firmware_has_feature(FW_FEATURE_ISERIES)) | |
938 | - return -ENODEV; | |
939 | - | |
940 | - op_struct_list = NULL; | |
941 | - if ((ret = add_op_structs(VIOTAPE_MAXREQ)) < 0) { | |
942 | - printk(VIOTAPE_KERN_WARN "couldn't allocate op structs\n"); | |
943 | - return ret; | |
944 | - } | |
945 | - spin_lock_init(&op_struct_list_lock); | |
946 | - | |
947 | - sema_init(&reqSem, VIOTAPE_MAXREQ); | |
948 | - | |
949 | - if (viopath_hostLp == HvLpIndexInvalid) { | |
950 | - vio_set_hostlp(); | |
951 | - if (viopath_hostLp == HvLpIndexInvalid) { | |
952 | - ret = -ENODEV; | |
953 | - goto clear_op; | |
954 | - } | |
955 | - } | |
956 | - | |
957 | - ret = viopath_open(viopath_hostLp, viomajorsubtype_tape, | |
958 | - VIOTAPE_MAXREQ + 2); | |
959 | - if (ret) { | |
960 | - printk(VIOTAPE_KERN_WARN | |
961 | - "error on viopath_open to hostlp %d\n", ret); | |
962 | - ret = -EIO; | |
963 | - goto clear_op; | |
964 | - } | |
965 | - | |
966 | - printk(VIOTAPE_KERN_INFO "vers " VIOTAPE_VERSION | |
967 | - ", hosting partition %d\n", viopath_hostLp); | |
968 | - | |
969 | - vio_setHandler(viomajorsubtype_tape, vioHandleTapeEvent); | |
970 | - | |
971 | - ret = register_chrdev(VIOTAPE_MAJOR, "viotape", &viotap_fops); | |
972 | - if (ret < 0) { | |
973 | - printk(VIOTAPE_KERN_WARN "Error registering viotape device\n"); | |
974 | - goto clear_handler; | |
975 | - } | |
976 | - | |
977 | - tape_class = class_create(THIS_MODULE, "tape"); | |
978 | - if (IS_ERR(tape_class)) { | |
979 | - printk(VIOTAPE_KERN_WARN "Unable to allocat class\n"); | |
980 | - ret = PTR_ERR(tape_class); | |
981 | - goto unreg_chrdev; | |
982 | - } | |
983 | - | |
984 | - ret = vio_register_driver(&viotape_driver); | |
985 | - if (ret) | |
986 | - goto unreg_class; | |
987 | - | |
988 | - proc_create("iSeries/viotape", S_IFREG|S_IRUGO, NULL, | |
989 | - &proc_viotape_operations); | |
990 | - | |
991 | - return 0; | |
992 | - | |
993 | -unreg_class: | |
994 | - class_destroy(tape_class); | |
995 | -unreg_chrdev: | |
996 | - unregister_chrdev(VIOTAPE_MAJOR, "viotape"); | |
997 | -clear_handler: | |
998 | - vio_clearHandler(viomajorsubtype_tape); | |
999 | - viopath_close(viopath_hostLp, viomajorsubtype_tape, VIOTAPE_MAXREQ + 2); | |
1000 | -clear_op: | |
1001 | - clear_op_struct_pool(); | |
1002 | - return ret; | |
1003 | -} | |
1004 | - | |
1005 | -/* Give a new state to the tape object */ | |
1006 | -static int chg_state(int index, unsigned char new_state, struct file *file) | |
1007 | -{ | |
1008 | - unsigned char *cur_state = | |
1009 | - &state[index].part_stat_rwi[state[index].cur_part]; | |
1010 | - int rc = 0; | |
1011 | - | |
1012 | - /* if the same state, don't bother */ | |
1013 | - if (*cur_state == new_state) | |
1014 | - return 0; | |
1015 | - | |
1016 | - /* write an EOF if changing from writing to some other state */ | |
1017 | - if (*cur_state == VIOT_WRITING) { | |
1018 | - struct mtop write_eof = { MTWEOF, 1 }; | |
1019 | - | |
1020 | - rc = viotap_ioctl(NULL, file, MTIOCTOP, | |
1021 | - (unsigned long)&write_eof); | |
1022 | - } | |
1023 | - *cur_state = new_state; | |
1024 | - return rc; | |
1025 | -} | |
1026 | - | |
1027 | -/* Cleanup */ | |
1028 | -static void __exit viotap_exit(void) | |
1029 | -{ | |
1030 | - remove_proc_entry("iSeries/viotape", NULL); | |
1031 | - vio_unregister_driver(&viotape_driver); | |
1032 | - class_destroy(tape_class); | |
1033 | - unregister_chrdev(VIOTAPE_MAJOR, "viotape"); | |
1034 | - viopath_close(viopath_hostLp, viomajorsubtype_tape, VIOTAPE_MAXREQ + 2); | |
1035 | - vio_clearHandler(viomajorsubtype_tape); | |
1036 | - clear_op_struct_pool(); | |
1037 | -} | |
1038 | - | |
1039 | -MODULE_LICENSE("GPL"); | |
1040 | -module_init(viotap_init); | |
1041 | -module_exit(viotap_exit); |