📄 viocd.c
字号:
static int viocd_media_changed(struct cdrom_device_info *cdi, int disc_nr){ struct viocd_waitevent we; HvLpEvent_Rc hvrc; int device_no = DEVICE_NR((struct disk_info *)cdi->handle); init_completion(&we.com); /* Send the open event to OS/400 */ hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, HvLpEvent_Type_VirtualIo, viomajorsubtype_cdio | viocdcheck, HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, viopath_sourceinst(viopath_hostLp), viopath_targetinst(viopath_hostLp), (u64)&we, VIOVERSION << 16, ((u64)device_no << 48), 0, 0, 0); if (hvrc != 0) { printk(VIOCD_KERN_WARNING "bad rc on HvCallEvent_signalLpEventFast %d\n", (int)hvrc); return -EIO; } wait_for_completion(&we.com); /* Check the return code. If bad, assume no change */ if (we.rc) { const struct vio_error_entry *err = vio_lookup_rc(viocd_err_table, we.sub_result); printk(VIOCD_KERN_WARNING "bad rc %d:0x%04X on check_change: %s; Assuming no change\n", we.rc, we.sub_result, err->msg); return 0; } return we.changed;}static int viocd_lock_door(struct cdrom_device_info *cdi, int locking){ HvLpEvent_Rc hvrc; u64 device_no = DEVICE_NR((struct disk_info *)cdi->handle); /* NOTE: flags is 1 or 0 so it won't overwrite the device_no */ u64 flags = !!locking; struct viocd_waitevent we; init_completion(&we.com); /* Send the lockdoor event to OS/400 */ hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, HvLpEvent_Type_VirtualIo, viomajorsubtype_cdio | viocdlockdoor, HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, viopath_sourceinst(viopath_hostLp), viopath_targetinst(viopath_hostLp), (u64)&we, VIOVERSION << 16, (device_no << 48) | (flags << 32), 0, 0, 0); if (hvrc != 0) { printk(VIOCD_KERN_WARNING "bad rc on HvCallEvent_signalLpEventFast %d\n", (int)hvrc); return -EIO; } wait_for_completion(&we.com); if (we.rc != 0) return -EIO; 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; 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;}/* This routine handles incoming CD LP events */static void vio_handle_cd_event(struct HvLpEvent *event){ struct viocdlpevent *bevent; struct viocd_waitevent *pwe; struct disk_info *di; unsigned long flags; struct request *req; if (event == NULL) /* Notification that a partition went away! */ return; /* First, we should NEVER get an int here...only acks */ if (event->xFlags.xFunction == HvLpEvent_Function_Int) { printk(VIOCD_KERN_WARNING "Yikes! got an int in viocd event handler!\n"); if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) { event->xRc = HvLpEvent_Rc_InvalidSubtype; HvCallEvent_ackLpEvent(event); } } bevent = (struct viocdlpevent *)event; switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) { case viocdopen: if (event->xRc == 0) { di = &viocd_diskinfo[bevent->disk]; blk_queue_hardsect_size(viocd_queue, bevent->block_size); set_capacity(di->viocd_disk, bevent->media_size * bevent->block_size / 512); } /* FALLTHROUGH !! */ case viocdgetinfo: case viocdlockdoor: pwe = (struct viocd_waitevent *)event->xCorrelationToken;return_complete: pwe->rc = event->xRc; pwe->sub_result = bevent->sub_result; complete(&pwe->com); break; case viocdcheck: pwe = (struct viocd_waitevent *)event->xCorrelationToken; pwe->changed = bevent->flags; goto 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(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--; if (event->xRc != HvLpEvent_Rc_Good) { const struct vio_error_entry *err = vio_lookup_rc(viocd_err_table, bevent->sub_result); printk(VIOCD_KERN_WARNING "request %p failed " "with rc %d:0x%04X: %s\n", req, event->xRc, bevent->sub_result, err->msg); end_request(req, 0); } else end_request(req, 1); /* restart handling of incoming requests */ spin_unlock_irqrestore(&viocd_reqlock, flags); blk_run_queue(viocd_queue); break; default: printk(VIOCD_KERN_WARNING "message with invalid subtype %0x04X!\n", event->xSubtype & VIOMINOR_SUBTYPE_MASK); if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) { event->xRc = HvLpEvent_Rc_InvalidSubtype; HvCallEvent_ackLpEvent(event); } }}static struct cdrom_device_ops viocd_dops = { .open = viocd_open, .release = viocd_release, .media_changed = viocd_media_changed, .lock_door = viocd_lock_door, .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_IOCTLS | 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){ struct capability_entry *entry; for(entry = capability_table; entry->type; ++entry) if(!strncmp(entry->type, type, 4)) break; return entry->capability;}static int viocd_probe(struct vio_dev *vdev, const struct vio_device_id *id){ struct gendisk *gendisk; int deviceno; struct disk_info *d; struct cdrom_device_info *c; struct cdrom_info *ci; 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); return 0; } 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); return 0; } 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; d->dev = &vdev->dev; gendisk->driverfs_dev = d->dev; add_disk(gendisk); return 0;}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); 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 = { { "viocd", "" }, { 0, }};MODULE_DEVICE_TABLE(vio, viocd_device_table);static struct vio_driver viocd_driver = { .name = "viocd", .id_table = viocd_device_table, .probe = viocd_probe, .remove = viocd_remove};static int __init viocd_init(void){ struct proc_dir_entry *e; int ret = 0; if (viopath_hostLp == HvLpIndexInvalid) { vio_set_hostlp(); /* If we don't have a host, bail out */ if (viopath_hostLp == HvLpIndexInvalid) return -ENODEV; } printk(VIOCD_KERN_INFO "vers " VIOCD_VERS ", hosting partition %d\n", viopath_hostLp); if (register_blkdev(VIOCD_MAJOR, VIOCD_DEVICE) != 0) { printk(VIOCD_KERN_WARNING "Unable to get major %d for %s\n", VIOCD_MAJOR, VIOCD_DEVICE); return -EIO; } ret = viopath_open(viopath_hostLp, viomajorsubtype_cdio, MAX_CD_REQ + 2); if (ret) { printk(VIOCD_KERN_WARNING "error opening path to host partition %d\n", viopath_hostLp); goto out_unregister; } /* Initialize our request handler */ vio_setHandler(viomajorsubtype_cdio, vio_handle_cd_event); get_viocd_info(); if (viocd_numdev == 0) goto out_undo_vio; spin_lock_init(&viocd_reqlock); viocd_queue = blk_init_queue(do_viocd_request, &viocd_reqlock); if (viocd_queue == NULL) { ret = -ENOMEM; goto out_free_info; } blk_queue_max_hw_segments(viocd_queue, 1); blk_queue_max_phys_segments(viocd_queue, 1); blk_queue_max_sectors(viocd_queue, 4096 / 512); ret = vio_register_driver(&viocd_driver); if (ret) goto out_cleanup_queue; e = create_proc_entry("iSeries/viocd", S_IFREG|S_IRUGO, NULL); if (e) { e->owner = THIS_MODULE; e->proc_fops = &proc_viocd_operations; } return 0;out_cleanup_queue: blk_cleanup_queue(viocd_queue);out_free_info: dma_free_coherent(iSeries_vio_dev, sizeof(*viocd_unitinfo) * VIOCD_MAX_CD, viocd_unitinfo, unitinfo_dmaaddr);out_undo_vio: vio_clearHandler(viomajorsubtype_cdio); viopath_close(viopath_hostLp, viomajorsubtype_cdio, MAX_CD_REQ + 2);out_unregister: unregister_blkdev(VIOCD_MAJOR, VIOCD_DEVICE); return ret;}static void __exit viocd_exit(void){ remove_proc_entry("iSeries/viocd", NULL); vio_unregister_driver(&viocd_driver); blk_cleanup_queue(viocd_queue); if (viocd_unitinfo != NULL) dma_free_coherent(iSeries_vio_dev, sizeof(*viocd_unitinfo) * VIOCD_MAX_CD, viocd_unitinfo, unitinfo_dmaaddr); viopath_close(viopath_hostLp, viomajorsubtype_cdio, MAX_CD_REQ + 2); vio_clearHandler(viomajorsubtype_cdio); unregister_blkdev(VIOCD_MAJOR, VIOCD_DEVICE);}module_init(viocd_init);module_exit(viocd_exit);MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -