summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/aic7xxx_proc.c
blob: a986c5e805d65103275643d7279eddd9dbe570a6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
/*+M*************************************************************************
 * Adaptec AIC7xxx device driver proc support for Linux.
 *
 * Copyright (c) 1995, 1996 Dean W. Gehnert
 *
 * 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, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * ----------------------------------------------------------------
 *  o Modified from the EATA-DMA /proc support.
 *  o Additional support for device block statistics provided by
 *    Matthew Jacob.
 *  o Correction of overflow by Heinz Mauelshagen
 *  o Adittional corrections by Doug Ledford
 *
 *  Dean W. Gehnert, deang@teleport.com, 05/01/96
 *
 *  $Id: aic7xxx_proc.c,v 4.1 1997/06/97 08:23:42 deang Exp $
 *-M*************************************************************************/

#include <linux/config.h>

#define	BLS	(&aic7xxx_buffer[size])
#define HDRB \
"             < 2K      2K+     4K+     8K+    16K+    32K+    64K+   128K+"

#ifdef PROC_DEBUG
extern int vsprintf(char *, const char *, va_list);

static void
proc_debug(const char *fmt, ...)
{
  va_list ap;
  char buf[256];

  va_start(ap, fmt);
  vsprintf(buf, fmt, ap);
  printk(buf);
  va_end(ap);
}
#else /* PROC_DEBUG */
#  define proc_debug(fmt, args...)
#endif /* PROC_DEBUG */

static int aic7xxx_buffer_size = 0;
static char *aic7xxx_buffer = NULL;


/*+F*************************************************************************
 * Function:
 *   aic7xxx_set_info
 *
 * Description:
 *   Set parameters for the driver from the /proc filesystem.
 *-F*************************************************************************/
int
aic7xxx_set_info(char *buffer, int length, struct Scsi_Host *HBAptr)
{
  proc_debug("aic7xxx_set_info(): %s\n", buffer);
  return (-ENOSYS);  /* Currently this is a no-op */
}


/*+F*************************************************************************
 * Function:
 *   aic7xxx_proc_info
 *
 * Description:
 *   Return information to handle /proc support for the driver.
 *-F*************************************************************************/
