OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [cdrom/] [viocd.c] - Blame information for rev 81

Go to most recent revision | Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.