📄 viocd.c
字号:
/* -*- linux-c -*- * drivers/cdrom/viocd.c * * iSeries Virtual CD Rom * * Authors: Dave Boutcher <boutcher@us.ibm.com> * Ryan Arnold <ryanarn@us.ibm.com> * Colin Devilbiss <devilbis@us.ibm.com> * Stephen Rothwell <sfr@au1.ibm.com> * * (C) Copyright 2000-2004 IBM Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) anyu later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * This routine provides access to CD ROM drives owned and managed by an * OS/400 partition running on the same box as this Linux partition. * * All operations are performed by sending messages back and forth to * the OS/400 partition. */#include <linux/major.h>#include <linux/blkdev.h>#include <linux/cdrom.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/dma-mapping.h>#include <linux/module.h>#include <linux/completion.h>#include <linux/proc_fs.h>#include <linux/seq_file.h>#include <linux/scatterlist.h>#include <asm/vio.h>#include <asm/iseries/hv_types.h>#include <asm/iseries/hv_lp_event.h>#include <asm/iseries/vio.h>#include <asm/firmware.h>#define VIOCD_DEVICE "iseries/vcd"#define VIOCD_VERS "1.06"#define VIOCD_KERN_WARNING KERN_WARNING "viocd: "#define VIOCD_KERN_INFO KERN_INFO "viocd: "/* * Should probably make this a module parameter....sigh */#define VIOCD_MAX_CD HVMAXARCHITECTEDVIRTUALCDROMSstatic const struct vio_error_entry viocd_err_table[] = { {0x0201, EINVAL, "Invalid Range"}, {0x0202, EINVAL, "Invalid Token"}, {0x0203, EIO, "DMA Error"}, {0x0204, EIO, "Use Error"}, {0x0205, EIO, "Release Error"}, {0x0206, EINVAL, "Invalid CD"}, {0x020C, EROFS, "Read Only Device"}, {0x020D, ENOMEDIUM, "Changed or Missing Volume (or Varied Off?)"}, {0x020E, EIO, "Optical System Error (Varied Off?)"}, {0x02FF, EIO, "Internal Error"}, {0x3010, EIO, "Changed Volume"}, {0xC100, EIO, "Optical System Error"}, {0x0000, 0, NULL},};/* * This is the structure we use to exchange info between driver and interrupt * handler */struct viocd_waitevent { struct completion com; int rc; u16 sub_result; int changed;};/* this is a lookup table for the true capabilities of a device */struct capability_entry { char *type; int capability;};static struct capability_entry capability_table[] __initdata = { { "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 },};/* These are our internal structures for keeping track of devices */static int viocd_numdev;struct disk_info { struct gendisk *viocd_disk; struct cdrom_device_info viocd_info; struct device *dev; const char *rsrcname; const char *type; const char *model;};static struct disk_info viocd_diskinfo[VIOCD_MAX_CD];#define DEVICE_NR(di) ((di) - &viocd_diskinfo[0])static spinlock_t viocd_reqlock;#define MAX_CD_REQ 1/* procfs support */static int proc_viocd_show(struct seq_file *m, void *v){ int i; for (i = 0; i < viocd_numdev; i++) { seq_printf(m, "viocd device %d is iSeries resource %10.10s" "type %4.4s, model %3.3s\n", i, viocd_diskinfo[i].rsrcname, viocd_diskinfo[i].type, viocd_diskinfo[i].model); } return 0;}static int proc_viocd_open(struct inode *inode, struct file *file){ return single_open(file, proc_viocd_show, NULL);}static const struct file_operations proc_viocd_operations = { .open = proc_viocd_open, .read = seq_read, .llseek = seq_lseek, .release = single_release,};static int viocd_blk_open(struct inode *inode, struct file *file){ struct disk_info *di = inode->i_bdev->bd_disk->private_data; return cdrom_open(&di->viocd_info, inode, file);}static int viocd_blk_release(struct inode *inode, struct file *file){ struct disk_info *di = inode->i_bdev->bd_disk->private_data; return cdrom_release(&di->viocd_info, file);}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(file, &di->viocd_info, inode, cmd, arg);}static int viocd_blk_media_changed(struct gendisk *disk){ struct disk_info *di = disk->private_data; return cdrom_media_changed(&di->viocd_info);}struct block_device_operations viocd_fops = { .owner = THIS_MODULE, .open = viocd_blk_open, .release = viocd_blk_release, .ioctl = viocd_blk_ioctl, .media_changed = viocd_blk_media_changed,};static int viocd_open(struct cdrom_device_info *cdi, int purpose){ struct disk_info *diskinfo = cdi->handle; int device_no = DEVICE_NR(diskinfo); HvLpEvent_Rc hvrc; struct viocd_waitevent we; init_completion(&we.com); hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, HvLpEvent_Type_VirtualIo, viomajorsubtype_cdio | viocdopen, 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); 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 open: %s\n", we.rc, we.sub_result, err->msg); return -err->errno; } return 0;}static void viocd_release(struct cdrom_device_info *cdi){ int device_no = DEVICE_NR((struct disk_info *)cdi->handle); HvLpEvent_Rc hvrc; hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, HvLpEvent_Type_VirtualIo, viomajorsubtype_cdio | viocdclose, HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck, viopath_sourceinst(viopath_hostLp), viopath_targetinst(viopath_hostLp), 0, 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);}/* Send a read or write request to OS/400 */static int send_request(struct request *req){ HvLpEvent_Rc hvrc; 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); if (rq_data_dir(req) == READ) { direction = DMA_FROM_DEVICE; cmd = viomajorsubtype_cdio | viocdread; } else { direction = DMA_TO_DEVICE; cmd = viomajorsubtype_cdio | viocdwrite; } sg_init_table(&sg, 1); if (blk_rq_map_sg(req->q, req, &sg) == 0) { printk(VIOCD_KERN_WARNING "error setting up scatter/gather list\n"); return -1; } if (dma_map_sg(diskinfo->dev, &sg, 1, direction) == 0) { printk(VIOCD_KERN_WARNING "error allocating sg tce\n"); return -1; } dmaaddr = sg_dma_address(&sg); len = sg_dma_len(&sg); hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, HvLpEvent_Type_VirtualIo, cmd, HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, viopath_sourceinst(viopath_hostLp), viopath_targetinst(viopath_hostLp), (u64)req, VIOVERSION << 16, ((u64)DEVICE_NR(diskinfo) << 48) | dmaaddr, (u64)req->sector * 512, len, 0); if (hvrc != HvLpEvent_Rc_Good) { printk(VIOCD_KERN_WARNING "hv error on op %d\n", (int)hvrc); return -1; } return 0;}static void viocd_end_request(struct request *req, int uptodate){ int nsectors = req->hard_nr_sectors; /* * Make sure it's fully ended, and ensure that we process * at least one sector. */ if (blk_pc_request(req)) nsectors = (req->data_len + 511) >> 9; if (!nsectors) nsectors = 1; if (end_that_request_first(req, uptodate, nsectors)) BUG(); add_disk_randomness(req->rq_disk); blkdev_dequeue_request(req); end_that_request_last(req, uptodate);}static int rwreq;static void do_viocd_request(struct request_queue *q){ struct request *req; while ((rwreq == 0) && ((req = elv_next_request(q)) != NULL)) { if (!blk_fs_request(req)) viocd_end_request(req, 0); else if (send_request(req) < 0) { printk(VIOCD_KERN_WARNING "unable to send message to OS/400!"); viocd_end_request(req, 0); } else rwreq++; }}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 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -