vserver 1.9.3
[linux-2.6.git] / 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 <sfr@au1.ibm.com>
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 #include <linux/major.h>
35 #include <linux/blkdev.h>
36 #include <linux/cdrom.h>
37 #include <linux/errno.h>
38 #include <linux/init.h>
39 #include <linux/dma-mapping.h>
40 #include <linux/module.h>
41 #include <linux/completion.h>
42 #include <linux/proc_fs.h>
43 #include <linux/seq_file.h>
44
45 #include <asm/bug.h>
46
47 #include <asm/vio.h>
48 #include <asm/scatterlist.h>
49 #include <asm/iSeries/HvTypes.h>
50 #include <asm/iSeries/HvLpEvent.h>
51 #include <asm/iSeries/vio.h>
52
53 #define VIOCD_DEVICE                    "iseries/vcd"
54 #define VIOCD_DEVICE_DEVFS              "iseries/vcd"
55
56 #define VIOCD_VERS "1.06"
57
58 #define VIOCD_KERN_WARNING              KERN_WARNING "viocd: "
59 #define VIOCD_KERN_INFO                 KERN_INFO "viocd: "
60
61 struct viocdlpevent {
62         struct HvLpEvent        event;
63         u32                     reserved;
64         u16                     version;
65         u16                     sub_result;
66         u16                     disk;
67         u16                     flags;
68         u32                     token;
69         u64                     offset;         /* On open, max number of disks */
70         u64                     len;            /* On open, size of the disk */
71         u32                     block_size;     /* Only set on open */
72         u32                     media_size;     /* Only set on open */
73 };
74
75 enum viocdsubtype {
76         viocdopen = 0x0001,
77         viocdclose = 0x0002,
78         viocdread = 0x0003,
79         viocdwrite = 0x0004,
80         viocdlockdoor = 0x0005,
81         viocdgetinfo = 0x0006,
82         viocdcheck = 0x0007
83 };
84
85 /*
86  * Should probably make this a module parameter....sigh
87  */
88 #define VIOCD_MAX_CD    HVMAXARCHITECTEDVIRTUALCDROMS
89
90 static const struct vio_error_entry viocd_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 CD"},
97         {0x020C, EROFS, "Read Only Device"},
98         {0x020D, ENOMEDIUM, "Changed or Missing Volume (or Varied Off?)"},
99         {0x020E, EIO, "Optical System Error (Varied Off?)"},
100         {0x02FF, EIO, "Internal Error"},
101         {0x3010, EIO, "Changed Volume"},
102         {0xC100, EIO, "Optical System Error"},
103         {0x0000, 0, NULL},
104 };
105
106 /*
107  * This is the structure we use to exchange info between driver and interrupt
108  * handler
109  */
110 struct viocd_waitevent {
111         struct completion       com;
112         int                     rc;
113         u16                     sub_result;
114         int                     changed;
115 };
116
117 /* this is a lookup table for the true capabilities of a device */
118 struct capability_entry {
119         char    *type;
120         int     capability;
121 };
122
123 static struct capability_entry capability_table[] __initdata = {
124         { "6330", CDC_LOCK | CDC_DVD_RAM | CDC_RAM },
125         { "6331", CDC_LOCK | CDC_DVD_RAM | CDC_RAM },
126         { "6333", CDC_LOCK | CDC_DVD_RAM | CDC_RAM },
127         { "632A", CDC_LOCK | CDC_DVD_RAM | CDC_RAM },
128         { "6321", CDC_LOCK },
129         { "632B", 0 },
130         { NULL  , CDC_LOCK },
131 };
132
133 /* These are our internal structures for keeping track of devices */
134 static int viocd_numdev;
135
136 struct cdrom_info {
137         char    rsrcname[10];
138         char    type[4];
139         char    model[3];
140 };
141 /*
142  * This needs to be allocated since it is passed to the
143  * Hypervisor and we may be a module.
144  */
145 static struct cdrom_info *viocd_unitinfo;
146 static dma_addr_t unitinfo_dmaaddr;
147
148 struct disk_info {
149         struct gendisk                  *viocd_disk;
150         struct cdrom_device_info        viocd_info;
151         struct device                   *dev;
152 };
153 static struct disk_info viocd_diskinfo[VIOCD_MAX_CD];
154
155 #define DEVICE_NR(di)   ((di) - &viocd_diskinfo[0])
156
157 static request_queue_t *viocd_queue;
158 static spinlock_t viocd_reqlock;
159
160 #define MAX_CD_REQ      1
161
162 /* procfs support */
163 static int proc_viocd_show(struct seq_file *m, void *v)
164 {
165         int i;
166
167         for (i = 0; i < viocd_numdev; i++) {
168                 seq_printf(m, "viocd device %d is iSeries resource %10.10s"
169                                 "type %4.4s, model %3.3s\n",
170                                 i, viocd_unitinfo[i].rsrcname,
171                                 viocd_unitinfo[i].type,
172                                 viocd_unitinfo[i].model);
173         }
174         return 0;
175 }
176
177 static int proc_viocd_open(struct inode *inode, struct file *file)
178 {
179         return single_open(file, proc_viocd_show, NULL);
180 }
181
182 static struct file_operations proc_viocd_operations = {
183         .open           = proc_viocd_open,
184         .read           = seq_read,
185         .llseek         = seq_lseek,
186         .release        = single_release,
187 };
188
189 static int viocd_blk_open(struct inode *inode, struct file *file)
190 {
191         struct disk_info *di = inode->i_bdev->bd_disk->private_data;
192         return cdrom_open(&di->viocd_info, inode, file);
193 }
194
195 static int viocd_blk_release(struct inode *inode, struct file *file)
196 {
197         struct disk_info *di = inode->i_bdev->bd_disk->private_data;
198         return cdrom_release(&di->viocd_info, file);
199 }
200
201 static int viocd_blk_ioctl(struct inode *inode, struct file *file,
202                 unsigned cmd, unsigned long arg)
203 {
204         struct disk_info *di = inode->i_bdev->bd_disk->private_data;
205         return cdrom_ioctl(file, &di->viocd_info, inode, cmd, arg);
206 }
207
208 static int viocd_blk_media_changed(struct gendisk *disk)
209 {
210         struct disk_info *di = disk->private_data;
211         return cdrom_media_changed(&di->viocd_info);
212 }
213
214 struct block_device_operations viocd_fops = {
215         .owner =                THIS_MODULE,
216         .open =                 viocd_blk_open,
217         .release =              viocd_blk_release,
218         .ioctl =                viocd_blk_ioctl,
219         .media_changed =        viocd_blk_media_changed,
220 };
221
222 /* Get info on CD devices from OS/400 */
223 static void __init get_viocd_info(void)
224 {
225         HvLpEvent_Rc hvrc;
226         int i;
227         struct viocd_waitevent we;
228
229         viocd_unitinfo = dma_alloc_coherent(iSeries_vio_dev,
230                         sizeof(*viocd_unitinfo) * VIOCD_MAX_CD,
231                         &unitinfo_dmaaddr, GFP_ATOMIC);
232         if (viocd_unitinfo == NULL) {
233                 printk(VIOCD_KERN_WARNING "error allocating unitinfo\n");
234                 return;
235         }
236
237         memset(viocd_unitinfo, 0, sizeof(*viocd_unitinfo) * VIOCD_MAX_CD);
238
239         init_completion(&we.com);
240
241         hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
242                         HvLpEvent_Type_VirtualIo,
243                         viomajorsubtype_cdio | viocdgetinfo,
244                         HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
245                         viopath_sourceinst(viopath_hostLp),
246                         viopath_targetinst(viopath_hostLp),
247                         (u64)&we, VIOVERSION << 16, unitinfo_dmaaddr, 0,
248                         sizeof(*viocd_unitinfo) * VIOCD_MAX_CD, 0);
249         if (hvrc != HvLpEvent_Rc_Good) {
250                 printk(VIOCD_KERN_WARNING "cdrom error sending event. rc %d\n",
251                                 (int)hvrc);
252                 goto error_ret;
253         }
254
255         wait_for_completion(&we.com);
256
257         if (we.rc) {
258                 const struct vio_error_entry *err =
259                         vio_lookup_rc(viocd_err_table, we.sub_result);
260                 printk(VIOCD_KERN_WARNING "bad rc %d:0x%04X on getinfo: %s\n",
261                                 we.rc, we.sub_result, err->msg);
262                 goto error_ret;
263         }
264
265         for (i = 0; (i < VIOCD_MAX_CD) && viocd_unitinfo[i].rsrcname[0]; i++)
266                 viocd_numdev++;
267
268 error_ret:
269         if (viocd_numdev == 0) {
270                 dma_free_coherent(iSeries_vio_dev,
271                                 sizeof(*viocd_unitinfo) * VIOCD_MAX_CD,
272                                 viocd_unitinfo, unitinfo_dmaaddr);
273                 viocd_unitinfo = NULL;
274         }
275 }
276
277 static int viocd_open(struct cdrom_device_info *cdi, int purpose)
278 {
279         struct disk_info *diskinfo = cdi->handle;
280         int device_no = DEVICE_NR(diskinfo);
281         HvLpEvent_Rc hvrc;
282         struct viocd_waitevent we;
283
284         init_completion(&we.com);
285         hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
286                         HvLpEvent_Type_VirtualIo,
287                         viomajorsubtype_cdio | viocdopen,
288                         HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
289                         viopath_sourceinst(viopath_hostLp),
290                         viopath_targetinst(viopath_hostLp),
291                         (u64)&we, VIOVERSION << 16, ((u64)device_no << 48),
292                         0, 0, 0);
293         if (hvrc != 0) {
294                 printk(VIOCD_KERN_WARNING
295                                 "bad rc on HvCallEvent_signalLpEventFast %d\n",
296                                 (int)hvrc);
297                 return -EIO;
298         }
299
300         wait_for_completion(&we.com);
301
302         if (we.rc) {
303                 const struct vio_error_entry *err =
304                         vio_lookup_rc(viocd_err_table, we.sub_result);
305                 printk(VIOCD_KERN_WARNING "bad rc %d:0x%04X on open: %s\n",
306                                 we.rc, we.sub_result, err->msg);
307                 return -err->errno;
308         }
309
310         return 0;
311 }
312
313 static void viocd_release(struct cdrom_device_info *cdi)
314 {
315         int device_no = DEVICE_NR((struct disk_info *)cdi->handle);
316         HvLpEvent_Rc hvrc;
317
318         hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
319                         HvLpEvent_Type_VirtualIo,
320                         viomajorsubtype_cdio | viocdclose,
321                         HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck,
322                         viopath_sourceinst(viopath_hostLp),
323                         viopath_targetinst(viopath_hostLp), 0,
324                         VIOVERSION << 16, ((u64)device_no << 48), 0, 0, 0);
325         if (hvrc != 0)
326                 printk(VIOCD_KERN_WARNING
327                                 "bad rc on HvCallEvent_signalLpEventFast %d\n",
328                                 (int)hvrc);
329 }
330
331 /* Send a read or write request to OS/400 */
332 static int send_request(struct request *req)
333 {
334         HvLpEvent_Rc hvrc;
335         struct disk_info *diskinfo = req->rq_disk->private_data;
336         u64 len;
337         dma_addr_t dmaaddr;
338         int direction;
339         u16 cmd;
340         struct scatterlist sg;
341
342         BUG_ON(req->nr_phys_segments > 1);
343
344         if (rq_data_dir(req) == READ) {
345                 direction = DMA_FROM_DEVICE;
346                 cmd = viomajorsubtype_cdio | viocdread;
347         } else {
348                 direction = DMA_TO_DEVICE;
349                 cmd = viomajorsubtype_cdio | viocdwrite;
350         }
351
352         if (blk_rq_map_sg(req->q, req, &sg) == 0) {
353                 printk(VIOCD_KERN_WARNING
354                                 "error setting up scatter/gather list\n");
355                 return -1;
356         }
357
358         if (dma_map_sg(diskinfo->dev, &sg, 1, direction) == 0) {
359                 printk(VIOCD_KERN_WARNING "error allocating sg tce\n");
360                 return -1;
361         }
362         dmaaddr = sg_dma_address(&sg);
363         len = sg_dma_len(&sg);
364
365         hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
366                         HvLpEvent_Type_VirtualIo, cmd,
367                         HvLpEvent_AckInd_DoAck,
368                         HvLpEvent_AckType_ImmediateAck,
369                         viopath_sourceinst(viopath_hostLp),
370                         viopath_targetinst(viopath_hostLp),
371                         (u64)req, VIOVERSION << 16,
372                         ((u64)DEVICE_NR(diskinfo) << 48) | dmaaddr,
373                         (u64)req->sector * 512, len, 0);
374         if (hvrc != HvLpEvent_Rc_Good) {
375                 printk(VIOCD_KERN_WARNING "hv error on op %d\n", (int)hvrc);
376                 return -1;
377         }
378
379         return 0;
380 }
381
382
383 static int rwreq;
384
385 static void do_viocd_request(request_queue_t *q)
386 {
387         struct request *req;
388
389         while ((rwreq == 0) && ((req = elv_next_request(q)) != NULL)) {
390                 if (!blk_fs_request(req))
391                         end_request(req, 0);
392                 else if (send_request(req) < 0) {
393                         printk(VIOCD_KERN_WARNING
394                                         "unable to send message to OS/400!");
395                         end_request(req, 0);
396                 } else
397                         rwreq++;
398         }
399 }
400
401 static int viocd_media_changed(struct cdrom_device_info *cdi, int disc_nr)
402 {
403         struct viocd_waitevent we;
404         HvLpEvent_Rc hvrc;
405         int device_no = DEVICE_NR((struct disk_info *)cdi->handle);
406
407         init_completion(&we.com);
408
409         /* Send the open event to OS/400 */
410         hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
411                         HvLpEvent_Type_VirtualIo,
412                         viomajorsubtype_cdio | viocdcheck,
413                         HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
414                         viopath_sourceinst(viopath_hostLp),
415                         viopath_targetinst(viopath_hostLp),
416                         (u64)&we, VIOVERSION << 16, ((u64)device_no << 48),
417                         0, 0, 0);
418         if (hvrc != 0) {
419                 printk(VIOCD_KERN_WARNING "bad rc on HvCallEvent_signalLpEventFast %d\n",
420                                 (int)hvrc);
421                 return -EIO;
422         }
423
424         wait_for_completion(&we.com);
425
426         /* Check the return code.  If bad, assume no change */
427         if (we.rc) {
428                 const struct vio_error_entry *err =
429                         vio_lookup_rc(viocd_err_table, we.sub_result);
430                 printk(VIOCD_KERN_WARNING
431                                 "bad rc %d:0x%04X on check_change: %s; Assuming no change\n",
432                                 we.rc, we.sub_result, err->msg);
433                 return 0;
434         }
435
436         return we.changed;
437 }
438
439 static int viocd_lock_door(struct cdrom_device_info *cdi, int locking)
440 {
441         HvLpEvent_Rc hvrc;
442         u64 device_no = DEVICE_NR((struct disk_info *)cdi->handle);
443         /* NOTE: flags is 1 or 0 so it won't overwrite the device_no */
444         u64 flags = !!locking;
445         struct viocd_waitevent we;
446
447         init_completion(&we.com);
448
449         /* Send the lockdoor event to OS/400 */
450         hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
451                         HvLpEvent_Type_VirtualIo,
452                         viomajorsubtype_cdio | viocdlockdoor,
453                         HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
454                         viopath_sourceinst(viopath_hostLp),
455                         viopath_targetinst(viopath_hostLp),
456                         (u64)&we, VIOVERSION << 16,
457                         (device_no << 48) | (flags << 32), 0, 0, 0);
458         if (hvrc != 0) {
459                 printk(VIOCD_KERN_WARNING "bad rc on HvCallEvent_signalLpEventFast %d\n",
460                                 (int)hvrc);
461                 return -EIO;
462         }
463
464         wait_for_completion(&we.com);
465
466         if (we.rc != 0)
467                 return -EIO;
468         return 0;
469 }
470
471 static int viocd_packet(struct cdrom_device_info *cdi,
472                 struct packet_command *cgc)
473 {
474         unsigned int buflen = cgc->buflen;
475         int ret = -EIO;
476
477         switch (cgc->cmd[0]) {
478         case GPCMD_READ_DISC_INFO:
479                 {
480                         disc_information *di = (disc_information *)cgc->buffer;
481
482                         if (buflen >= 2) {
483                                 di->disc_information_length = cpu_to_be16(1);
484                                 ret = 0;
485                         }
486                         if (buflen >= 3)
487                                 di->erasable =
488                                         (cdi->ops->capability & ~cdi->mask
489                                          & (CDC_DVD_RAM | CDC_RAM)) != 0;
490                 }
491                 break;
492         default:
493                 if (cgc->sense) {
494                         /* indicate Unknown code */
495                         cgc->sense->sense_key = 0x05;
496                         cgc->sense->asc = 0x20;
497                         cgc->sense->ascq = 0x00;
498                 }
499                 break;
500         }
501
502         cgc->stat = ret;
503         return ret;
504 }
505
506 /* This routine handles incoming CD LP events */
507 static void vio_handle_cd_event(struct HvLpEvent *event)
508 {
509         struct viocdlpevent *bevent;
510         struct viocd_waitevent *pwe;
511         struct disk_info *di;
512         unsigned long flags;
513         struct request *req;
514
515
516         if (event == NULL)
517                 /* Notification that a partition went away! */
518                 return;
519         /* First, we should NEVER get an int here...only acks */
520         if (event->xFlags.xFunction == HvLpEvent_Function_Int) {
521                 printk(VIOCD_KERN_WARNING
522                                 "Yikes! got an int in viocd event handler!\n");
523                 if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) {
524                         event->xRc = HvLpEvent_Rc_InvalidSubtype;
525                         HvCallEvent_ackLpEvent(event);
526                 }
527         }
528
529         bevent = (struct viocdlpevent *)event;
530
531         switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
532         case viocdopen:
533                 if (event->xRc == 0) {
534                         di = &viocd_diskinfo[bevent->disk];
535                         blk_queue_hardsect_size(viocd_queue,
536                                         bevent->block_size);
537                         set_capacity(di->viocd_disk,
538                                         bevent->media_size *
539                                         bevent->block_size / 512);
540                 }
541                 /* FALLTHROUGH !! */
542         case viocdgetinfo:
543         case viocdlockdoor:
544                 pwe = (struct viocd_waitevent *)event->xCorrelationToken;
545 return_complete:
546                 pwe->rc = event->xRc;
547                 pwe->sub_result = bevent->sub_result;
548                 complete(&pwe->com);
549                 break;
550
551         case viocdcheck:
552                 pwe = (struct viocd_waitevent *)event->xCorrelationToken;
553                 pwe->changed = bevent->flags;
554                 goto return_complete;
555
556         case viocdclose:
557                 break;
558
559         case viocdwrite:
560         case viocdread:
561                 /*
562                  * Since this is running in interrupt mode, we need to
563                  * make sure we're not stepping on any global I/O operations
564                  */
565                 di = &viocd_diskinfo[bevent->disk];
566                 spin_lock_irqsave(&viocd_reqlock, flags);
567                 dma_unmap_single(di->dev, bevent->token, bevent->len,
568                                 ((event->xSubtype & VIOMINOR_SUBTYPE_MASK) == viocdread)
569                                 ?  DMA_FROM_DEVICE : DMA_TO_DEVICE);
570                 req = (struct request *)bevent->event.xCorrelationToken;
571                 rwreq--;
572
573                 if (event->xRc != HvLpEvent_Rc_Good) {
574                         const struct vio_error_entry *err =
575                                 vio_lookup_rc(viocd_err_table,
576                                                 bevent->sub_result);
577                         printk(VIOCD_KERN_WARNING "request %p failed "
578                                         "with rc %d:0x%04X: %s\n",
579                                         req, event->xRc,
580                                         bevent->sub_result, err->msg);
581                         end_request(req, 0);
582                 } else
583                         end_request(req, 1);
584
585                 /* restart handling of incoming requests */
586                 spin_unlock_irqrestore(&viocd_reqlock, flags);
587                 blk_run_queue(viocd_queue);
588                 break;
589
590         default:
591                 printk(VIOCD_KERN_WARNING
592                                 "message with invalid subtype %0x04X!\n",
593                                 event->xSubtype & VIOMINOR_SUBTYPE_MASK);
594                 if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) {
595                         event->xRc = HvLpEvent_Rc_InvalidSubtype;
596                         HvCallEvent_ackLpEvent(event);
597                 }
598         }
599 }
600
601 static struct cdrom_device_ops viocd_dops = {
602         .open = viocd_open,
603         .release = viocd_release,
604         .media_changed = viocd_media_changed,
605         .lock_door = viocd_lock_door,
606         .generic_packet = viocd_packet,
607         .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_IOCTLS | CDC_DRIVE_STATUS | CDC_GENERIC_PACKET | CDC_CD_R | CDC_CD_RW | CDC_DVD | CDC_DVD_R | CDC_DVD_RAM | CDC_RAM
608 };
609
610 static int __init find_capability(const char *type)
611 {
612         struct capability_entry *entry;
613
614         for(entry = capability_table; entry->type; ++entry)
615                 if(!strncmp(entry->type, type, 4))
616                         break;
617         return entry->capability;
618 }
619
620 static int viocd_probe(struct vio_dev *vdev, const struct vio_device_id *id)
621 {
622         struct gendisk *gendisk;
623         int deviceno;
624         struct disk_info *d;
625         struct cdrom_device_info *c;
626         struct cdrom_info *ci;
627
628         deviceno = vdev->unit_address;
629         if (deviceno >= viocd_numdev)
630                 return -ENODEV;
631
632         d = &viocd_diskinfo[deviceno];
633         c = &d->viocd_info;
634         ci = &viocd_unitinfo[deviceno];
635
636         c->ops = &viocd_dops;
637         c->speed = 4;
638         c->capacity = 1;
639         c->handle = d;
640         c->mask = ~find_capability(ci->type);
641         sprintf(c->name, VIOCD_DEVICE "%c", 'a' + deviceno);
642
643         if (register_cdrom(c) != 0) {
644                 printk(VIOCD_KERN_WARNING "Cannot register viocd CD-ROM %s!\n",
645                                 c->name);
646                 return 0;
647         }
648         printk(VIOCD_KERN_INFO "cd %s is iSeries resource %10.10s "
649                         "type %4.4s, model %3.3s\n",
650                         c->name, ci->rsrcname, ci->type, ci->model);
651         gendisk = alloc_disk(1);
652         if (gendisk == NULL) {
653                 printk(VIOCD_KERN_WARNING "Cannot create gendisk for %s!\n",
654                                 c->name);
655                 unregister_cdrom(c);
656                 return 0;
657         }
658         gendisk->major = VIOCD_MAJOR;
659         gendisk->first_minor = deviceno;
660         strncpy(gendisk->disk_name, c->name,
661                         sizeof(gendisk->disk_name));
662         snprintf(gendisk->devfs_name, sizeof(gendisk->devfs_name),
663                         VIOCD_DEVICE_DEVFS "%d", deviceno);
664         gendisk->queue = viocd_queue;
665         gendisk->fops = &viocd_fops;
666         gendisk->flags = GENHD_FL_CD|GENHD_FL_REMOVABLE;
667         set_capacity(gendisk, 0);
668         gendisk->private_data = d;
669         d->viocd_disk = gendisk;
670         d->dev = &vdev->dev;
671         gendisk->driverfs_dev = d->dev;
672         add_disk(gendisk);
673
674         return 0;
675 }
676
677 static int viocd_remove(struct vio_dev *vdev)
678 {
679         struct disk_info *d = &viocd_diskinfo[vdev->unit_address];
680
681         if (unregister_cdrom(&d->viocd_info) != 0)
682                 printk(VIOCD_KERN_WARNING
683                                 "Cannot unregister viocd CD-ROM %s!\n",
684                                 d->viocd_info.name);
685         del_gendisk(d->viocd_disk);
686         put_disk(d->viocd_disk);
687         return 0;
688 }
689
690 /**
691  * viocd_device_table: Used by vio.c to match devices that we
692  * support.
693  */
694 static struct vio_device_id viocd_device_table[] __devinitdata = {
695         { "viocd", "" },
696         { 0, }
697 };
698
699 MODULE_DEVICE_TABLE(vio, viocd_device_table);
700 static struct vio_driver viocd_driver = {
701         .name = "viocd",
702         .id_table = viocd_device_table,
703         .probe = viocd_probe,
704         .remove = viocd_remove
705 };
706
707 static int __init viocd_init(void)
708 {
709         struct proc_dir_entry *e;
710         int ret = 0;
711
712         if (viopath_hostLp == HvLpIndexInvalid) {
713                 vio_set_hostlp();
714                 /* If we don't have a host, bail out */
715                 if (viopath_hostLp == HvLpIndexInvalid)
716                         return -ENODEV;
717         }
718
719         printk(VIOCD_KERN_INFO "vers " VIOCD_VERS ", hosting partition %d\n",
720                         viopath_hostLp);
721
722         if (register_blkdev(VIOCD_MAJOR, VIOCD_DEVICE) != 0) {
723                 printk(VIOCD_KERN_WARNING "Unable to get major %d for %s\n",
724                                 VIOCD_MAJOR, VIOCD_DEVICE);
725                 return -EIO;
726         }
727
728         ret = viopath_open(viopath_hostLp, viomajorsubtype_cdio,
729                         MAX_CD_REQ + 2);
730         if (ret) {
731                 printk(VIOCD_KERN_WARNING
732                                 "error opening path to host partition %d\n",
733                                 viopath_hostLp);
734                 goto out_unregister;
735         }
736
737         /* Initialize our request handler */
738         vio_setHandler(viomajorsubtype_cdio, vio_handle_cd_event);
739
740         get_viocd_info();
741         if (viocd_numdev == 0)
742                 goto out_undo_vio;
743
744         spin_lock_init(&viocd_reqlock);
745         viocd_queue = blk_init_queue(do_viocd_request, &viocd_reqlock);
746         if (viocd_queue == NULL) {
747                 ret = -ENOMEM;
748                 goto out_free_info;
749         }
750         blk_queue_max_hw_segments(viocd_queue, 1);
751         blk_queue_max_phys_segments(viocd_queue, 1);
752         blk_queue_max_sectors(viocd_queue, 4096 / 512);
753
754         ret = vio_register_driver(&viocd_driver);
755         if (ret)
756                 goto out_cleanup_queue;
757
758         e = create_proc_entry("iSeries/viocd", S_IFREG|S_IRUGO, NULL);
759         if (e) {
760                 e->owner = THIS_MODULE;
761                 e->proc_fops = &proc_viocd_operations;
762         }
763
764         return 0;
765
766 out_cleanup_queue:
767         blk_cleanup_queue(viocd_queue);
768 out_free_info:
769         dma_free_coherent(iSeries_vio_dev,
770                         sizeof(*viocd_unitinfo) * VIOCD_MAX_CD,
771                         viocd_unitinfo, unitinfo_dmaaddr);
772 out_undo_vio:
773         vio_clearHandler(viomajorsubtype_cdio);
774         viopath_close(viopath_hostLp, viomajorsubtype_cdio, MAX_CD_REQ + 2);
775 out_unregister:
776         unregister_blkdev(VIOCD_MAJOR, VIOCD_DEVICE);
777         return ret;
778 }
779
780 static void __exit viocd_exit(void)
781 {
782         remove_proc_entry("iSeries/viocd", NULL);
783         vio_unregister_driver(&viocd_driver);
784         blk_cleanup_queue(viocd_queue);
785         if (viocd_unitinfo != NULL)
786                 dma_free_coherent(iSeries_vio_dev,
787                                 sizeof(*viocd_unitinfo) * VIOCD_MAX_CD,
788                                 viocd_unitinfo, unitinfo_dmaaddr);
789         viopath_close(viopath_hostLp, viomajorsubtype_cdio, MAX_CD_REQ + 2);
790         vio_clearHandler(viomajorsubtype_cdio);
791         unregister_blkdev(VIOCD_MAJOR, VIOCD_DEVICE);
792 }
793
794 module_init(viocd_init);
795 module_exit(viocd_exit);
796 MODULE_LICENSE("GPL");