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 + -
显示快捷键?