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 */
proc_viocd_show(struct seq_file * m,void * v)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 
proc_viocd_open(struct inode * inode,struct file * file)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 
viocd_blk_open(struct block_device * bdev,fmode_t mode)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 
viocd_blk_release(struct gendisk * disk,fmode_t mode)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 
viocd_blk_ioctl(struct block_device * bdev,fmode_t mode,unsigned cmd,unsigned long arg)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 
viocd_blk_check_events(struct gendisk * disk,unsigned int clearing)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 
viocd_open(struct cdrom_device_info * cdi,int purpose)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 
viocd_release(struct cdrom_device_info * cdi)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 */
send_request(struct request * req)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 
do_viocd_request(struct request_queue * q)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 
viocd_check_events(struct cdrom_device_info * cdi,unsigned int clearing,int disc_nr)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 
viocd_lock_door(struct cdrom_device_info * cdi,int locking)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 
viocd_packet(struct cdrom_device_info * cdi,struct packet_command * cgc)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 
restart_all_queues(int first_index)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 */
vio_handle_cd_event(struct HvLpEvent * event)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 
viocd_audio_ioctl(struct cdrom_device_info * cdi,unsigned int cmd,void * arg)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 
find_capability(const char * type)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 
viocd_probe(struct vio_dev * vdev,const struct vio_device_id * id)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 
viocd_remove(struct vio_dev * vdev)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 
viocd_init(void)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 
viocd_exit(void)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");
740