setup enviroment for compilation
[linux-2.4.21-pre4.git] / drivers / iseries / viocd.c
1 /* -*- linux-c -*-
2  *  drivers/cdrom/viocd.c
3  *
4  ***************************************************************************
5  *  iSeries Virtual CD Rom
6  *
7  *  Authors: Dave Boutcher <boutcher@us.ibm.com>
8  *           Ryan Arnold <ryanarn@us.ibm.com>
9  *           Colin Devilbiss <devilbis@us.ibm.com>
10  *
11  * (C) Copyright 2000 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  * This device driver can either use it's own major number, or it can
35  * pretend to be an AZTECH drive. This is controlled with a 
36  * CONFIG option.  You can either call this an elegant solution to the 
37  * fact that a lot of software doesn't recognize a new CD major number...
38  * or you can call this a really ugly hack.  Your choice.
39  *
40  */
41
42 #include <linux/major.h>
43 #include <linux/config.h>
44
45 /***************************************************************************
46  * Decide if we are using our own major or pretending to be an AZTECH drive
47  ***************************************************************************/
48 #ifdef CONFIG_VIOCD_AZTECH
49 #define MAJOR_NR AZTECH_CDROM_MAJOR
50 #define do_viocd_request do_aztcd_request
51 #else
52 #define MAJOR_NR VIOCD_MAJOR
53 #endif
54
55 #define VIOCD_VERS "1.04"
56
57 #include <linux/blk.h>
58 #include <linux/cdrom.h>
59 #include <linux/errno.h>
60 #include <linux/init.h>
61 #include <linux/pci.h>
62 #include <linux/proc_fs.h>
63 #include <linux/module.h>
64
65 #include <asm/iSeries/HvTypes.h>
66 #include <asm/iSeries/HvLpEvent.h>
67 #include "vio.h"
68 #include <asm/iSeries/iSeries_proc.h>
69
70 extern struct pci_dev * iSeries_vio_dev;
71
72 #define signalLpEvent HvCallEvent_signalLpEventFast
73
74 struct viocdlpevent {
75         struct HvLpEvent event;
76         u32 mReserved1;
77         u16 mVersion;
78         u16 mSubTypeRc;
79         u16 mDisk;
80         u16 mFlags;
81         u32 mToken;
82         u64 mOffset;            // On open, the max number of disks
83         u64 mLen;               // On open, the size of the disk
84         u32 mBlockSize;         // Only set on open
85         u32 mMediaSize;         // Only set on open
86 };
87
88 enum viocdsubtype {
89         viocdopen = 0x0001,
90         viocdclose = 0x0002,
91         viocdread = 0x0003,
92         viocdwrite = 0x0004,
93         viocdlockdoor = 0x0005,
94         viocdgetinfo = 0x0006,
95         viocdcheck = 0x0007
96 };
97
98 /* Should probably make this a module parameter....sigh
99  */
100 #define VIOCD_MAX_CD 8
101 int viocd_blocksizes[VIOCD_MAX_CD];
102 static u64 viocd_size_in_bytes[VIOCD_MAX_CD];
103
104 /* This is the structure we use to exchange info between driver and interrupt
105  * handler
106  */
107 struct viocd_waitevent {
108         struct semaphore *sem;
109         int rc;
110         int changed;
111 };
112
113 /* this is a lookup table for the true capabilities of a device */
114 struct capability_entry {
115         char *type;
116         int capability;
117 };
118
119 static struct capability_entry capability_table[] = {
120         { "6330", CDC_LOCK | CDC_DVD_RAM },
121         { "6321", CDC_LOCK },
122         { "632B", 0 },
123         { NULL  , CDC_LOCK },
124 };
125
126 struct block_device_operations viocd_fops =
127 {
128         owner:                  THIS_MODULE,
129         open:                   cdrom_open,
130         release:                cdrom_release,
131         ioctl:                  cdrom_ioctl,
132         check_media_change:     cdrom_media_changed,
133 };
134
135 /* These are our internal structures for keeping track of devices
136  */
137 static int viocd_numdev;
138
139 struct cdrom_info {
140         char rsrcname[10];
141         char type[4];
142         char model[3];
143 };
144 static struct cdrom_info *viocd_unitinfo = NULL;
145
146 struct disk_info{
147         u32 useCount;
148         u32 blocksize;
149         u32 mediasize;
150 };
151 static struct disk_info viocd_diskinfo[VIOCD_MAX_CD];
152
153 static struct cdrom_device_info viocd_info[VIOCD_MAX_CD];
154
155 static spinlock_t viocd_lock = SPIN_LOCK_UNLOCKED;
156
157 #define MAX_CD_REQ 1
158 static LIST_HEAD(reqlist);
159
160 /* End a request
161  */
162 static int viocd_end_request(struct request *req, int uptodate)
163 {
164         if (end_that_request_first(req, uptodate, DEVICE_NAME))
165                 return 0;
166         end_that_request_last(req);
167         return 1;
168 }
169
170
171 /* Get info on CD devices from OS/400
172  */
173 static void get_viocd_info(void)
174 {
175         dma_addr_t dmaaddr;
176         HvLpEvent_Rc hvrc;
177         int i;
178         DECLARE_MUTEX_LOCKED(Semaphore);
179         struct viocd_waitevent we;
180
181         // If we don't have a host, bail out
182         if (viopath_hostLp == HvLpIndexInvalid)
183                 return;
184
185         if (viocd_unitinfo == NULL)
186                 viocd_unitinfo =
187                     kmalloc(sizeof(struct cdrom_info) * VIOCD_MAX_CD,
188                             GFP_KERNEL);
189
190         memset(viocd_unitinfo, 0x00,
191                sizeof(struct cdrom_info) * VIOCD_MAX_CD);
192
193         dmaaddr = pci_map_single(iSeries_vio_dev, viocd_unitinfo,
194                                  sizeof(struct cdrom_info) * VIOCD_MAX_CD,
195                                  PCI_DMA_FROMDEVICE);
196         if (dmaaddr == 0xFFFFFFFF) {
197                 printk(KERN_WARNING_VIO "error allocating tce\n");
198                 return;
199         }
200
201         we.sem = &Semaphore;
202
203         hvrc = signalLpEvent(viopath_hostLp,
204                              HvLpEvent_Type_VirtualIo,
205                              viomajorsubtype_cdio | viocdgetinfo,
206                              HvLpEvent_AckInd_DoAck,
207                              HvLpEvent_AckType_ImmediateAck,
208                              viopath_sourceinst(viopath_hostLp),
209                              viopath_targetinst(viopath_hostLp),
210                              (u64) (unsigned long) &we,
211                              VIOVERSION << 16,
212                              dmaaddr,
213                              0,
214                              sizeof(struct cdrom_info) * VIOCD_MAX_CD,
215                              0);
216         if (hvrc != HvLpEvent_Rc_Good) {
217                 printk(KERN_WARNING_VIO "cdrom error sending event. rc %d\n", (int) hvrc);
218                 return;
219         }
220
221         down(&Semaphore);
222
223         if (we.rc) {
224                 printk(KERN_WARNING_VIO "bad rc %d on getinfo\n", we.rc);
225                 return;
226         }
227
228
229         for (i = 0; (i < VIOCD_MAX_CD) && (viocd_unitinfo[i].rsrcname[0]); i++) {
230                 viocd_numdev++;
231         }
232 }
233
234 /* Open a device
235  */
236 static int viocd_open(struct cdrom_device_info *cdi, int purpose)
237 {
238         DECLARE_MUTEX_LOCKED(Semaphore);
239         int device_no = MINOR(cdi->dev);
240         HvLpEvent_Rc hvrc;
241         struct viocd_waitevent we;
242         struct disk_info *diskinfo = &viocd_diskinfo[device_no];
243
244         // If we don't have a host, bail out
245         if (viopath_hostLp == HvLpIndexInvalid || device_no >= viocd_numdev)
246                 return -ENODEV;
247
248         we.sem = &Semaphore;
249         hvrc = signalLpEvent(viopath_hostLp,
250                              HvLpEvent_Type_VirtualIo,
251                              viomajorsubtype_cdio | viocdopen,
252                              HvLpEvent_AckInd_DoAck,
253                              HvLpEvent_AckType_ImmediateAck,
254                              viopath_sourceinst(viopath_hostLp),
255                              viopath_targetinst(viopath_hostLp),
256                              (u64) (unsigned long) &we,
257                              VIOVERSION << 16,
258                              ((u64) device_no << 48),
259                              0, 0, 0);
260         if (hvrc != 0) {
261                 printk(KERN_WARNING_VIO "bad rc on signalLpEvent %d\n",
262                        (int) hvrc);
263                 return -EIO;
264         }
265
266         down(&Semaphore);
267
268         if (we.rc)
269                 return -EIO;
270
271         if (diskinfo->useCount == 0) {
272                 if(diskinfo->blocksize > 0) {
273                         viocd_blocksizes[device_no] = diskinfo->blocksize;
274                         viocd_size_in_bytes[device_no] = diskinfo->blocksize * diskinfo->mediasize;
275                 } else {
276                         viocd_size_in_bytes[device_no] = 0xFFFFFFFFFFFFFFFF;
277                 }
278         }
279         MOD_INC_USE_COUNT;
280         return 0;
281 }
282
283 /* Release a device
284  */
285 static void viocd_release(struct cdrom_device_info *cdi)
286 {
287         int device_no = MINOR(cdi->dev);
288         HvLpEvent_Rc hvrc;
289
290         /* If we don't have a host, bail out */
291         if (viopath_hostLp == HvLpIndexInvalid
292             || device_no >= viocd_numdev)
293                 return;
294
295         hvrc = signalLpEvent(viopath_hostLp,
296                              HvLpEvent_Type_VirtualIo,
297                              viomajorsubtype_cdio | viocdclose,
298                              HvLpEvent_AckInd_NoAck,
299                              HvLpEvent_AckType_ImmediateAck,
300                              viopath_sourceinst(viopath_hostLp),
301                              viopath_targetinst(viopath_hostLp),
302                              0,
303                              VIOVERSION << 16,
304                              ((u64) device_no << 48),
305                              0, 0, 0);
306         if (hvrc != 0) {
307                 printk(KERN_WARNING_VIO "bad rc on signalLpEvent %d\n", (int) hvrc);
308                 return;
309         }
310
311         MOD_DEC_USE_COUNT;
312 }
313
314 /* Send a read or write request to OS/400
315  */
316 static int send_request(struct request *req)
317 {
318         HvLpEvent_Rc hvrc;
319         dma_addr_t dmaaddr;
320         int device_no = DEVICE_NR(req->rq_dev);
321         u64 start = req->sector * 512,
322             len = req->current_nr_sectors * 512;
323         char reading = req->cmd == READ;
324         u16 command = reading ? viocdread : viocdwrite;
325
326
327         if(start + len > viocd_size_in_bytes[device_no]) {
328                 printk(KERN_WARNING_VIO "viocd%d; access position %lx, past size %lx\n",
329                        device_no, (unsigned long)(start + len), (unsigned long)viocd_size_in_bytes[device_no]);
330                 return -1;
331         }
332         
333         dmaaddr = pci_map_single(iSeries_vio_dev, req->buffer, len,
334                                  reading ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);
335         if (dmaaddr == 0xFFFFFFFF) {
336                 printk(KERN_WARNING_VIO "error allocating tce for address %p len %ld\n",
337                            req->buffer, (long)len);
338                 return -1;
339         }
340
341 /* FIXME: experimental
342 // note the special use of mReserved1 to indicate that a response
343 //   is desired, but via NoAck...
344 //                           HvLpEvent_AckInd_NoAck,
345 //                           ((u64)0x00000001 << 32) | ((u64)VIOVERSION << 16),
346 */
347
348         hvrc = signalLpEvent(viopath_hostLp,
349                              HvLpEvent_Type_VirtualIo,
350                              viomajorsubtype_cdio | command,
351                              HvLpEvent_AckInd_DoAck,
352                              HvLpEvent_AckType_ImmediateAck,
353                              viopath_sourceinst(viopath_hostLp),
354                              viopath_targetinst(viopath_hostLp),
355                              (u64) (unsigned long) req->buffer,
356                              VIOVERSION << 16,
357                              ((u64) device_no << 48) | dmaaddr,
358                              start, len, 0);
359         if (hvrc != HvLpEvent_Rc_Good) {
360                 printk(KERN_WARNING_VIO "hv error on op %d\n", (int) hvrc);
361                 return -1;
362         }
363
364         return 0;
365 }
366
367
368 /* Do a request
369  */
370 static int rwreq;
371 static void do_viocd_request(request_queue_t * q)
372 {
373         for (;;) {
374                 struct request *req;
375                 char err_str[80] = "";
376                 int device_no;
377
378                 INIT_REQUEST;
379                 if (rwreq >= MAX_CD_REQ) {
380                         return;
381                 }
382
383                 device_no = CURRENT_DEV;
384
385                 /* remove the current request from the queue */
386                 req = CURRENT;
387                 blkdev_dequeue_request(req);
388
389                 /* check for any kind of error */
390                 if (device_no > viocd_numdev)
391                         sprintf(err_str, "Invalid device number %d", device_no);
392                 else if (send_request(req) < 0)
393                         strcpy(err_str, "unable to send message to OS/400!");
394
395                 /* if we had any sort of error, log it and cancel the request */
396                 if (*err_str) {
397                         printk(KERN_WARNING_VIO "%s\n", err_str);
398                         viocd_end_request(req, 0);
399                 } else {
400                         spin_lock(&viocd_lock);
401                         list_add_tail(&req->queue, &reqlist);
402                         ++rwreq;
403                         spin_unlock(&viocd_lock);
404                 }
405         }
406 }
407
408 /* Check if the CD changed
409  */
410 static int viocd_media_changed(struct cdrom_device_info *cdi, int disc_nr)
411 {
412         struct viocd_waitevent we;
413         HvLpEvent_Rc hvrc;
414         int device_no = MINOR(cdi->dev);
415
416         /* This semaphore is raised in the interrupt handler                     */
417         DECLARE_MUTEX_LOCKED(Semaphore);
418
419         /* Check that we are dealing with a valid hosting partition              */
420         if (viopath_hostLp == HvLpIndexInvalid) {
421                 printk(KERN_WARNING_VIO "Invalid hosting partition\n");
422                 return -EIO;
423         }
424
425         we.sem = &Semaphore;
426
427         /* Send the open event to OS/400                                         */
428         hvrc = signalLpEvent(viopath_hostLp,
429                              HvLpEvent_Type_VirtualIo,
430                              viomajorsubtype_cdio | viocdcheck,
431                              HvLpEvent_AckInd_DoAck,
432                              HvLpEvent_AckType_ImmediateAck,
433                              viopath_sourceinst(viopath_hostLp),
434                              viopath_targetinst(viopath_hostLp),
435                              (u64) (unsigned long) &we,
436                              VIOVERSION << 16,
437                              ((u64) device_no << 48),
438                              0, 0, 0);
439
440         if (hvrc != 0) {
441                 printk(KERN_WARNING_VIO "bad rc on signalLpEvent %d\n", (int) hvrc);
442                 return -EIO;
443         }
444
445         /* Wait for the interrupt handler to get the response                    */
446         down(&Semaphore);
447
448         /* Check the return code.  If bad, assume no change                      */
449         if (we.rc != 0) {
450                 printk(KERN_WARNING_VIO "bad rc on check_change. Assuming no change\n");
451                 return 0;
452         }
453
454         return we.changed;
455 }
456
457 static int viocd_lock_door(struct cdrom_device_info *cdi, int locking)
458 {
459         HvLpEvent_Rc hvrc;
460         u64 device_no = MINOR(cdi->dev);
461         /* NOTE: flags is 1 or 0 so it won't overwrite the device_no             */
462         u64 flags = !!locking;
463         /* This semaphore is raised in the interrupt handler                     */
464         DECLARE_MUTEX_LOCKED(Semaphore);
465         struct viocd_waitevent we = { sem:&Semaphore };
466
467         /* Check that we are dealing with a valid hosting partition              */
468         if (viopath_hostLp == HvLpIndexInvalid) {
469                 printk(KERN_WARNING_VIO "Invalid hosting partition\n");
470                 return -EIO;
471         }
472
473         we.sem = &Semaphore;
474
475         /* Send the lockdoor event to OS/400                                     */
476         hvrc = signalLpEvent(viopath_hostLp,
477                              HvLpEvent_Type_VirtualIo,
478                              viomajorsubtype_cdio | viocdlockdoor,
479                              HvLpEvent_AckInd_DoAck,
480                              HvLpEvent_AckType_ImmediateAck,
481                              viopath_sourceinst(viopath_hostLp),
482                              viopath_targetinst(viopath_hostLp),
483                              (u64) (unsigned long) &we,
484                              VIOVERSION << 16,
485                              (device_no << 48) | (flags << 32),
486                              0, 0, 0);
487
488         if (hvrc != 0) {
489                 printk(KERN_WARNING_VIO "bad rc on signalLpEvent %d\n", (int) hvrc);
490                 return -EIO;
491         }
492
493         /* Wait for the interrupt handler to get the response                    */
494         down(&Semaphore);
495
496         /* Check the return code.  If bad, assume no change                      */
497         if (we.rc != 0) {
498                 return -EIO;
499         }
500
501         return 0;
502 }
503
504 /* This routine handles incoming CD LP events
505  */
506 static void vioHandleCDEvent(struct HvLpEvent *event)
507 {
508         struct viocdlpevent *bevent = (struct viocdlpevent *) event;
509         struct viocd_waitevent *pwe;
510
511         if (event == NULL) {
512                 /* Notification that a partition went away! */
513                 return;
514         }
515         /* First, we should NEVER get an int here...only acks */
516         if (event->xFlags.xFunction == HvLpEvent_Function_Int) {
517                 printk(KERN_WARNING_VIO "Yikes! got an int in viocd event handler!\n");
518                 if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) {
519                         event->xRc = HvLpEvent_Rc_InvalidSubtype;
520                         HvCallEvent_ackLpEvent(event);
521                 }
522         }
523
524         switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
525         case viocdopen:
526                 viocd_diskinfo[bevent->mDisk].blocksize = bevent->mBlockSize;
527                 viocd_diskinfo[bevent->mDisk].mediasize = bevent->mMediaSize;
528                 /* FALLTHROUGH !! */
529         case viocdgetinfo:
530         case viocdlockdoor:
531                 pwe = (struct viocd_waitevent *) (unsigned long) event->xCorrelationToken;
532                 pwe->rc = event->xRc;
533                 up(pwe->sem);
534                 break;
535
536         case viocdclose:
537                 break;
538
539         case viocdwrite:
540         case viocdread:{
541                 unsigned long flags;
542                 int reading = ((event->xSubtype & VIOMINOR_SUBTYPE_MASK) == viocdread);
543                 struct request *req = blkdev_entry_to_request(reqlist.next);
544                 /* Since this is running in interrupt mode, we need to make sure we're not
545                  * stepping on any global I/O operations
546                  */
547                 spin_lock_irqsave(&io_request_lock, flags);
548
549                 pci_unmap_single(iSeries_vio_dev,
550                                  bevent->mToken,
551                                  bevent->mLen,
552                                  reading ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);
553
554                 /* find the event to which this is a response */
555                 while ((&req->queue != &reqlist) &&
556                        ((u64) (unsigned long) req->buffer != bevent->event.xCorrelationToken))
557                         req = blkdev_entry_to_request(req->queue.next);
558
559                 /* if the event was not there, then what are we responding to?? */
560                 if (&req->queue == &reqlist) {
561                         printk(KERN_WARNING_VIO "Yikes! we didn't ever enqueue this guy!\n");
562                         spin_unlock_irqrestore(&io_request_lock,
563                                                flags);
564                         break;
565                 }
566
567                 /* we don't need to keep it around anymore... */
568                 spin_lock(&viocd_lock);
569                 list_del(&req->queue);
570                 --rwreq;
571                 spin_unlock(&viocd_lock);
572                 {
573                         char stat = event->xRc == HvLpEvent_Rc_Good;
574                         int nsect = bevent->mLen >> 9;
575
576                         if (!stat)
577                         printk(KERN_WARNING_VIO
578                                "request %p failed with rc %d:0x%08x\n",
579                                req->buffer, event->xRc, bevent->mSubTypeRc);
580                         while ((nsect > 0) && (req->bh)) {
581                                 nsect -= req->current_nr_sectors;
582                                 viocd_end_request(req, stat);
583                         }
584                         /* we weren't done yet */
585                         if (req->bh) {
586                                 if (send_request(req) < 0) {
587                                         printk(KERN_WARNING_VIO
588                                             "couldn't re-submit req %p\n", req->buffer);
589                                         viocd_end_request(req, 0);
590                                 } else {
591                                         spin_lock(&viocd_lock);
592                                         list_add_tail(&req->queue, &reqlist);
593                                         ++rwreq;
594                                         spin_unlock(&viocd_lock);
595                                 }
596                         }
597                 }
598
599                 /* restart handling of incoming requests */
600                 do_viocd_request(NULL);
601                 spin_unlock_irqrestore(&io_request_lock, flags);
602                 break;
603         }
604         case viocdcheck:
605                 pwe = (struct viocd_waitevent *) (unsigned long) event->xCorrelationToken;
606                 pwe->rc = event->xRc;
607                 pwe->changed = bevent->mFlags;
608                 up(pwe->sem);
609                 break;
610
611         default:
612                 printk(KERN_WARNING_VIO "invalid subtype!");
613                 if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) {
614                         event->xRc = HvLpEvent_Rc_InvalidSubtype;
615                         HvCallEvent_ackLpEvent(event);
616                 }
617         }
618 }
619
620 /* Our file operations table
621  */
622 static struct cdrom_device_ops viocd_dops = {
623         open:viocd_open,
624         release:viocd_release,
625         media_changed:viocd_media_changed,
626         lock_door:viocd_lock_door,
627         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
628 };
629
630 /* Handle reads from the proc file system
631  */
632 static int proc_read(char *buf, char **start, off_t offset,
633                      int blen, int *eof, void *data)
634 {
635         int len = 0;
636         int i;
637
638         for (i = 0; i < viocd_numdev; i++) {
639                 len +=
640                     sprintf(buf + len,
641                             "viocd device %d is iSeries resource %10.10s type %4.4s, model %3.3s\n",
642                             i, viocd_unitinfo[i].rsrcname,
643                             viocd_unitinfo[i].type,
644                             viocd_unitinfo[i].model);
645         }
646         *eof = 1;
647         return len;
648 }
649
650
651 /* setup our proc file system entries
652  */
653 void viocd_proc_init(struct proc_dir_entry *iSeries_proc)
654 {
655         struct proc_dir_entry *ent;
656         ent = create_proc_entry("viocd", S_IFREG | S_IRUSR, iSeries_proc);
657         if (!ent)
658                 return;
659         ent->nlink = 1;
660         ent->data = NULL;
661         ent->read_proc = proc_read;
662 }
663
664 /* clean up our proc file system entries
665  */
666 void viocd_proc_delete(struct proc_dir_entry *iSeries_proc)
667 {
668         remove_proc_entry("viocd", iSeries_proc);
669 }
670
671 static int find_capability(const char *type)
672 {
673         struct capability_entry *entry;
674         for(entry = capability_table; entry->type; ++entry)
675                 if(!strncmp(entry->type, type, 4))
676                         break;
677         return entry->capability;
678 }
679
680 /* Initialize the whole device driver.  Handle module and non-module
681  * versions
682  */
683 __init int viocd_init(void)
684 {
685         int i, rc;
686
687         if (viopath_hostLp == HvLpIndexInvalid)
688                 vio_set_hostlp();
689
690         /* If we don't have a host, bail out */
691         if (viopath_hostLp == HvLpIndexInvalid)
692                 return -ENODEV;
693
694         rc = viopath_open(viopath_hostLp, viomajorsubtype_cdio, MAX_CD_REQ+2);
695         if (rc) {
696                 printk(KERN_WARNING_VIO "error opening path to host partition %d\n",
697                            viopath_hostLp);
698                 return rc;
699         }
700
701         /* Initialize our request handler
702          */
703         rwreq = 0;
704         vio_setHandler(viomajorsubtype_cdio, vioHandleCDEvent);
705
706         memset(&viocd_diskinfo, 0x00, sizeof(viocd_diskinfo));
707
708         get_viocd_info();
709
710         if (viocd_numdev == 0) {
711                 vio_clearHandler(viomajorsubtype_cdio);
712                 viopath_close(viopath_hostLp, viomajorsubtype_cdio, MAX_CD_REQ+2);
713                 return 0;
714         }
715
716         printk(KERN_INFO_VIO
717                "%s: iSeries Virtual CD vers %s, major %d, max disks %d, hosting partition %d\n",
718                DEVICE_NAME, VIOCD_VERS, MAJOR_NR, VIOCD_MAX_CD, viopath_hostLp);
719
720         if (devfs_register_blkdev(MAJOR_NR, "viocd", &viocd_fops) != 0) {
721                 printk(KERN_WARNING_VIO "Unable to get major %d for viocd CD-ROM\n", MAJOR_NR);
722                 return -EIO;
723         }
724
725         blksize_size[MAJOR_NR] = viocd_blocksizes;
726         blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
727         read_ahead[MAJOR_NR] = 4;
728
729         memset(&viocd_info, 0x00, sizeof(viocd_info));
730         for (i = 0; i < viocd_numdev; i++) {
731                 viocd_info[i].dev = MKDEV(MAJOR_NR, i);
732                 viocd_info[i].ops = &viocd_dops;
733                 viocd_info[i].speed = 4;
734                 viocd_info[i].capacity = 1;
735                 viocd_info[i].mask = ~find_capability(viocd_unitinfo[i].type);
736                 sprintf(viocd_info[i].name, "viocd%d", i);
737                 if (register_cdrom(&viocd_info[i]) != 0) {
738                         printk(KERN_WARNING_VIO "Cannot register viocd CD-ROM %s!\n", viocd_info[i].name);
739                 } else {
740                         printk(KERN_INFO_VIO 
741                                "cd %s is iSeries resource %10.10s type %4.4s, model %3.3s\n",
742                                viocd_info[i].name,
743                                viocd_unitinfo[i].rsrcname,
744                                viocd_unitinfo[i].type,
745                                viocd_unitinfo[i].model);
746                 }
747         }
748
749         /* 
750          * Create the proc entry
751          */
752         iSeries_proc_callback(&viocd_proc_init);
753
754         return 0;
755 }
756
757 #ifdef MODULE
758 void viocd_exit(void)
759 {
760         int i;
761         for (i = 0; i < viocd_numdev; i++) {
762                 if (unregister_cdrom(&viocd_info[i]) != 0) {
763                         printk(KERN_WARNING_VIO "Cannot unregister viocd CD-ROM %s!\n", viocd_info[i].name);
764                 }
765         }
766         if ((devfs_unregister_blkdev(MAJOR_NR, "viocd") == -EINVAL)) {
767                 printk(KERN_WARNING_VIO "can't unregister viocd\n");
768                 return;
769         }
770         blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
771         if (viocd_unitinfo)
772                 kfree(viocd_unitinfo);
773
774         iSeries_proc_callback(&viocd_proc_delete);
775
776         viopath_close(viopath_hostLp, viomajorsubtype_cdio, MAX_CD_REQ+2);
777         vio_clearHandler(viomajorsubtype_cdio);
778 }
779 #endif
780
781 #ifdef MODULE
782 module_init(viocd_init);
783 module_exit(viocd_exit);
784 #endif