i2o_config.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,251 行 · 第 1/2 页

C
1,251
字号
/* * I2O Configuration Interface Driver * * (C) Copyright 1999-2002  Red Hat * * Written by Alan Cox, Building Number Three Ltd * * Fixes/additions: *	Deepak Saxena (04/20/1999): *		Added basic ioctl() support *	Deepak Saxena (06/07/1999): *		Added software download ioctl (still testing) *	Auvo H鋕kinen (09/10/1999): *		Changes to i2o_cfg_reply(), ioctl_parms() *		Added ioct_validate() *	Taneli V鋒鋕angas (09/30/1999): *		Fixed ioctl_swdl() *	Taneli V鋒鋕angas (10/04/1999): *		Changed ioctl_swdl(), implemented ioctl_swul() and ioctl_swdel() *	Deepak Saxena (11/18/1999): *		Added event managmenet support *	Alan Cox <alan@redhat.com>: *		2.4 rewrite ported to 2.5 *	Markus Lidel <Markus.Lidel@shadowconnect.com>: *		Added pass-thru support for Adaptec's raidutils * * 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) any later version. */#include <linux/module.h>#include <linux/kernel.h>#include <linux/pci.h>#include <linux/i2o.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/miscdevice.h>#include <linux/mm.h>#include <linux/spinlock.h>#include <linux/smp_lock.h>#include <linux/ioctl32.h>#include <linux/compat.h>#include <linux/syscalls.h>#include <asm/uaccess.h>#include <asm/io.h>extern int i2o_parm_issue(struct i2o_device *, int, void *, int, void *, int);static spinlock_t i2o_config_lock = SPIN_LOCK_UNLOCKED;struct wait_queue *i2o_wait_queue;#define MODINC(x,y) ((x) = ((x) + 1) % (y))struct sg_simple_element {	u32 flag_count;	u32 addr_bus;};struct i2o_cfg_info {	struct file *fp;	struct fasync_struct *fasync;	struct i2o_evt_info event_q[I2O_EVT_Q_LEN];	u16 q_in;		// Queue head index	u16 q_out;		// Queue tail index	u16 q_len;		// Queue length	u16 q_lost;		// Number of lost events	ulong q_id;		// Event queue ID...used as tx_context	struct i2o_cfg_info *next;};static struct i2o_cfg_info *open_files = NULL;static ulong i2o_cfg_info_id = 0;#if 0/* *	This is the callback for any message we have posted. The message itself *	will be returned to the message pool when we return from the IRQ * *	This runs in irq context so be short and sweet. */static void i2o_cfg_reply(struct i2o_handler *h, struct i2o_controller *c,			  struct i2o_message *m){	u32 *msg = (u32 *) m;	if (msg[0] & MSG_FAIL) {		u32 *preserved_msg = (u32 *) (c->msg_virt + msg[7]);		printk(KERN_ERR "i2o_config: IOP failed to process the msg.\n");		/* Release the preserved msg frame by resubmitting it as a NOP */		preserved_msg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0;		preserved_msg[1] = I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | 0;		preserved_msg[2] = 0;		i2o_post_message(c, msg[7]);	}	if (msg[4] >> 24)	// ReqStatus != SUCCESS		i2o_report_status(KERN_INFO, "i2o_config", msg);	if (m->function == I2O_CMD_UTIL_EVT_REGISTER) {		struct i2o_cfg_info *inf;		for (inf = open_files; inf; inf = inf->next)			if (inf->q_id == i2o_cntxt_list_get(c, msg[3]))				break;		//		// If this is the case, it means that we're getting		// events for a file descriptor that's been close()'d		// w/o the user unregistering for events first.		// The code currently assumes that the user will		// take care of unregistering for events before closing		// a file.		//		// TODO:		// Should we track event registartion and deregister		// for events when a file is close()'d so this doesn't		// happen? That would get rid of the search through		// the linked list since file->private_data could point		// directly to the i2o_config_info data structure...but		// it would mean having all sorts of tables to track		// what each file is registered for...I think the		// current method is simpler. - DS		//		if (!inf)			return;		inf->event_q[inf->q_in].id.iop = c->unit;		inf->event_q[inf->q_in].id.tid = m->target_tid;		inf->event_q[inf->q_in].id.evt_mask = msg[4];		//		// Data size = msg size - reply header		//		inf->event_q[inf->q_in].data_size = (m->size - 5) * 4;		if (inf->event_q[inf->q_in].data_size)			memcpy(inf->event_q[inf->q_in].evt_data,			       (unsigned char *)(msg + 5),			       inf->event_q[inf->q_in].data_size);		spin_lock(&i2o_config_lock);		MODINC(inf->q_in, I2O_EVT_Q_LEN);		if (inf->q_len == I2O_EVT_Q_LEN) {			MODINC(inf->q_out, I2O_EVT_Q_LEN);			inf->q_lost++;		} else {			// Keep I2OEVTGET on another CPU from touching this			inf->q_len++;		}		spin_unlock(&i2o_config_lock);//              printk(KERN_INFO "File %p w/id %d has %d events\n",//                      inf->fp, inf->q_id, inf->q_len);		kill_fasync(&inf->fasync, SIGIO, POLL_IN);	}	return;}#endif/* *	Each of these describes an i2o message handler. They are *	multiplexed by the i2o_core code */struct i2o_driver i2o_config_driver = {	.name = "Config-OSM"};static int i2o_cfg_getiops(unsigned long arg){	struct i2o_controller *c;	u8 __user *user_iop_table = (void __user *)arg;	u8 tmp[MAX_I2O_CONTROLLERS];	memset(tmp, 0, MAX_I2O_CONTROLLERS);	if (!access_ok(VERIFY_WRITE, user_iop_table, MAX_I2O_CONTROLLERS))		return -EFAULT;	list_for_each_entry(c, &i2o_controllers, list)	    tmp[c->unit] = 1;	__copy_to_user(user_iop_table, tmp, MAX_I2O_CONTROLLERS);	return 0;};static int i2o_cfg_gethrt(unsigned long arg){	struct i2o_controller *c;	struct i2o_cmd_hrtlct __user *cmd = (struct i2o_cmd_hrtlct __user *)arg;	struct i2o_cmd_hrtlct kcmd;	i2o_hrt *hrt;	int len;	u32 reslen;	int ret = 0;	if (copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_hrtlct)))		return -EFAULT;	if (get_user(reslen, kcmd.reslen) < 0)		return -EFAULT;	if (kcmd.resbuf == NULL)		return -EFAULT;	c = i2o_find_iop(kcmd.iop);	if (!c)		return -ENXIO;	hrt = (i2o_hrt *) c->hrt.virt;	len = 8 + ((hrt->entry_len * hrt->num_entries) << 2);	/* We did a get user...so assuming mem is ok...is this bad? */	put_user(len, kcmd.reslen);	if (len > reslen)		ret = -ENOBUFS;	if (copy_to_user(kcmd.resbuf, (void *)hrt, len))		ret = -EFAULT;	return ret;};static int i2o_cfg_getlct(unsigned long arg){	struct i2o_controller *c;	struct i2o_cmd_hrtlct __user *cmd = (struct i2o_cmd_hrtlct __user *)arg;	struct i2o_cmd_hrtlct kcmd;	i2o_lct *lct;	int len;	int ret = 0;	u32 reslen;	if (copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_hrtlct)))		return -EFAULT;	if (get_user(reslen, kcmd.reslen) < 0)		return -EFAULT;	if (kcmd.resbuf == NULL)		return -EFAULT;	c = i2o_find_iop(kcmd.iop);	if (!c)		return -ENXIO;	lct = (i2o_lct *) c->lct;	len = (unsigned int)lct->table_size << 2;	put_user(len, kcmd.reslen);	if (len > reslen)		ret = -ENOBUFS;	else if (copy_to_user(kcmd.resbuf, lct, len))		ret = -EFAULT;	return ret;};static int i2o_cfg_parms(unsigned long arg, unsigned int type){	int ret = 0;	struct i2o_controller *c;	struct i2o_device *dev;	struct i2o_cmd_psetget __user *cmd =	    (struct i2o_cmd_psetget __user *)arg;	struct i2o_cmd_psetget kcmd;	u32 reslen;	u8 *ops;	u8 *res;	int len = 0;	u32 i2o_cmd = (type == I2OPARMGET ?		       I2O_CMD_UTIL_PARAMS_GET : I2O_CMD_UTIL_PARAMS_SET);	if (copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_psetget)))		return -EFAULT;	if (get_user(reslen, kcmd.reslen))		return -EFAULT;	c = i2o_find_iop(kcmd.iop);	if (!c)		return -ENXIO;	dev = i2o_iop_find_device(c, kcmd.tid);	if (!dev)		return -ENXIO;	ops = (u8 *) kmalloc(kcmd.oplen, GFP_KERNEL);	if (!ops)		return -ENOMEM;	if (copy_from_user(ops, kcmd.opbuf, kcmd.oplen)) {		kfree(ops);		return -EFAULT;	}	/*	 * It's possible to have a _very_ large table	 * and that the user asks for all of it at once...	 */	res = (u8 *) kmalloc(65536, GFP_KERNEL);	if (!res) {		kfree(ops);		return -ENOMEM;	}	len = i2o_parm_issue(dev, i2o_cmd, ops, kcmd.oplen, res, 65536);	kfree(ops);	if (len < 0) {		kfree(res);		return -EAGAIN;	}	put_user(len, kcmd.reslen);	if (len > reslen)		ret = -ENOBUFS;	else if (copy_to_user(kcmd.resbuf, res, len))		ret = -EFAULT;	kfree(res);	return ret;};static int i2o_cfg_swdl(unsigned long arg){	struct i2o_sw_xfer kxfer;	struct i2o_sw_xfer __user *pxfer = (struct i2o_sw_xfer __user *)arg;	unsigned char maxfrag = 0, curfrag = 1;	struct i2o_dma buffer;	struct i2o_message *msg;	u32 m;	unsigned int status = 0, swlen = 0, fragsize = 8192;	struct i2o_controller *c;	if (copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer)))		return -EFAULT;	if (get_user(swlen, kxfer.swlen) < 0)		return -EFAULT;	if (get_user(maxfrag, kxfer.maxfrag) < 0)		return -EFAULT;	if (get_user(curfrag, kxfer.curfrag) < 0)		return -EFAULT;	if (curfrag == maxfrag)		fragsize = swlen - (maxfrag - 1) * 8192;	if (!kxfer.buf || !access_ok(VERIFY_READ, kxfer.buf, fragsize))		return -EFAULT;	c = i2o_find_iop(kxfer.iop);	if (!c)		return -ENXIO;	m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);	if (m == I2O_QUEUE_EMPTY)		return -EBUSY;	if (i2o_dma_alloc(&c->pdev->dev, &buffer, fragsize, GFP_KERNEL)) {		i2o_msg_nop(c, m);		return -ENOMEM;	}	__copy_from_user(buffer.virt, kxfer.buf, fragsize);	writel(NINE_WORD_MSG_SIZE | SGL_OFFSET_7, &msg->u.head[0]);	writel(I2O_CMD_SW_DOWNLOAD << 24 | HOST_TID << 12 | ADAPTER_TID,	       &msg->u.head[1]);	writel(i2o_config_driver.context, &msg->u.head[2]);	writel(0, &msg->u.head[3]);	writel((((u32) kxfer.flags) << 24) | (((u32) kxfer.sw_type) << 16) |	       (((u32) maxfrag) << 8) | (((u32) curfrag)), &msg->body[0]);	writel(swlen, &msg->body[1]);	writel(kxfer.sw_id, &msg->body[2]);	writel(0xD0000000 | fragsize, &msg->body[3]);	writel(buffer.phys, &msg->body[4]);//      printk("i2o_config: swdl frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize);	status = i2o_msg_post_wait_mem(c, m, 60, &buffer);	if (status != -ETIMEDOUT)		i2o_dma_free(&c->pdev->dev, &buffer);	if (status != I2O_POST_WAIT_OK) {		// it fails if you try and send frags out of order		// and for some yet unknown reasons too		printk(KERN_INFO		       "i2o_config: swdl failed, DetailedStatus = %d\n",		       status);		return status;	}	return 0;};static int i2o_cfg_swul(unsigned long arg){	struct i2o_sw_xfer kxfer;	struct i2o_sw_xfer __user *pxfer = (struct i2o_sw_xfer __user *)arg;	unsigned char maxfrag = 0, curfrag = 1;	struct i2o_dma buffer;	struct i2o_message *msg;	u32 m;	unsigned int status = 0, swlen = 0, fragsize = 8192;	struct i2o_controller *c;	if (copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer)))		return -EFAULT;	if (get_user(swlen, kxfer.swlen) < 0)		return -EFAULT;	if (get_user(maxfrag, kxfer.maxfrag) < 0)		return -EFAULT;	if (get_user(curfrag, kxfer.curfrag) < 0)		return -EFAULT;	if (curfrag == maxfrag)		fragsize = swlen - (maxfrag - 1) * 8192;	if (!kxfer.buf || !access_ok(VERIFY_WRITE, kxfer.buf, fragsize))		return -EFAULT;	c = i2o_find_iop(kxfer.iop);	if (!c)		return -ENXIO;	m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);	if (m == I2O_QUEUE_EMPTY)		return -EBUSY;	if (i2o_dma_alloc(&c->pdev->dev, &buffer, fragsize, GFP_KERNEL)) {		i2o_msg_nop(c, m);		return -ENOMEM;	}	writel(NINE_WORD_MSG_SIZE | SGL_OFFSET_7, &msg->u.head[0]);	writel(I2O_CMD_SW_UPLOAD << 24 | HOST_TID << 12 | ADAPTER_TID,	       &msg->u.head[1]);	writel(i2o_config_driver.context, &msg->u.head[2]);	writel(0, &msg->u.head[3]);	writel((u32) kxfer.flags << 24 | (u32) kxfer.	       sw_type << 16 | (u32) maxfrag << 8 | (u32) curfrag,	       &msg->body[0]);	writel(swlen, &msg->body[1]);	writel(kxfer.sw_id, &msg->body[2]);	writel(0xD0000000 | fragsize, &msg->body[3]);	writel(buffer.phys, &msg->body[4]);//      printk("i2o_config: swul frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize);	status = i2o_msg_post_wait_mem(c, m, 60, &buffer);	if (status != I2O_POST_WAIT_OK) {		if (status != -ETIMEDOUT)			i2o_dma_free(&c->pdev->dev, &buffer);		printk(KERN_INFO		       "i2o_config: swul failed, DetailedStatus = %d\n",		       status);		return status;	}	__copy_to_user(kxfer.buf, buffer.virt, fragsize);	i2o_dma_free(&c->pdev->dev, &buffer);	return 0;};static int i2o_cfg_swdel(unsigned long arg){	struct i2o_controller *c;	struct i2o_sw_xfer kxfer;	struct i2o_sw_xfer __user *pxfer = (struct i2o_sw_xfer __user *)arg;	struct i2o_message *msg;	u32 m;	unsigned int swlen;	int token;	if (copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer)))		return -EFAULT;	if (get_user(swlen, kxfer.swlen) < 0)		return -EFAULT;	c = i2o_find_iop(kxfer.iop);	if (!c)		return -ENXIO;	m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);	if (m == I2O_QUEUE_EMPTY)		return -EBUSY;	writel(SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]);	writel(I2O_CMD_SW_REMOVE << 24 | HOST_TID << 12 | ADAPTER_TID,	       &msg->u.head[1]);	writel(i2o_config_driver.context, &msg->u.head[2]);	writel(0, &msg->u.head[3]);	writel((u32) kxfer.flags << 24 | (u32) kxfer.sw_type << 16,	       &msg->body[0]);	writel(swlen, &msg->body[1]);	writel(kxfer.sw_id, &msg->body[2]);	token = i2o_msg_post_wait(c, m, 10);	if (token != I2O_POST_WAIT_OK) {		printk(KERN_INFO		       "i2o_config: swdel failed, DetailedStatus = %d\n",		       token);		return -ETIMEDOUT;	}	return 0;};static int i2o_cfg_validate(unsigned long arg){	int token;	int iop = (int)arg;	struct i2o_message *msg;	u32 m;	struct i2o_controller *c;	c = i2o_find_iop(iop);	if (!c)		return -ENXIO;	m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);	if (m == I2O_QUEUE_EMPTY)		return -EBUSY;	writel(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]);	writel(I2O_CMD_CONFIG_VALIDATE << 24 | HOST_TID << 12 | iop,	       &msg->u.head[1]);	writel(i2o_config_driver.context, &msg->u.head[2]);	writel(0, &msg->u.head[3]);	token = i2o_msg_post_wait(c, m, 10);	if (token != I2O_POST_WAIT_OK) {		printk(KERN_INFO "Can't validate configuration, ErrorStatus = "		       "%d\n", token);		return -ETIMEDOUT;	}	return 0;};static int i2o_cfg_evt_reg(unsigned long arg, struct file *fp){	struct i2o_message *msg;	u32 m;	struct i2o_evt_id __user *pdesc = (struct i2o_evt_id __user *)arg;	struct i2o_evt_id kdesc;	struct i2o_controller *c;	struct i2o_device *d;	if (copy_from_user(&kdesc, pdesc, sizeof(struct i2o_evt_id)))		return -EFAULT;	/* IOP exists? */	c = i2o_find_iop(kdesc.iop);	if (!c)		return -ENXIO;	/* Device exists? */	d = i2o_iop_find_device(c, kdesc.tid);	if (!d)		return -ENODEV;	m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);	if (m == I2O_QUEUE_EMPTY)		return -EBUSY;	writel(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]);	writel(I2O_CMD_UTIL_EVT_REGISTER << 24 | HOST_TID << 12 | kdesc.tid,	       &msg->u.head[1]);	writel(i2o_config_driver.context, &msg->u.head[2]);	writel(i2o_cntxt_list_add(c, fp->private_data), &msg->u.head[3]);	writel(kdesc.evt_mask, &msg->body[0]);	i2o_msg_post(c, m);	return 0;}static int i2o_cfg_evt_get(unsigned long arg, struct file *fp){	struct i2o_cfg_info *p = NULL;	struct i2o_evt_get __user *uget = (struct i2o_evt_get __user *)arg;	struct i2o_evt_get kget;	unsigned long flags;	for (p = open_files; p; p = p->next)		if (p->q_id == (ulong) fp->private_data)			break;	if (!p->q_len)		return -ENOENT;	memcpy(&kget.info, &p->event_q[p->q_out], sizeof(struct i2o_evt_info));	MODINC(p->q_out, I2O_EVT_Q_LEN);	spin_lock_irqsave(&i2o_config_lock, flags);	p->q_len--;	kget.pending = p->q_len;	kget.lost = p->q_lost;	spin_unlock_irqrestore(&i2o_config_lock, flags);	if (copy_to_user(uget, &kget, sizeof(struct i2o_evt_get)))		return -EFAULT;	return 0;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?