📄 viocd.c
字号:
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; 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){ 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 (hvlpevent_is_int(event)) { printk(VIOCD_KERN_WARNING "Yikes! got an int in viocd event handler!\n"); if (hvlpevent_need_ack(event)) { 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(di->viocd_disk->queue, bevent->block_size); set_capacity(di->viocd_disk, bevent->media_size * bevent->block_size / 512); } /* FALLTHROUGH !! */ 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); viocd_end_request(req, 0); } else viocd_end_request(req, 1); /* restart handling of incoming requests */ spin_unlock_irqrestore(&viocd_reqlock, flags); restart_all_queues(bevent->disk); break; default: printk(VIOCD_KERN_WARNING "message with invalid subtype %0x04X!\n", event->xSubtype & VIOMINOR_SUBTYPE_MASK); if (hvlpevent_need_ack(event)) { 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_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 request_queue *q; struct device_node *node = vdev->dev.archdata.of_node; deviceno = vdev->unit_address; if (deviceno > VIOCD_MAX_CD) return -ENODEV; if (!node) return -ENODEV; if (deviceno >= viocd_numdev) viocd_numdev = deviceno + 1; d = &viocd_diskinfo[deviceno]; d->rsrcname = of_get_property(node, "linux,vio_rsrcname", NULL); d->type = of_get_property(node, "linux,vio_type", NULL); d->model = of_get_property(node, "linux,vio_model", NULL); c = &d->viocd_info; c->ops = &viocd_dops; c->speed = 4; c->capacity = 1; c->handle = d; c->mask = ~find_capability(d->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, d->rsrcname, d->type, d->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(); /* 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); spin_lock_init(&viocd_reqlock); 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) { e->owner = THIS_MODULE; e->proc_fops = &proc_viocd_operations; } return 0;out_free_info: 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); 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 + -