⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 viocd.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* -*- 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 + -