X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fcdrom%2Fviocd.c;h=93fbf84dcc4a5d48dbd09d34d4ceb6cd3807ea20;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=9d2759d8aa9212e786d292af5d4a614eefb64a09;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/drivers/cdrom/viocd.c b/drivers/cdrom/viocd.c index 9d2759d8a..93fbf84dc 100644 --- a/drivers/cdrom/viocd.c +++ b/drivers/cdrom/viocd.c @@ -42,15 +42,14 @@ #include #include -#include - +#include #include -#include -#include -#include +#include +#include +#include +#include #define VIOCD_DEVICE "iseries/vcd" -#define VIOCD_DEVICE_DEVFS "iseries/vcd" #define VIOCD_VERS "1.06" @@ -84,7 +83,7 @@ enum viocdsubtype { /* * Should probably make this a module parameter....sigh */ -#define VIOCD_MAX_CD 8 +#define VIOCD_MAX_CD HVMAXARCHITECTEDVIRTUALCDROMS static const struct vio_error_entry viocd_err_table[] = { {0x0201, EINVAL, "Invalid Range"}, @@ -120,7 +119,10 @@ struct capability_entry { }; static struct capability_entry capability_table[] __initdata = { - { "6330", CDC_LOCK | CDC_DVD_RAM }, + { "6330", CDC_LOCK | CDC_DVD_RAM | CDC_RAM }, + { "6331", CDC_LOCK | CDC_DVD_RAM | CDC_RAM }, + { "6333", CDC_LOCK | CDC_DVD_RAM | CDC_RAM }, + { "632A", CDC_LOCK | CDC_DVD_RAM | CDC_RAM }, { "6321", CDC_LOCK }, { "632B", 0 }, { NULL , CDC_LOCK }, @@ -144,12 +146,12 @@ static dma_addr_t unitinfo_dmaaddr; struct disk_info { struct gendisk *viocd_disk; struct cdrom_device_info viocd_info; + struct device *dev; }; static struct disk_info viocd_diskinfo[VIOCD_MAX_CD]; #define DEVICE_NR(di) ((di) - &viocd_diskinfo[0]) -static request_queue_t *viocd_queue; static spinlock_t viocd_reqlock; #define MAX_CD_REQ 1 @@ -197,7 +199,7 @@ static int viocd_blk_ioctl(struct inode *inode, struct file *file, unsigned cmd, unsigned long arg) { struct disk_info *di = inode->i_bdev->bd_disk->private_data; - return cdrom_ioctl(&di->viocd_info, inode, cmd, arg); + return cdrom_ioctl(file, &di->viocd_info, inode, cmd, arg); } static int viocd_blk_media_changed(struct gendisk *disk) @@ -260,13 +262,13 @@ static void __init get_viocd_info(void) for (i = 0; (i < VIOCD_MAX_CD) && viocd_unitinfo[i].rsrcname[0]; i++) viocd_numdev++; - return; - error_ret: - dma_free_coherent(iSeries_vio_dev, - sizeof(*viocd_unitinfo) * VIOCD_MAX_CD, - viocd_unitinfo, unitinfo_dmaaddr); - viocd_unitinfo = NULL; + if (viocd_numdev == 0) { + dma_free_coherent(iSeries_vio_dev, + sizeof(*viocd_unitinfo) * VIOCD_MAX_CD, + viocd_unitinfo, unitinfo_dmaaddr); + viocd_unitinfo = NULL; + } } static int viocd_open(struct cdrom_device_info *cdi, int purpose) @@ -330,10 +332,19 @@ static int send_request(struct request *req) struct disk_info *diskinfo = req->rq_disk->private_data; u64 len; dma_addr_t dmaaddr; + int direction; + u16 cmd; struct scatterlist sg; BUG_ON(req->nr_phys_segments > 1); - BUG_ON(rq_data_dir(req) != READ); + + if (rq_data_dir(req) == READ) { + direction = DMA_FROM_DEVICE; + cmd = viomajorsubtype_cdio | viocdread; + } else { + direction = DMA_TO_DEVICE; + cmd = viomajorsubtype_cdio | viocdwrite; + } if (blk_rq_map_sg(req->q, req, &sg) == 0) { printk(VIOCD_KERN_WARNING @@ -341,7 +352,7 @@ static int send_request(struct request *req) return -1; } - if (dma_map_sg(iSeries_vio_dev, &sg, 1, DMA_FROM_DEVICE) == 0) { + if (dma_map_sg(diskinfo->dev, &sg, 1, direction) == 0) { printk(VIOCD_KERN_WARNING "error allocating sg tce\n"); return -1; } @@ -349,8 +360,7 @@ static int send_request(struct request *req) len = sg_dma_len(&sg); hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_cdio | viocdread, + HvLpEvent_Type_VirtualIo, cmd, HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, viopath_sourceinst(viopath_hostLp), @@ -455,6 +465,67 @@ static int viocd_lock_door(struct cdrom_device_info *cdi, int locking) return 0; } +static int viocd_packet(struct cdrom_device_info *cdi, + struct packet_command *cgc) +{ + unsigned int buflen = cgc->buflen; + int ret = -EIO; + + switch (cgc->cmd[0]) { + case GPCMD_READ_DISC_INFO: + { + disc_information *di = (disc_information *)cgc->buffer; + + if (buflen >= 2) { + di->disc_information_length = cpu_to_be16(1); + ret = 0; + } + if (buflen >= 3) + di->erasable = + (cdi->ops->capability & ~cdi->mask + & (CDC_DVD_RAM | CDC_RAM)) != 0; + } + break; + case GPCMD_GET_CONFIGURATION: + if (cgc->cmd[3] == CDF_RWRT) { + struct rwrt_feature_desc *rfd = (struct rwrt_feature_desc *)(cgc->buffer + sizeof(struct feature_header)); + + if ((buflen >= + (sizeof(struct feature_header) + sizeof(*rfd))) && + (cdi->ops->capability & ~cdi->mask + & (CDC_DVD_RAM | CDC_RAM))) { + rfd->feature_code = cpu_to_be16(CDF_RWRT); + rfd->curr = 1; + ret = 0; + } + } + break; + default: + if (cgc->sense) { + /* indicate Unknown code */ + cgc->sense->sense_key = 0x05; + cgc->sense->asc = 0x20; + cgc->sense->ascq = 0x00; + } + break; + } + + cgc->stat = ret; + return ret; +} + +static void restart_all_queues(int first_index) +{ + int i; + + for (i = first_index + 1; i < viocd_numdev; i++) + if (viocd_diskinfo[i].viocd_disk) + blk_run_queue(viocd_diskinfo[i].viocd_disk->queue); + for (i = 0; i <= first_index; i++) + if (viocd_diskinfo[i].viocd_disk) + blk_run_queue(viocd_diskinfo[i].viocd_disk->queue); +} + /* This routine handles incoming CD LP events */ static void vio_handle_cd_event(struct HvLpEvent *event) { @@ -469,10 +540,10 @@ static void vio_handle_cd_event(struct HvLpEvent *event) /* Notification that a partition went away! */ return; /* First, we should NEVER get an int here...only acks */ - if (event->xFlags.xFunction == HvLpEvent_Function_Int) { + if (hvlpevent_is_int(event)) { printk(VIOCD_KERN_WARNING "Yikes! got an int in viocd event handler!\n"); - if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) { + if (hvlpevent_need_ack(event)) { event->xRc = HvLpEvent_Rc_InvalidSubtype; HvCallEvent_ackLpEvent(event); } @@ -484,7 +555,7 @@ static void vio_handle_cd_event(struct HvLpEvent *event) case viocdopen: if (event->xRc == 0) { di = &viocd_diskinfo[bevent->disk]; - blk_queue_hardsect_size(viocd_queue, + blk_queue_hardsect_size(di->viocd_disk->queue, bevent->block_size); set_capacity(di->viocd_disk, bevent->media_size * @@ -508,14 +579,17 @@ return_complete: case viocdclose: break; + case viocdwrite: case viocdread: /* * Since this is running in interrupt mode, we need to * make sure we're not stepping on any global I/O operations */ + di = &viocd_diskinfo[bevent->disk]; spin_lock_irqsave(&viocd_reqlock, flags); - dma_unmap_single(iSeries_vio_dev, bevent->token, bevent->len, - DMA_FROM_DEVICE); + dma_unmap_single(di->dev, bevent->token, bevent->len, + ((event->xSubtype & VIOMINOR_SUBTYPE_MASK) == viocdread) + ? DMA_FROM_DEVICE : DMA_TO_DEVICE); req = (struct request *)bevent->event.xCorrelationToken; rwreq--; @@ -533,14 +607,14 @@ return_complete: /* restart handling of incoming requests */ spin_unlock_irqrestore(&viocd_reqlock, flags); - blk_run_queue(viocd_queue); + restart_all_queues(bevent->disk); break; default: printk(VIOCD_KERN_WARNING "message with invalid subtype %0x04X!\n", event->xSubtype & VIOMINOR_SUBTYPE_MASK); - if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) { + if (hvlpevent_need_ack(event)) { event->xRc = HvLpEvent_Rc_InvalidSubtype; HvCallEvent_ackLpEvent(event); } @@ -552,7 +626,8 @@ static struct cdrom_device_ops viocd_dops = { .release = viocd_release, .media_changed = viocd_media_changed, .lock_door = viocd_lock_door, - .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 + .generic_packet = viocd_packet, + .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 }; static int __init find_capability(const char *type) @@ -565,12 +640,117 @@ static int __init find_capability(const char *type) return entry->capability; } -static int __init viocd_init(void) +static int viocd_probe(struct vio_dev *vdev, const struct vio_device_id *id) { struct gendisk *gendisk; int deviceno; - int ret = 0; + struct disk_info *d; + struct cdrom_device_info *c; + struct cdrom_info *ci; + struct request_queue *q; + + deviceno = vdev->unit_address; + if (deviceno >= viocd_numdev) + return -ENODEV; + + d = &viocd_diskinfo[deviceno]; + c = &d->viocd_info; + ci = &viocd_unitinfo[deviceno]; + + c->ops = &viocd_dops; + c->speed = 4; + c->capacity = 1; + c->handle = d; + c->mask = ~find_capability(ci->type); + sprintf(c->name, VIOCD_DEVICE "%c", 'a' + deviceno); + + if (register_cdrom(c) != 0) { + printk(VIOCD_KERN_WARNING "Cannot register viocd CD-ROM %s!\n", + c->name); + goto out; + } + printk(VIOCD_KERN_INFO "cd %s is iSeries resource %10.10s " + "type %4.4s, model %3.3s\n", + c->name, ci->rsrcname, ci->type, ci->model); + q = blk_init_queue(do_viocd_request, &viocd_reqlock); + if (q == NULL) { + printk(VIOCD_KERN_WARNING "Cannot allocate queue for %s!\n", + c->name); + goto out_unregister_cdrom; + } + gendisk = alloc_disk(1); + if (gendisk == NULL) { + printk(VIOCD_KERN_WARNING "Cannot create gendisk for %s!\n", + c->name); + goto out_cleanup_queue; + } + gendisk->major = VIOCD_MAJOR; + gendisk->first_minor = deviceno; + strncpy(gendisk->disk_name, c->name, + sizeof(gendisk->disk_name)); + blk_queue_max_hw_segments(q, 1); + blk_queue_max_phys_segments(q, 1); + blk_queue_max_sectors(q, 4096 / 512); + gendisk->queue = q; + gendisk->fops = &viocd_fops; + gendisk->flags = GENHD_FL_CD|GENHD_FL_REMOVABLE; + set_capacity(gendisk, 0); + gendisk->private_data = d; + d->viocd_disk = gendisk; + d->dev = &vdev->dev; + gendisk->driverfs_dev = d->dev; + add_disk(gendisk); + return 0; + +out_cleanup_queue: + blk_cleanup_queue(q); +out_unregister_cdrom: + unregister_cdrom(c); +out: + return -ENODEV; +} + +static int viocd_remove(struct vio_dev *vdev) +{ + struct disk_info *d = &viocd_diskinfo[vdev->unit_address]; + + if (unregister_cdrom(&d->viocd_info) != 0) + printk(VIOCD_KERN_WARNING + "Cannot unregister viocd CD-ROM %s!\n", + d->viocd_info.name); + del_gendisk(d->viocd_disk); + blk_cleanup_queue(d->viocd_disk->queue); + put_disk(d->viocd_disk); + return 0; +} + +/** + * viocd_device_table: Used by vio.c to match devices that we + * support. + */ +static struct vio_device_id viocd_device_table[] __devinitdata = { + { "block", "IBM,iSeries-viocd" }, + { "", "" } +}; +MODULE_DEVICE_TABLE(vio, viocd_device_table); + +static struct vio_driver viocd_driver = { + .id_table = viocd_device_table, + .probe = viocd_probe, + .remove = viocd_remove, + .driver = { + .name = "viocd", + .owner = THIS_MODULE, + } +}; + +static int __init viocd_init(void) +{ struct proc_dir_entry *e; + int ret = 0; + + if (!firmware_has_feature(FW_FEATURE_ISERIES)) + return -ENODEV; if (viopath_hostLp == HvLpIndexInvalid) { vio_set_hostlp(); @@ -583,8 +763,7 @@ static int __init viocd_init(void) viopath_hostLp); if (register_blkdev(VIOCD_MAJOR, VIOCD_DEVICE) != 0) { - printk(VIOCD_KERN_WARNING - "Unable to get major %d for %s\n", + printk(VIOCD_KERN_WARNING "Unable to get major %d for %s\n", VIOCD_MAJOR, VIOCD_DEVICE); return -EIO; } @@ -602,62 +781,12 @@ static int __init viocd_init(void) vio_setHandler(viomajorsubtype_cdio, vio_handle_cd_event); get_viocd_info(); - if (viocd_numdev == 0) - goto out_undo_vio; - ret = -ENOMEM; spin_lock_init(&viocd_reqlock); - viocd_queue = blk_init_queue(do_viocd_request, &viocd_reqlock); - if (viocd_queue == NULL) - goto out_unregister; - blk_queue_max_hw_segments(viocd_queue, 1); - blk_queue_max_phys_segments(viocd_queue, 1); - blk_queue_max_sectors(viocd_queue, 4096 / 512); - - /* initialize units */ - for (deviceno = 0; deviceno < viocd_numdev; deviceno++) { - struct disk_info *d = &viocd_diskinfo[deviceno]; - struct cdrom_device_info *c = &d->viocd_info; - struct cdrom_info *ci = &viocd_unitinfo[deviceno]; - - c->ops = &viocd_dops; - c->speed = 4; - c->capacity = 1; - c->handle = d; - c->mask = ~find_capability(ci->type); - sprintf(c->name, VIOCD_DEVICE "%c", 'a' + deviceno); - - if (register_cdrom(c) != 0) { - printk(VIOCD_KERN_WARNING - "Cannot register viocd CD-ROM %s!\n", - c->name); - continue; - } - printk(VIOCD_KERN_INFO "cd %s is iSeries resource %10.10s " - "type %4.4s, model %3.3s\n", - c->name, ci->rsrcname, ci->type, ci->model); - gendisk = alloc_disk(1); - if (gendisk == NULL) { - printk(VIOCD_KERN_WARNING - "Cannot create gendisk for %s!\n", - c->name); - unregister_cdrom(c); - continue; - } - gendisk->major = VIOCD_MAJOR; - gendisk->first_minor = deviceno; - strncpy(gendisk->disk_name, c->name, - sizeof(gendisk->disk_name)); - snprintf(gendisk->devfs_name, sizeof(gendisk->devfs_name), - VIOCD_DEVICE_DEVFS "%d", deviceno); - gendisk->queue = viocd_queue; - gendisk->fops = &viocd_fops; - gendisk->flags = GENHD_FL_CD|GENHD_FL_REMOVABLE; - set_capacity(gendisk, 0); - gendisk->private_data = d; - d->viocd_disk = gendisk; - add_disk(gendisk); - } + + ret = vio_register_driver(&viocd_driver); + if (ret) + goto out_free_info; e = create_proc_entry("iSeries/viocd", S_IFREG|S_IRUGO, NULL); if (e) { @@ -667,7 +796,10 @@ static int __init viocd_init(void) return 0; -out_undo_vio: +out_free_info: + dma_free_coherent(iSeries_vio_dev, + sizeof(*viocd_unitinfo) * VIOCD_MAX_CD, + viocd_unitinfo, unitinfo_dmaaddr); vio_clearHandler(viomajorsubtype_cdio); viopath_close(viopath_hostLp, viomajorsubtype_cdio, MAX_CD_REQ + 2); out_unregister: @@ -677,19 +809,8 @@ out_unregister: static void __exit viocd_exit(void) { - int deviceno; - remove_proc_entry("iSeries/viocd", NULL); - for (deviceno = 0; deviceno < viocd_numdev; deviceno++) { - struct disk_info *d = &viocd_diskinfo[deviceno]; - if (unregister_cdrom(&d->viocd_info) != 0) - printk(VIOCD_KERN_WARNING - "Cannot unregister viocd CD-ROM %s!\n", - d->viocd_info.name); - del_gendisk(d->viocd_disk); - put_disk(d->viocd_disk); - } - blk_cleanup_queue(viocd_queue); + vio_unregister_driver(&viocd_driver); if (viocd_unitinfo != NULL) dma_free_coherent(iSeries_vio_dev, sizeof(*viocd_unitinfo) * VIOCD_MAX_CD,