1 /* 2 * Compaq Hot Plug Controller Driver 3 * 4 * Copyright (C) 1995,2001 Compaq Computer Corporation 5 * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com) 6 * Copyright (C) 2001 IBM Corp. 7 * 8 * All rights reserved. 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or (at 13 * your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, but 16 * WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 18 * NON INFRINGEMENT. See the GNU General Public License for more 19 * details. 20 * 21 * You should have received a copy of the GNU General Public License 22 * along with this program; if not, write to the Free Software 23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 24 * 25 * Send feedback to <greg@kroah.com> 26 * 27 */ 28 29 #include <linux/module.h> 30 #include <linux/kernel.h> 31 #include <linux/slab.h> 32 #include <linux/types.h> 33 #include <linux/proc_fs.h> 34 #include <linux/workqueue.h> 35 #include <linux/pci.h> 36 #include <linux/pci_hotplug.h> 37 #include <linux/mutex.h> 38 #include <linux/debugfs.h> 39 #include "cpqphp.h" 40 41 static DEFINE_MUTEX(cpqphp_mutex); 42 static int show_ctrl (struct controller *ctrl, char *buf) 43 { 44 char *out = buf; 45 int index; 46 struct pci_resource *res; 47 48 out += sprintf(buf, "Free resources: memory\n"); 49 index = 11; 50 res = ctrl->mem_head; 51 while (res && index--) { 52 out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); 53 res = res->next; 54 } 55 out += sprintf(out, "Free resources: prefetchable memory\n"); 56 index = 11; 57 res = ctrl->p_mem_head; 58 while (res && index--) { 59 out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); 60 res = res->next; 61 } 62 out += sprintf(out, "Free resources: IO\n"); 63 index = 11; 64 res = ctrl->io_head; 65 while (res && index--) { 66 out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); 67 res = res->next; 68 } 69 out += sprintf(out, "Free resources: bus numbers\n"); 70 index = 11; 71 res = ctrl->bus_head; 72 while (res && index--) { 73 out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); 74 res = res->next; 75 } 76 77 return out - buf; 78 } 79 80 static int show_dev (struct controller *ctrl, char *buf) 81 { 82 char * out = buf; 83 int index; 84 struct pci_resource *res; 85 struct pci_func *new_slot; 86 struct slot *slot; 87 88 slot = ctrl->slot; 89 90 while (slot) { 91 new_slot = cpqhp_slot_find(slot->bus, slot->device, 0); 92 if (!new_slot) 93 break; 94 out += sprintf(out, "assigned resources: memory\n"); 95 index = 11; 96 res = new_slot->mem_head; 97 while (res && index--) { 98 out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); 99 res = res->next; 100 } 101 out += sprintf(out, "assigned resources: prefetchable memory\n"); 102 index = 11; 103 res = new_slot->p_mem_head; 104 while (res && index--) { 105 out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); 106 res = res->next; 107 } 108 out += sprintf(out, "assigned resources: IO\n"); 109 index = 11; 110 res = new_slot->io_head; 111 while (res && index--) { 112 out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); 113 res = res->next; 114 } 115 out += sprintf(out, "assigned resources: bus numbers\n"); 116 index = 11; 117 res = new_slot->bus_head; 118 while (res && index--) { 119 out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); 120 res = res->next; 121 } 122 slot=slot->next; 123 } 124 125 return out - buf; 126 } 127 128 static int spew_debug_info(struct controller *ctrl, char *data, int size) 129 { 130 int used; 131 132 used = size - show_ctrl(ctrl, data); 133 used = (size - used) - show_dev(ctrl, &data[used]); 134 return used; 135 } 136 137 struct ctrl_dbg { 138 int size; 139 char *data; 140 struct controller *ctrl; 141 }; 142 143 #define MAX_OUTPUT (4*PAGE_SIZE) 144 145 static int open(struct inode *inode, struct file *file) 146 { 147 struct controller *ctrl = inode->i_private; 148 struct ctrl_dbg *dbg; 149 int retval = -ENOMEM; 150 151 mutex_lock(&cpqphp_mutex); 152 dbg = kmalloc(sizeof(*dbg), GFP_KERNEL); 153 if (!dbg) 154 goto exit; 155 dbg->data = kmalloc(MAX_OUTPUT, GFP_KERNEL); 156 if (!dbg->data) { 157 kfree(dbg); 158 goto exit; 159 } 160 dbg->size = spew_debug_info(ctrl, dbg->data, MAX_OUTPUT); 161 file->private_data = dbg; 162 retval = 0; 163 exit: 164 mutex_unlock(&cpqphp_mutex); 165 return retval; 166 } 167 168 static loff_t lseek(struct file *file, loff_t off, int whence) 169 { 170 struct ctrl_dbg *dbg; 171 loff_t new = -1; 172 173 mutex_lock(&cpqphp_mutex); 174 dbg = file->private_data; 175 176 switch (whence) { 177 case 0: 178 new = off; 179 break; 180 case 1: 181 new = file->f_pos + off; 182 break; 183 } 184 if (new < 0 || new > dbg->size) { 185 mutex_unlock(&cpqphp_mutex); 186 return -EINVAL; 187 } 188 mutex_unlock(&cpqphp_mutex); 189 return (file->f_pos = new); 190 } 191 192 static ssize_t read(struct file *file, char __user *buf, 193 size_t nbytes, loff_t *ppos) 194 { 195 struct ctrl_dbg *dbg = file->private_data; 196 return simple_read_from_buffer(buf, nbytes, ppos, dbg->data, dbg->size); 197 } 198 199 static int release(struct inode *inode, struct file *file) 200 { 201 struct ctrl_dbg *dbg = file->private_data; 202 203 kfree(dbg->data); 204 kfree(dbg); 205 return 0; 206 } 207 208 static const struct file_operations debug_ops = { 209 .owner = THIS_MODULE, 210 .open = open, 211 .llseek = lseek, 212 .read = read, 213 .release = release, 214 }; 215 216 static struct dentry *root; 217 218 void cpqhp_initialize_debugfs(void) 219 { 220 if (!root) 221 root = debugfs_create_dir("cpqhp", NULL); 222 } 223 224 void cpqhp_shutdown_debugfs(void) 225 { 226 debugfs_remove(root); 227 } 228 229 void cpqhp_create_debugfs_files(struct controller *ctrl) 230 { 231 ctrl->dentry = debugfs_create_file(dev_name(&ctrl->pci_dev->dev), 232 S_IRUGO, root, ctrl, &debug_ops); 233 } 234 235 void cpqhp_remove_debugfs_files(struct controller *ctrl) 236 { 237 if (ctrl->dentry) 238 debugfs_remove(ctrl->dentry); 239 ctrl->dentry = NULL; 240 } 241 242