i2o_config.c
来自「优龙2410linux2.6.8内核源代码」· C语言 代码 · 共 1,150 行 · 第 1/2 页
C
1,150 行
/* * 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 <asm/uaccess.h>#include <asm/io.h>static int i2o_cfg_context = -1;static void *page_buf;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 u32 q_id; // Event queue ID...used as tx_context struct i2o_cfg_info *next;};static struct i2o_cfg_info *open_files = NULL;static int i2o_cfg_info_id = 0;static int ioctl_getiops(unsigned long);static int ioctl_gethrt(unsigned long);static int ioctl_getlct(unsigned long);static int ioctl_parms(unsigned long, unsigned int);static int ioctl_html(unsigned long);static int ioctl_swdl(unsigned long);static int ioctl_swul(unsigned long);static int ioctl_swdel(unsigned long);static int ioctl_validate(unsigned long); static int ioctl_evt_reg(unsigned long, struct file *);static int ioctl_evt_get(unsigned long, struct file *);static int ioctl_passthru(unsigned long);static int cfg_fasync(int, struct file*, int);/* * 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 == 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;}/* * Each of these describes an i2o message handler. They are * multiplexed by the i2o_core code */ struct i2o_handler cfg_handler={ i2o_cfg_reply, NULL, NULL, NULL, "Configuration", 0, 0xffffffff // All classes};static ssize_t cfg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos){ printk(KERN_INFO "i2o_config write not yet supported\n"); return 0;}static ssize_t cfg_read(struct file *file, char __user *buf, size_t count, loff_t *ptr){ return 0;}/* * IOCTL Handler */static int cfg_ioctl(struct inode *inode, struct file *fp, unsigned int cmd, unsigned long arg){ int ret; switch(cmd) { case I2OGETIOPS: ret = ioctl_getiops(arg); break; case I2OHRTGET: ret = ioctl_gethrt(arg); break; case I2OLCTGET: ret = ioctl_getlct(arg); break; case I2OPARMSET: ret = ioctl_parms(arg, I2OPARMSET); break; case I2OPARMGET: ret = ioctl_parms(arg, I2OPARMGET); break; case I2OSWDL: ret = ioctl_swdl(arg); break; case I2OSWUL: ret = ioctl_swul(arg); break; case I2OSWDEL: ret = ioctl_swdel(arg); break; case I2OVALIDATE: ret = ioctl_validate(arg); break; case I2OHTML: ret = ioctl_html(arg); break; case I2OEVTREG: ret = ioctl_evt_reg(arg, fp); break; case I2OEVTGET: ret = ioctl_evt_get(arg, fp); break; case I2OPASSTHRU: ret = ioctl_passthru(arg); break; default: ret = -EINVAL; } return ret;}int ioctl_getiops(unsigned long arg){ u8 __user *user_iop_table = (void __user *)arg; struct i2o_controller *c = NULL; int i; u8 foo[MAX_I2O_CONTROLLERS]; if(!access_ok(VERIFY_WRITE, user_iop_table, MAX_I2O_CONTROLLERS)) return -EFAULT; for(i = 0; i < MAX_I2O_CONTROLLERS; i++) { c = i2o_find_controller(i); if(c) { foo[i] = 1; if(pci_set_dma_mask(c->pdev, 0xffffffff)) { printk(KERN_WARNING "i2o_config : No suitable DMA available on controller %d\n", i); i2o_unlock_controller(c); continue; } i2o_unlock_controller(c); } else { foo[i] = 0; } } __copy_to_user(user_iop_table, foo, MAX_I2O_CONTROLLERS); return 0;}int ioctl_gethrt(unsigned long arg){ struct i2o_controller *c; struct i2o_cmd_hrtlct __user *cmd = (void __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_controller(kcmd.iop); if(!c) return -ENXIO; hrt = (i2o_hrt *)c->hrt; i2o_unlock_controller(c); 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;}int ioctl_getlct(unsigned long arg){ struct i2o_controller *c; struct i2o_cmd_hrtlct __user *cmd = (void __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_controller(kcmd.iop); if(!c) return -ENXIO; lct = (i2o_lct *)c->lct; i2o_unlock_controller(c); len = (unsigned int)lct->table_size << 2; put_user(len, kcmd.reslen); if(len > reslen) ret = -ENOBUFS; else if(copy_to_user(kcmd.resbuf, (void*)lct, len)) ret = -EFAULT; return ret;}static int ioctl_parms(unsigned long arg, unsigned int type){ int ret = 0; struct i2o_controller *c; struct i2o_cmd_psetget __user *cmd = (void __user *)arg; struct i2o_cmd_psetget kcmd; u32 reslen; u8 *ops; u8 *res; int len; 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_controller(kcmd.iop); if(!c) return -ENXIO; ops = (u8*)kmalloc(kcmd.oplen, GFP_KERNEL); if(!ops) { i2o_unlock_controller(c); return -ENOMEM; } if(copy_from_user(ops, kcmd.opbuf, kcmd.oplen)) { i2o_unlock_controller(c); 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) { i2o_unlock_controller(c); kfree(ops); return -ENOMEM; } len = i2o_issue_params(i2o_cmd, c, kcmd.tid, ops, kcmd.oplen, res, 65536); i2o_unlock_controller(c); 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;}int ioctl_html(unsigned long arg){ struct i2o_html __user *cmd = (void __user *)arg; struct i2o_html kcmd; struct i2o_controller *c; u8 *res = NULL; void *query = NULL; dma_addr_t query_phys, res_phys; int ret = 0; int token; u32 len; u32 reslen; u32 msg[MSG_FRAME_SIZE]; if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_html))) { printk(KERN_INFO "i2o_config: can't copy html cmd\n"); return -EFAULT; } if(get_user(reslen, kcmd.reslen) < 0) { printk(KERN_INFO "i2o_config: can't copy html reslen\n"); return -EFAULT; } if(!kcmd.resbuf) { printk(KERN_INFO "i2o_config: NULL html buffer\n"); return -EFAULT; } c = i2o_find_controller(kcmd.iop); if(!c) return -ENXIO; if(kcmd.qlen) /* Check for post data */ { query = pci_alloc_consistent(c->pdev, kcmd.qlen, &query_phys); if(!query) { i2o_unlock_controller(c); return -ENOMEM; } if(copy_from_user(query, kcmd.qbuf, kcmd.qlen)) { i2o_unlock_controller(c); printk(KERN_INFO "i2o_config: could not get query\n"); pci_free_consistent(c->pdev, kcmd.qlen, query, query_phys); return -EFAULT; } } res = pci_alloc_consistent(c->pdev, 65536, &res_phys); if(!res) { i2o_unlock_controller(c); pci_free_consistent(c->pdev, kcmd.qlen, query, query_phys); return -ENOMEM; } msg[1] = (I2O_CMD_UTIL_CONFIG_DIALOG << 24)|HOST_TID<<12|kcmd.tid; msg[2] = i2o_cfg_context; msg[3] = 0; msg[4] = kcmd.page; msg[5] = 0xD0000000|65536; msg[6] = res_phys; if(!kcmd.qlen) /* Check for post data */ msg[0] = SEVEN_WORD_MSG_SIZE|SGL_OFFSET_5; else { msg[0] = NINE_WORD_MSG_SIZE|SGL_OFFSET_5; msg[5] = 0x50000000|65536; msg[7] = 0xD4000000|(kcmd.qlen); msg[8] = query_phys; } /* Wait for a considerable time till the Controller does its job before timing out. The controller might take more time to process this request if there are many devices connected to it. */ token = i2o_post_wait_mem(c, msg, 9*4, 400, query, res, query_phys, res_phys, kcmd.qlen, 65536); if(token < 0) { printk(KERN_DEBUG "token = %#10x\n", token); i2o_unlock_controller(c); if(token != -ETIMEDOUT) { pci_free_consistent(c->pdev, 65536, res, res_phys); if(kcmd.qlen) pci_free_consistent(c->pdev, kcmd.qlen, query, query_phys); } return token; } i2o_unlock_controller(c); len = strnlen(res, 65536); put_user(len, kcmd.reslen); if(len > reslen) ret = -ENOMEM; if(copy_to_user(kcmd.resbuf, res, len)) ret = -EFAULT; pci_free_consistent(c->pdev, 65536, res, res_phys); if(kcmd.qlen) pci_free_consistent(c->pdev, kcmd.qlen, query, query_phys); return ret;} int ioctl_swdl(unsigned long arg){ struct i2o_sw_xfer kxfer;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?