int
aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length, 
                    int hostno, int inout)
{
  struct Scsi_Host *HBAptr;
  struct aic7xxx_host *p;
  int    size = 0;
  unsigned char i;
  struct aic7xxx_xferstats *sp;
  unsigned char target;

  HBAptr = NULL;

  for(p=first_aic7xxx; p->host->host_no != hostno; p=p->next)
    ;

  if (!p)
  {
    size += sprintf(buffer, "Can't find adapter for host number %d\n", hostno);
    if (size > length)
    {
      return (size);
    }
    else
    {
      return (length);
    }
  }

  HBAptr = p->host;

  if (inout == TRUE) /* Has data been written to the file? */ 
  {
    return (aic7xxx_set_info(buffer, length, HBAptr));
  }

  p = (struct aic7xxx_host *) HBAptr->hostdata;

  /*
   * It takes roughly 1K of space to hold all relevant card info, not
   * counting any proc stats, so we start out with a 1.5k buffer size and
   * if proc_stats is defined, then we sweep the stats structure to see
   * how many drives we will be printing out for and add 384 bytes per
   * device with active stats.
   *
   * Hmmmm...that 1.5k seems to keep growing as items get added so they
   * can be easily viewed for debugging purposes.  So, we bumped that
   * 1.5k to 4k so we can quit having to bump it all the time.
   */

  size = 4096;
  for (target = 0; target < MAX_TARGETS; target++)
  {
    if (p->dev_flags[target] & DEVICE_PRESENT)
#ifdef AIC7XXX_PROC_STATS
      size += 512;
#else
      size += 256;
#endif
  }
  if (aic7xxx_buffer_size != size)
  {
    if (aic7xxx_buffer != NULL) 
    {
      kfree(aic7xxx_buffer);
      aic7xxx_buffer_size = 0;
    }
    aic7xxx_buffer = kmalloc(size, GFP_KERNEL);
  }
  if (aic7xxx_buffer == NULL)
  {
    size = sprintf(buffer, "AIC7xxx - kmalloc error at line %d\n",
        __LINE__);
    return size;
  }
  aic7xxx_buffer_size = size;

  size = 0;
  size += sprintf(BLS, "Adaptec AIC7xxx driver version: ");
  size += sprintf(BLS, "%s/", AIC7XXX_C_VERSION);
  size += sprintf(BLS, "%s", AIC7XXX_H_VERSION);
  size += sprintf(BLS, "\n");
  size += sprintf(BLS, "Compile Options:\n");
#ifdef CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT
  size += sprintf(BLS, "  TCQ Enabled By Default : Enabled\n");
#else
  size += sprintf(BLS, "  TCQ Enabled By Default : Disabled\n");
#endif
#ifdef AIC7XXX_PROC_STATS
  size += sprintf(BLS, "  AIC7XXX_PROC_STATS     : Enabled\n");
#else
  size += sprintf(BLS, "  AIC7XXX_PROC_STATS     : Disabled\n");
#endif
  size += sprintf(BLS, "  AIC7XXX_RESET_DELAY    : %d\n", AIC7XXX_RESET_DELAY);
  size += sprintf(BLS, "\n");
  size += sprintf(BLS, "Adapter Configuration:\n");
  size += sprintf(BLS, "           SCSI Adapter: %s\n",
      board_names[p->board_name_index]);
  if (p->flags & AHC_TWIN)
    size += sprintf(BLS, "                         Twin Channel\n");
  else
  {
    char *channel = "";
    char *ultra = "";
    char *wide = "Narrow ";
    if (p->flags & AHC_MULTI_CHANNEL)
    {
      channel = " Channel A";
      if (p->flags & (AHC_CHNLB|AHC_CHNLC))
        channel = (p->flags & AHC_CHNLB) ? " Channel B" : " Channel C";
    }
    if (p->features & AHC_WIDE)
      wide = "Wide ";
    if (p->features & AHC_ULTRA3)
    {
      switch(p->chip & AHC_CHIPID_MASK)
      {
        case AHC_AIC7892:
        case AHC_AIC7899:
          ultra = "Ultra-160/m LVD/SE ";
          break;
        default:
          ultra = "Ultra-3 LVD/SE ";
          break;
      }
    }
    else if (p->features & AHC_ULTRA2)
      ultra = "Ultra-2 LVD/SE ";
    else if (p->features & AHC_ULTRA)
      ultra = "Ultra ";
    size += sprintf(BLS, "                           %s%sController%s\n",
      ultra, wide, channel);
  }
  if( !(p->maddr) )
  {
    size += sprintf(BLS, "    Programmed I/O Base: %lx\n", p->base);
  }
  else
  {
    size += sprintf(BLS, "    PCI MMAPed I/O Base: 0x%lx\n", p->mbase);
  }
  if( (p->chip & (AHC_VL | AHC_EISA)) )
  {
    size += sprintf(BLS, "    BIOS Memory Address: 0x%08x\n", p->bios_address);
  }
  size += sprintf(BLS, " Adapter SEEPROM Config: %s\n",
          (p->flags & AHC_SEEPROM_FOUND) ? "SEEPROM found and used." :
         ((p->flags & AHC_USEDEFAULTS) ? "SEEPROM not found, using defaults." :
           "SEEPROM not found, using leftover BIOS values.") );
  size += sprintf(BLS, "      Adaptec SCSI BIOS: %s\n",
          (p->flags & AHC_BIOS_ENABLED) ? "Enabled" : "Disabled");
  size += sprintf(BLS, "                    IRQ: %d\n", HBAptr->irq);
  size += sprintf(BLS, "                   SCBs: Active %d, Max Active %d,\n",
            p->activescbs, p->max_activescbs);
  size += sprintf(BLS, "                         Allocated %d, HW %d, "
            "Page %d\n", p->scb_data->numscbs, p->scb_data->maxhscbs,
            p->scb_data->maxscbs);
  if (p->flags & AHC_EXTERNAL_SRAM)
    size += sprintf(BLS, "                         Using External SCB SRAM\n");
  size += sprintf(BLS, "             Interrupts: %ld", p->isr_count);
  if (p->chip & AHC_EISA)
  {
    size += sprintf(BLS, " %s\n",
        (p->pause & IRQMS) ? "(Level Sensitive)" : "(Edge Triggered)");
  }
  else
  {
    size += sprintf(BLS, "\n");
  }
  size += sprintf(BLS, "      BIOS Control Word: 0x%04x\n",
            p->bios_control);
  size += sprintf(BLS, "   Adapter Control Word: 0x%04x\n",
            p->adapter_control);
  size += sprintf(BLS, "   Extended Translation: %sabled\n",
      (p->flags & AHC_EXTEND_TRANS_A) ? "En" : "Dis");
  size += sprintf(BLS, "Disconnect Enable Flags: 0x%04x\n", p->discenable);
  if (p->features & (AHC_ULTRA | AHC_ULTRA2))
  {
    size += sprintf(BLS, "     Ultra Enable Flags: 0x%04x\n", p->ultraenb);
  }
  size += sprintf(BLS, " Tag Queue Enable Flags: 0x%04x\n", p->tagenable);
  size += sprintf(BLS, "Ordered Queue Tag Flags: 0x%04x\n", p->orderedtag);
  size += sprintf(BLS, "Default Tag Queue Depth: %d\n", AIC7XXX_CMDS_PER_DEVICE);
  size += sprintf(BLS, "    Tagged Queue By Device array for aic7xxx host "
                       "instance %d:\n", p->instance);
  size += sprintf(BLS, "      {");
  for(i=0; i < (MAX_TARGETS - 1); i++)
    size += sprintf(BLS, "%d,",aic7xxx_tag_info[p->instance].tag_commands[i]);
  size += sprintf(BLS, "%d}\n",aic7xxx_tag_info[p->instance].tag_commands[i]);
  size += sprintf(BLS, "    Actual queue depth per device for aic7xxx host "
                       "instance %d:\n", p->instance);
  size += sprintf(BLS, "      {");
  for(i=0; i < (MAX_TARGETS - 1); i++)
    size += sprintf(BLS, "%d,", p->dev_max_queue_depth[i]);
  size += sprintf(BLS, "%d}\n", p->dev_max_queue_depth[i]);

  size += sprintf(BLS, "\n");
  size += sprintf(BLS, "Statistics:\n\n");
  for (target = 0; target < MAX_TARGETS; target++)
  {
    sp = &p->stats[target];
    if ((p->dev_flags[target] & DEVICE_PRESENT) == 0)
    {
      continue;
    }
    if (p->features & AHC_TWIN)
    {
      size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
          p->host_no, (target >> 3), (target & 0x7), 0);
    }
    else
    {
      size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
          p->host_no, 0, target, 0);
    }
    size += sprintf(BLS, "  Device using %s/%s",
          (p->transinfo[target].cur_width == MSG_EXT_WDTR_BUS_16_BIT) ?
          "Wide" : "Narrow",
          (p->transinfo[target].cur_offset != 0) ?
          "Sync transfers at " : "Async transfers.\n" );
    if (p->transinfo[target].cur_offset != 0)
    {
      struct aic7xxx_syncrate *sync_rate;
      unsigned char options = p->transinfo[target].cur_options;
      int period = p->transinfo[target].cur_period;
      int rate = (p->transinfo[target].cur_width ==
                  MSG_EXT_WDTR_BUS_16_BIT) ? 1 : 0;

      sync_rate = aic7xxx_find_syncrate(p, &period, 0, &options);
      if (sync_rate != NULL)
      {
        size += sprintf(BLS, "%s MByte/sec, offset %d\n",
                        sync_rate->rate[rate],
                        p->transinfo[target].cur_offset );
      }
      else
      {
        size += sprintf(BLS, "3.3 MByte/sec, offset %d\n",
                        p->transinfo[target].cur_offset );
      }
    }
    size += sprintf(BLS, "  Transinfo settings: ");
    size += sprintf(BLS, "current(%d/%d/%d/%d), ",
                    p->transinfo[target].cur_period,
                    p->transinfo[target].cur_offset,
                    p->transinfo[target].cur_width,
                    p->transinfo[target].cur_options);
    size += sprintf(BLS, "goal(%d/%d/%d/%d), ",
                    p->transinfo[target].goal_period,
                    p->transinfo[target].goal_offset,
                    p->transinfo[target].goal_width,
                    p->transinfo[target].goal_options);
    size += sprintf(BLS, "user(%d/%d/%d/%d)\n",
                    p->transinfo[target].user_period,
                    p->transinfo[target].user_offset,
                    p->transinfo[target].user_width,
                    p->transinfo[target].user_options);
