1/*+M************************************************************************* 2 * Adaptec AIC7xxx device driver proc support for Linux. 3 * 4 * Copyright (c) 1995, 1996 Dean W. Gehnert 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2, or (at your option) 9 * any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; see the file COPYING. If not, write to 18 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 19 * 20 * ---------------------------------------------------------------- 21 * o Modified from the EATA-DMA /proc support. 22 * o Additional support for device block statistics provided by 23 * Matthew Jacob. 24 * o Correction of overflow by Heinz Mauelshagen 25 * o Adittional corrections by Doug Ledford 26 * 27 * Dean W. Gehnert, deang@teleport.com, 05/01/96 28 * 29 * $Id: aic7xxx_proc.c,v 1.1.1.1 2007/08/03 18:52:58 Exp $ 30 *-M*************************************************************************/ 31 32 33#define BLS (&aic7xxx_buffer[size]) 34#define HDRB \ 35" 0 - 4K 4 - 16K 16 - 64K 64 - 256K 256K - 1M 1M+" 36 37#ifdef PROC_DEBUG 38extern int vsprintf(char *, const char *, va_list); 39 40static void 41proc_debug(const char *fmt, ...) 42{ 43 va_list ap; 44 char buf[256]; 45 46 va_start(ap, fmt); 47 vsprintf(buf, fmt, ap); 48 printk(buf); 49 va_end(ap); 50} 51#else /* PROC_DEBUG */ 52# define proc_debug(fmt, args...) 53#endif /* PROC_DEBUG */ 54 55static int aic7xxx_buffer_size = 0; 56static char *aic7xxx_buffer = NULL; 57 58 59/*+F************************************************************************* 60 * Function: 61 * aic7xxx_set_info 62 * 63 * Description: 64 * Set parameters for the driver from the /proc filesystem. 65 *-F*************************************************************************/ 66static int 67aic7xxx_set_info(char *buffer, int length, struct Scsi_Host *HBAptr) 68{ 69 proc_debug("aic7xxx_set_info(): %s\n", buffer); 70 return (-ENOSYS); /* Currently this is a no-op */ 71} 72 73 74/*+F************************************************************************* 75 * Function: 76 * aic7xxx_proc_info 77 * 78 * Description: 79 * Return information to handle /proc support for the driver. 80 *-F*************************************************************************/ 81int 82aic7xxx_proc_info ( struct Scsi_Host *HBAptr, char *buffer, char **start, off_t offset, int length, 83 int inout) 84{ 85 struct aic7xxx_host *p; 86 struct aic_dev_data *aic_dev; 87 struct scsi_device *sdptr; 88 int size = 0; 89 unsigned char i; 90 unsigned char tindex; 91 92 for(p=first_aic7xxx; p && p->host != HBAptr; p=p->next) 93 ; 94 95 if (!p) 96 { 97 size += sprintf(buffer, "Can't find adapter for host number %d\n", HBAptr->host_no); 98 if (size > length) 99 { 100 return (size); 101 } 102 else 103 { 104 return (length); 105 } 106 } 107 108 if (inout == TRUE) /* Has data been written to the file? */ 109 { 110 return (aic7xxx_set_info(buffer, length, HBAptr)); 111 } 112 113 p = (struct aic7xxx_host *) HBAptr->hostdata; 114 115 /* 116 * It takes roughly 1K of space to hold all relevant card info, not 117 * counting any proc stats, so we start out with a 1.5k buffer size and 118 * if proc_stats is defined, then we sweep the stats structure to see 119 * how many drives we will be printing out for and add 384 bytes per 120 * device with active stats. 121 * 122 * Hmmmm...that 1.5k seems to keep growing as items get added so they 123 * can be easily viewed for debugging purposes. So, we bumped that 124 * 1.5k to 4k so we can quit having to bump it all the time. 125 */ 126 127 size = 4096; 128 list_for_each_entry(aic_dev, &p->aic_devs, list) 129 size += 512; 130 if (aic7xxx_buffer_size != size) 131 { 132 if (aic7xxx_buffer != NULL) 133 { 134 kfree(aic7xxx_buffer); 135 aic7xxx_buffer_size = 0; 136 } 137 aic7xxx_buffer = kmalloc(size, GFP_KERNEL); 138 } 139 if (aic7xxx_buffer == NULL) 140 { 141 size = sprintf(buffer, "AIC7xxx - kmalloc error at line %d\n", 142 __LINE__); 143 return size; 144 } 145 aic7xxx_buffer_size = size; 146 147 size = 0; 148 size += sprintf(BLS, "Adaptec AIC7xxx driver version: "); 149 size += sprintf(BLS, "%s/", AIC7XXX_C_VERSION); 150 size += sprintf(BLS, "%s", AIC7XXX_H_VERSION); 151 size += sprintf(BLS, "\n"); 152 size += sprintf(BLS, "Adapter Configuration:\n"); 153 size += sprintf(BLS, " SCSI Adapter: %s\n", 154 board_names[p->board_name_index]); 155 if (p->flags & AHC_TWIN) 156 size += sprintf(BLS, " Twin Channel Controller "); 157 else 158 { 159 char *channel = ""; 160 char *ultra = ""; 161 char *wide = "Narrow "; 162 if (p->flags & AHC_MULTI_CHANNEL) 163 { 164 channel = " Channel A"; 165 if (p->flags & (AHC_CHNLB|AHC_CHNLC)) 166 channel = (p->flags & AHC_CHNLB) ? " Channel B" : " Channel C"; 167 } 168 if (p->features & AHC_WIDE) 169 wide = "Wide "; 170 if (p->features & AHC_ULTRA3) 171 { 172 switch(p->chip & AHC_CHIPID_MASK) 173 { 174 case AHC_AIC7892: 175 case AHC_AIC7899: 176 ultra = "Ultra-160/m LVD/SE "; 177 break; 178 default: 179 ultra = "Ultra-3 LVD/SE "; 180 break; 181 } 182 } 183 else if (p->features & AHC_ULTRA2) 184 ultra = "Ultra-2 LVD/SE "; 185 else if (p->features & AHC_ULTRA) 186 ultra = "Ultra "; 187 size += sprintf(BLS, " %s%sController%s ", 188 ultra, wide, channel); 189 } 190 switch(p->chip & ~AHC_CHIPID_MASK) 191 { 192 case AHC_VL: 193 size += sprintf(BLS, "at VLB slot %d\n", p->pci_device_fn); 194 break; 195 case AHC_EISA: 196 size += sprintf(BLS, "at EISA slot %d\n", p->pci_device_fn); 197 break; 198 default: 199 size += sprintf(BLS, "at PCI %d/%d/%d\n", p->pci_bus, 200 PCI_SLOT(p->pci_device_fn), PCI_FUNC(p->pci_device_fn)); 201 break; 202 } 203 if( !(p->maddr) ) 204 { 205 size += sprintf(BLS, " Programmed I/O Base: %lx\n", p->base); 206 } 207 else 208 { 209 size += sprintf(BLS, " PCI MMAPed I/O Base: 0x%lx\n", p->mbase); 210 } 211 if( (p->chip & (AHC_VL | AHC_EISA)) ) 212 { 213 size += sprintf(BLS, " BIOS Memory Address: 0x%08x\n", p->bios_address); 214 } 215 size += sprintf(BLS, " Adapter SEEPROM Config: %s\n", 216 (p->flags & AHC_SEEPROM_FOUND) ? "SEEPROM found and used." : 217 ((p->flags & AHC_USEDEFAULTS) ? "SEEPROM not found, using defaults." : 218 "SEEPROM not found, using leftover BIOS values.") ); 219 size += sprintf(BLS, " Adaptec SCSI BIOS: %s\n", 220 (p->flags & AHC_BIOS_ENABLED) ? "Enabled" : "Disabled"); 221 size += sprintf(BLS, " IRQ: %d\n", HBAptr->irq); 222 size += sprintf(BLS, " SCBs: Active %d, Max Active %d,\n", 223 p->activescbs, p->max_activescbs); 224 size += sprintf(BLS, " Allocated %d, HW %d, " 225 "Page %d\n", p->scb_data->numscbs, p->scb_data->maxhscbs, 226 p->scb_data->maxscbs); 227 if (p->flags & AHC_EXTERNAL_SRAM) 228 size += sprintf(BLS, " Using External SCB SRAM\n"); 229 size += sprintf(BLS, " Interrupts: %ld", p->isr_count); 230 if (p->chip & AHC_EISA) 231 { 232 size += sprintf(BLS, " %s\n", 233 (p->pause & IRQMS) ? "(Level Sensitive)" : "(Edge Triggered)"); 234 } 235 else 236 { 237 size += sprintf(BLS, "\n"); 238 } 239 size += sprintf(BLS, " BIOS Control Word: 0x%04x\n", 240 p->bios_control); 241 size += sprintf(BLS, " Adapter Control Word: 0x%04x\n", 242 p->adapter_control); 243 size += sprintf(BLS, " Extended Translation: %sabled\n", 244 (p->flags & AHC_EXTEND_TRANS_A) ? "En" : "Dis"); 245 size += sprintf(BLS, "Disconnect Enable Flags: 0x%04x\n", p->discenable); 246 if (p->features & (AHC_ULTRA | AHC_ULTRA2)) 247 { 248 size += sprintf(BLS, " Ultra Enable Flags: 0x%04x\n", p->ultraenb); 249 } 250 size += sprintf(BLS, "Default Tag Queue Depth: %d\n", aic7xxx_default_queue_depth); 251 size += sprintf(BLS, " Tagged Queue By Device array for aic7xxx host " 252 "instance %d:\n", p->instance); 253 size += sprintf(BLS, " {"); 254 for(i=0; i < (MAX_TARGETS - 1); i++) 255 size += sprintf(BLS, "%d,",aic7xxx_tag_info[p->instance].tag_commands[i]); 256 size += sprintf(BLS, "%d}\n",aic7xxx_tag_info[p->instance].tag_commands[i]); 257 258 size += sprintf(BLS, "\n"); 259 size += sprintf(BLS, "Statistics:\n\n"); 260 list_for_each_entry(aic_dev, &p->aic_devs, list) 261 { 262 sdptr = aic_dev->SDptr; 263 tindex = sdptr->channel << 3 | sdptr->id; 264 size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n", 265 p->host_no, sdptr->channel, sdptr->id, sdptr->lun); 266 size += sprintf(BLS, " Device using %s/%s", 267 (aic_dev->cur.width == MSG_EXT_WDTR_BUS_16_BIT) ? 268 "Wide" : "Narrow", 269 (aic_dev->cur.offset != 0) ? 270 "Sync transfers at " : "Async transfers.\n" ); 271 if (aic_dev->cur.offset != 0) 272 { 273 struct aic7xxx_syncrate *sync_rate; 274 unsigned char options = aic_dev->cur.options; 275 int period = aic_dev->cur.period; 276 int rate = (aic_dev->cur.width == 277 MSG_EXT_WDTR_BUS_16_BIT) ? 1 : 0; 278 279 sync_rate = aic7xxx_find_syncrate(p, &period, 0, &options); 280 if (sync_rate != NULL) 281 { 282 size += sprintf(BLS, "%s MByte/sec, offset %d\n", 283 sync_rate->rate[rate], 284 aic_dev->cur.offset ); 285 } 286 else 287 { 288 size += sprintf(BLS, "3.3 MByte/sec, offset %d\n", 289 aic_dev->cur.offset ); 290 } 291 } 292 size += sprintf(BLS, " Transinfo settings: "); 293 size += sprintf(BLS, "current(%d/%d/%d/%d), ", 294 aic_dev->cur.period, 295 aic_dev->cur.offset, 296 aic_dev->cur.width, 297 aic_dev->cur.options); 298 size += sprintf(BLS, "goal(%d/%d/%d/%d), ", 299 aic_dev->goal.period, 300 aic_dev->goal.offset, 301 aic_dev->goal.width, 302 aic_dev->goal.options); 303 size += sprintf(BLS, "user(%d/%d/%d/%d)\n", 304 p->user[tindex].period, 305 p->user[tindex].offset, 306 p->user[tindex].width, 307 p->user[tindex].options); 308 if(sdptr->simple_tags) 309 { 310 size += sprintf(BLS, " Tagged Command Queueing Enabled, Ordered Tags %s, Depth %d/%d\n", sdptr->ordered_tags ? "Enabled" : "Disabled", sdptr->queue_depth, aic_dev->max_q_depth); 311 } 312 if(aic_dev->barrier_total) 313 size += sprintf(BLS, " Total transfers %ld:\n (%ld/%ld/%ld/%ld reads/writes/REQ_BARRIER/Ordered Tags)\n", 314 aic_dev->r_total+aic_dev->w_total, aic_dev->r_total, aic_dev->w_total, 315 aic_dev->barrier_total, aic_dev->ordered_total); 316 else 317 size += sprintf(BLS, " Total transfers %ld:\n (%ld/%ld reads/writes)\n", 318 aic_dev->r_total+aic_dev->w_total, aic_dev->r_total, aic_dev->w_total); 319 size += sprintf(BLS, "%s\n", HDRB); 320 size += sprintf(BLS, " Reads:"); 321 for (i = 0; i < ARRAY_SIZE(aic_dev->r_bins); i++) 322 { 323 size += sprintf(BLS, " %10ld", aic_dev->r_bins[i]); 324 } 325 size += sprintf(BLS, "\n"); 326 size += sprintf(BLS, " Writes:"); 327 for (i = 0; i < ARRAY_SIZE(aic_dev->w_bins); i++) 328 { 329 size += sprintf(BLS, " %10ld", aic_dev->w_bins[i]); 330 } 331 size += sprintf(BLS, "\n"); 332 size += sprintf(BLS, "\n\n"); 333 } 334 if (size >= aic7xxx_buffer_size) 335 { 336 printk(KERN_WARNING "aic7xxx: Overflow in aic7xxx_proc.c\n"); 337 } 338 339 if (offset > size - 1) 340 { 341 kfree(aic7xxx_buffer); 342 aic7xxx_buffer = NULL; 343 aic7xxx_buffer_size = length = 0; 344 *start = NULL; 345 } 346 else 347 { 348 *start = buffer; 349 length = min_t(int, length, size - offset); 350 memcpy(buffer, &aic7xxx_buffer[offset], length); 351 } 352 353 return (length); 354} 355 356/* 357 * Overrides for Emacs so that we follow Linus's tabbing style. 358 * Emacs will notice this stuff at the end of the file and automatically 359 * adjust the settings for this buffer only. This must remain at the end 360 * of the file. 361 * --------------------------------------------------------------------------- 362 * Local variables: 363 * c-indent-level: 2 364 * c-brace-imaginary-offset: 0 365 * c-brace-offset: -2 366 * c-argdecl-indent: 2 367 * c-label-offset: -2 368 * c-continued-statement-offset: 2 369 * c-continued-brace-offset: 0 370 * indent-tabs-mode: nil 371 * tab-width: 8 372 * End: 373 */ 374