#ifdef AIC7XXX_PROC_STATS
    size += sprintf(BLS, "  Total transfers %ld (%ld reads and %ld writes)\n",
        sp->r_total + sp->w_total, sp->r_total, sp->w_total);
    size += sprintf(BLS, "%s\n", HDRB);
    size += sprintf(BLS, "   Reads:");
    for (i = 0; i < NUMBER(sp->r_bins); i++)
    {
      size += sprintf(BLS, " %7ld", sp->r_bins[i]);
    }
    size += sprintf(BLS, "\n");
    size += sprintf(BLS, "  Writes:");
    for (i = 0; i < NUMBER(sp->w_bins); i++)
    {
      size += sprintf(BLS, " %7ld", sp->w_bins[i]);
    }
    size += sprintf(BLS, "\n");
#else
    size += sprintf(BLS, "  Total transfers %ld (%ld reads and %ld writes)\n",
        sp->r_total + sp->w_total, sp->r_total, sp->w_total);
#endif /* AIC7XXX_PROC_STATS */
    size += sprintf(BLS, "\n\n");
  }

  if (size >= aic7xxx_buffer_size)
  {
    printk(KERN_WARNING "aic7xxx: Overflow in aic7xxx_proc.c\n");
  }

  if (offset > size - 1)
  {
    kfree(aic7xxx_buffer);
    aic7xxx_buffer = NULL;
    aic7xxx_buffer_size = length = 0;
    *start = NULL;
  }
  else
  {
    *start = &aic7xxx_buffer[offset];   /* Start of wanted data */
    if (size - offset < length)
    {
      length = size - offset;
    }
  }

  return (length);
}

/*
 * Overrides for Emacs so that we follow Linus's tabbing style.
 * Emacs will notice this stuff at the end of the file and automatically
 * adjust the settings for this buffer only.  This must remain at the end
 * of the file.
 * ---------------------------------------------------------------------------
 * Local variables:
 * c-indent-level: 2
 * c-brace-imaginary-offset: 0
 * c-brace-offset: -2
 * c-argdecl-indent: 2
 * c-label-offset: -2
 * c-continued-statement-offset: 2
 * c-continued-brace-offset: 0
 * indent-tabs-mode: nil
 * tab-width: 8
 * End:
 */