Merge branch 'fixes-jgarzik' of git://git.kernel.org/pub/scm/linux/kernel/git/linvill...
[linux-2.6] / drivers / scsi / gdth_proc.c
1 /* gdth_proc.c 
2  * $Id: gdth_proc.c,v 1.43 2006/01/11 16:15:00 achim Exp $
3  */
4
5 #include <linux/completion.h>
6
7 int gdth_proc_info(struct Scsi_Host *host, char *buffer,char **start,off_t offset,int length,   
8                    int inout)
9 {
10     gdth_ha_str *ha = shost_priv(host);
11
12     TRACE2(("gdth_proc_info() length %d offs %d inout %d\n",
13             length,(int)offset,inout));
14
15     if (inout)
16         return(gdth_set_info(buffer,length,host,ha));
17     else
18         return(gdth_get_info(buffer,start,offset,length,host,ha));
19 }
20
21 static int gdth_set_info(char *buffer,int length,struct Scsi_Host *host,
22                          gdth_ha_str *ha)
23 {
24     int ret_val = -EINVAL;
25
26     TRACE2(("gdth_set_info() ha %d\n",ha->hanum,));
27
28     if (length >= 4) {
29         if (strncmp(buffer,"gdth",4) == 0) {
30             buffer += 5;
31             length -= 5;
32             ret_val = gdth_set_asc_info(host, buffer, length, ha);
33         }
34     }
35
36     return ret_val;
37 }
38          
39 static int gdth_set_asc_info(struct Scsi_Host *host, char *buffer,
40                         int length, gdth_ha_str *ha)
41 {
42     int orig_length, drive, wb_mode;
43     int i, found;
44     gdth_cmd_str    gdtcmd;
45     gdth_cpar_str   *pcpar;
46     ulong64         paddr;
47
48     char            cmnd[MAX_COMMAND_SIZE];
49     memset(cmnd, 0xff, 12);
50     memset(&gdtcmd, 0, sizeof(gdth_cmd_str));
51
52     TRACE2(("gdth_set_asc_info() ha %d\n",ha->hanum));
53     orig_length = length + 5;
54     drive = -1;
55     wb_mode = 0;
56     found = FALSE;
57
58     if (length >= 5 && strncmp(buffer,"flush",5)==0) {
59         buffer += 6;
60         length -= 6;
61         if (length && *buffer>='0' && *buffer<='9') {
62             drive = (int)(*buffer-'0');
63             ++buffer; --length;
64             if (length && *buffer>='0' && *buffer<='9') {
65                 drive = drive*10 + (int)(*buffer-'0');
66                 ++buffer; --length;
67             }
68             printk("GDT: Flushing host drive %d .. ",drive);
69         } else {
70             printk("GDT: Flushing all host drives .. ");
71         }
72         for (i = 0; i < MAX_HDRIVES; ++i) {
73             if (ha->hdr[i].present) {
74                 if (drive != -1 && i != drive)
75                     continue;
76                 found = TRUE;
77                 gdtcmd.Service = CACHESERVICE;
78                 gdtcmd.OpCode = GDT_FLUSH;
79                 if (ha->cache_feat & GDT_64BIT) {
80                     gdtcmd.u.cache64.DeviceNo = i;
81                     gdtcmd.u.cache64.BlockNo = 1;
82                 } else {
83                     gdtcmd.u.cache.DeviceNo = i;
84                     gdtcmd.u.cache.BlockNo = 1;
85                 }
86
87                 gdth_execute(host, &gdtcmd, cmnd, 30, NULL);
88             }
89         }
90         if (!found)
91             printk("\nNo host drive found !\n");
92         else
93             printk("Done.\n");
94         return(orig_length);
95     }
96
97     if (length >= 7 && strncmp(buffer,"wbp_off",7)==0) {
98         buffer += 8;
99         length -= 8;
100         printk("GDT: Disabling write back permanently .. ");
101         wb_mode = 1;
102     } else if (length >= 6 && strncmp(buffer,"wbp_on",6)==0) {
103         buffer += 7;
104         length -= 7;
105         printk("GDT: Enabling write back permanently .. ");
106         wb_mode = 2;
107     } else if (length >= 6 && strncmp(buffer,"wb_off",6)==0) {
108         buffer += 7;
109         length -= 7;
110         printk("GDT: Disabling write back commands .. ");
111         if (ha->cache_feat & GDT_WR_THROUGH) {
112             gdth_write_through = TRUE;
113             printk("Done.\n");
114         } else {
115             printk("Not supported !\n");
116         }
117         return(orig_length);
118     } else if (length >= 5 && strncmp(buffer,"wb_on",5)==0) {
119         buffer += 6;
120         length -= 6;
121         printk("GDT: Enabling write back commands .. ");
122         gdth_write_through = FALSE;
123         printk("Done.\n");
124         return(orig_length);
125     }
126
127     if (wb_mode) {
128         if (!gdth_ioctl_alloc(ha, sizeof(gdth_cpar_str), TRUE, &paddr))
129             return(-EBUSY);
130         pcpar = (gdth_cpar_str *)ha->pscratch;
131         memcpy( pcpar, &ha->cpar, sizeof(gdth_cpar_str) );
132         gdtcmd.Service = CACHESERVICE;
133         gdtcmd.OpCode = GDT_IOCTL;
134         gdtcmd.u.ioctl.p_param = paddr;
135         gdtcmd.u.ioctl.param_size = sizeof(gdth_cpar_str);
136         gdtcmd.u.ioctl.subfunc = CACHE_CONFIG;
137         gdtcmd.u.ioctl.channel = INVALID_CHANNEL;
138         pcpar->write_back = wb_mode==1 ? 0:1;
139
140         gdth_execute(host, &gdtcmd, cmnd, 30, NULL);
141
142         gdth_ioctl_free(ha, GDTH_SCRATCH, ha->pscratch, paddr);
143         printk("Done.\n");
144         return(orig_length);
145     }
146
147     printk("GDT: Unknown command: %s  Length: %d\n",buffer,length);
148     return(-EINVAL);
149 }
150
151 static int gdth_get_info(char *buffer,char **start,off_t offset,int length,
152                          struct Scsi_Host *host, gdth_ha_str *ha)
153 {
154     int size = 0,len = 0;
155     off_t begin = 0,pos = 0;
156     int id, i, j, k, sec, flag;
157     int no_mdrv = 0, drv_no, is_mirr;
158     ulong32 cnt;
159     ulong64 paddr;
160     int rc = -ENOMEM;
161
162     gdth_cmd_str *gdtcmd;
163     gdth_evt_str *estr;
164     char hrec[161];
165     struct timeval tv;
166
167     char *buf;
168     gdth_dskstat_str *pds;
169     gdth_diskinfo_str *pdi;
170     gdth_arrayinf_str *pai;
171     gdth_defcnt_str *pdef;
172     gdth_cdrinfo_str *pcdi;
173     gdth_hget_str *phg;
174     char cmnd[MAX_COMMAND_SIZE];
175
176     gdtcmd = kmalloc(sizeof(*gdtcmd), GFP_KERNEL);
177     estr = kmalloc(sizeof(*estr), GFP_KERNEL);
178     if (!gdtcmd || !estr)
179         goto free_fail;
180
181     memset(cmnd, 0xff, 12);
182     memset(gdtcmd, 0, sizeof(gdth_cmd_str));
183
184     TRACE2(("gdth_get_info() ha %d\n",ha->hanum));
185
186     
187     /* request is i.e. "cat /proc/scsi/gdth/0" */ 
188     /* format: %-15s\t%-10s\t%-15s\t%s */
189     /* driver parameters */
190     size = sprintf(buffer+len,"Driver Parameters:\n");
191     len += size;  pos = begin + len;
192     if (reserve_list[0] == 0xff)
193         strcpy(hrec, "--");
194     else {
195         sprintf(hrec, "%d", reserve_list[0]);
196         for (i = 1;  i < MAX_RES_ARGS; i++) {
197             if (reserve_list[i] == 0xff) 
198                 break;
199             sprintf(hrec,"%s,%d", hrec, reserve_list[i]);
200         }
201     }
202     size = sprintf(buffer+len,
203                    " reserve_mode: \t%d         \treserve_list:  \t%s\n",
204                    reserve_mode, hrec);
205     len += size;  pos = begin + len;
206     size = sprintf(buffer+len,
207                    " max_ids:      \t%-3d       \thdr_channel:   \t%d\n",
208                    max_ids, hdr_channel);
209     len += size;  pos = begin + len;
210
211     /* controller information */
212     size = sprintf(buffer+len,"\nDisk Array Controller Information:\n");
213     len += size;  pos = begin + len;
214     strcpy(hrec, ha->binfo.type_string);
215     size = sprintf(buffer+len,
216                    " Number:       \t%d         \tName:          \t%s\n",
217                    ha->hanum, hrec);
218     len += size;  pos = begin + len;
219
220     if (ha->more_proc)
221         sprintf(hrec, "%d.%02d.%02d-%c%03X", 
222                 (unchar)(ha->binfo.upd_fw_ver>>24),
223                 (unchar)(ha->binfo.upd_fw_ver>>16),
224                 (unchar)(ha->binfo.upd_fw_ver),
225                 ha->bfeat.raid ? 'R':'N',
226                 ha->binfo.upd_revision);
227     else
228         sprintf(hrec, "%d.%02d", (unchar)(ha->cpar.version>>8),
229                 (unchar)(ha->cpar.version));
230
231     size = sprintf(buffer+len,
232                    " Driver Ver.:  \t%-10s\tFirmware Ver.: \t%s\n",
233                    GDTH_VERSION_STR, hrec);
234     len += size;  pos = begin + len;
235  
236     if (ha->more_proc) {
237         /* more information: 1. about controller */
238         size = sprintf(buffer+len,
239                        " Serial No.:   \t0x%8X\tCache RAM size:\t%d KB\n",
240                        ha->binfo.ser_no, ha->binfo.memsize / 1024);
241         len += size;  pos = begin + len;
242     }
243
244 #ifdef GDTH_DMA_STATISTICS
245     /* controller statistics */
246     size = sprintf(buffer+len,"\nController Statistics:\n");
247     len += size;  pos = begin + len;
248     size = sprintf(buffer+len,
249                    " 32-bit DMA buffer:\t%lu\t64-bit DMA buffer:\t%lu\n",
250                    ha->dma32_cnt, ha->dma64_cnt);
251     len += size;  pos = begin + len;
252 #endif
253
254     if (pos < offset) {
255         len = 0;
256         begin = pos;
257     }
258     if (pos > offset + length)
259         goto stop_output;
260
261     if (ha->more_proc) {
262         /* more information: 2. about physical devices */
263         size = sprintf(buffer+len,"\nPhysical Devices:");
264         len += size;  pos = begin + len;
265         flag = FALSE;
266             
267         buf = gdth_ioctl_alloc(ha, GDTH_SCRATCH, FALSE, &paddr);
268         if (!buf) 
269             goto stop_output;
270         for (i = 0; i < ha->bus_cnt; ++i) {
271             /* 2.a statistics (and retries/reassigns) */
272             TRACE2(("pdr_statistics() chn %d\n",i));                
273             pds = (gdth_dskstat_str *)(buf + GDTH_SCRATCH/4);
274             gdtcmd->Service = CACHESERVICE;
275             gdtcmd->OpCode = GDT_IOCTL;
276             gdtcmd->u.ioctl.p_param = paddr + GDTH_SCRATCH/4;
277             gdtcmd->u.ioctl.param_size = 3*GDTH_SCRATCH/4;
278             gdtcmd->u.ioctl.subfunc = DSK_STATISTICS | L_CTRL_PATTERN;
279             gdtcmd->u.ioctl.channel = ha->raw[i].address | INVALID_CHANNEL;
280             pds->bid = ha->raw[i].local_no;
281             pds->first = 0;
282             pds->entries = ha->raw[i].pdev_cnt;
283             cnt = (3*GDTH_SCRATCH/4 - 5 * sizeof(ulong32)) /
284                 sizeof(pds->list[0]);
285             if (pds->entries > cnt)
286                 pds->entries = cnt;
287
288             if (gdth_execute(host, gdtcmd, cmnd, 30, NULL) != S_OK)
289                 pds->count = 0;
290
291             /* other IOCTLs must fit into area GDTH_SCRATCH/4 */
292             for (j = 0; j < ha->raw[i].pdev_cnt; ++j) {
293                 /* 2.b drive info */
294                 TRACE2(("scsi_drv_info() chn %d dev %d\n",
295                     i, ha->raw[i].id_list[j]));             
296                 pdi = (gdth_diskinfo_str *)buf;
297                 gdtcmd->Service = CACHESERVICE;
298                 gdtcmd->OpCode = GDT_IOCTL;
299                 gdtcmd->u.ioctl.p_param = paddr;
300                 gdtcmd->u.ioctl.param_size = sizeof(gdth_diskinfo_str);
301                 gdtcmd->u.ioctl.subfunc = SCSI_DR_INFO | L_CTRL_PATTERN;
302                 gdtcmd->u.ioctl.channel = 
303                     ha->raw[i].address | ha->raw[i].id_list[j];
304
305                 if (gdth_execute(host, gdtcmd, cmnd, 30, NULL) == S_OK) {
306                     strncpy(hrec,pdi->vendor,8);
307                     strncpy(hrec+8,pdi->product,16);
308                     strncpy(hrec+24,pdi->revision,4);
309                     hrec[28] = 0;
310                     size = sprintf(buffer+len,
311                                    "\n Chn/ID/LUN:   \t%c/%02d/%d    \tName:          \t%s\n",
312                                    'A'+i,pdi->target_id,pdi->lun,hrec);
313                     len += size;  pos = begin + len;
314                     flag = TRUE;
315                     pdi->no_ldrive &= 0xffff;
316                     if (pdi->no_ldrive == 0xffff)
317                         strcpy(hrec,"--");
318                     else
319                         sprintf(hrec,"%d",pdi->no_ldrive);
320                     size = sprintf(buffer+len,
321                                    " Capacity [MB]:\t%-6d    \tTo Log. Drive: \t%s\n",
322                                    pdi->blkcnt/(1024*1024/pdi->blksize),
323                                    hrec);
324                     len += size;  pos = begin + len;
325                 } else {
326                     pdi->devtype = 0xff;
327                 }
328                     
329                 if (pdi->devtype == 0) {
330                     /* search retries/reassigns */
331                     for (k = 0; k < pds->count; ++k) {
332                         if (pds->list[k].tid == pdi->target_id &&
333                             pds->list[k].lun == pdi->lun) {
334                             size = sprintf(buffer+len,
335                                            " Retries:      \t%-6d    \tReassigns:     \t%d\n",
336                                            pds->list[k].retries,
337                                            pds->list[k].reassigns);
338                             len += size;  pos = begin + len;
339                             break;
340                         }
341                     }
342                     /* 2.c grown defects */
343                     TRACE2(("scsi_drv_defcnt() chn %d dev %d\n",
344                             i, ha->raw[i].id_list[j]));             
345                     pdef = (gdth_defcnt_str *)buf;
346                     gdtcmd->Service = CACHESERVICE;
347                     gdtcmd->OpCode = GDT_IOCTL;
348                     gdtcmd->u.ioctl.p_param = paddr;
349                     gdtcmd->u.ioctl.param_size = sizeof(gdth_defcnt_str);
350                     gdtcmd->u.ioctl.subfunc = SCSI_DEF_CNT | L_CTRL_PATTERN;
351                     gdtcmd->u.ioctl.channel = 
352                         ha->raw[i].address | ha->raw[i].id_list[j];
353                     pdef->sddc_type = 0x08;
354
355                     if (gdth_execute(host, gdtcmd, cmnd, 30, NULL) == S_OK) {
356                         size = sprintf(buffer+len,
357                                        " Grown Defects:\t%d\n",
358                                        pdef->sddc_cnt);
359                         len += size;  pos = begin + len;
360                     }
361                 }
362                 if (pos < offset) {
363                     len = 0;
364                     begin = pos;
365                 }
366                 if (pos > offset + length)
367                     goto stop_output;
368             }
369         }
370         gdth_ioctl_free(ha, GDTH_SCRATCH, buf, paddr);
371
372         if (!flag) {
373             size = sprintf(buffer+len, "\n --\n");
374             len += size;  pos = begin + len;
375         }
376
377         /* 3. about logical drives */
378         size = sprintf(buffer+len,"\nLogical Drives:");
379         len += size;  pos = begin + len;
380         flag = FALSE;
381
382         buf = gdth_ioctl_alloc(ha, GDTH_SCRATCH, FALSE, &paddr);
383         if (!buf) 
384             goto stop_output;
385         for (i = 0; i < MAX_LDRIVES; ++i) {
386             if (!ha->hdr[i].is_logdrv)
387                 continue;
388             drv_no = i;
389             j = k = 0;
390             is_mirr = FALSE;
391             do {
392                 /* 3.a log. drive info */
393                 TRACE2(("cache_drv_info() drive no %d\n",drv_no));
394                 pcdi = (gdth_cdrinfo_str *)buf;
395                 gdtcmd->Service = CACHESERVICE;
396                 gdtcmd->OpCode = GDT_IOCTL;
397                 gdtcmd->u.ioctl.p_param = paddr;
398                 gdtcmd->u.ioctl.param_size = sizeof(gdth_cdrinfo_str);
399                 gdtcmd->u.ioctl.subfunc = CACHE_DRV_INFO;
400                 gdtcmd->u.ioctl.channel = drv_no;
401                 if (gdth_execute(host, gdtcmd, cmnd, 30, NULL) != S_OK)
402                     break;
403                 pcdi->ld_dtype >>= 16;
404                 j++;
405                 if (pcdi->ld_dtype > 2) {
406                     strcpy(hrec, "missing");
407                 } else if (pcdi->ld_error & 1) {
408                     strcpy(hrec, "fault");
409                 } else if (pcdi->ld_error & 2) {
410                     strcpy(hrec, "invalid");
411                     k++; j--;
412                 } else {
413                     strcpy(hrec, "ok");
414                 }
415                     
416                 if (drv_no == i) {
417                     size = sprintf(buffer+len,
418                                    "\n Number:       \t%-2d        \tStatus:        \t%s\n",
419                                    drv_no, hrec);
420                     len += size;  pos = begin + len;
421                     flag = TRUE;
422                     no_mdrv = pcdi->cd_ldcnt;
423                     if (no_mdrv > 1 || pcdi->ld_slave != -1) {
424                         is_mirr = TRUE;
425                         strcpy(hrec, "RAID-1");
426                     } else if (pcdi->ld_dtype == 0) {
427                         strcpy(hrec, "Disk");
428                     } else if (pcdi->ld_dtype == 1) {
429                         strcpy(hrec, "RAID-0");
430                     } else if (pcdi->ld_dtype == 2) {
431                         strcpy(hrec, "Chain");
432                     } else {
433                         strcpy(hrec, "???");
434                     }
435                     size = sprintf(buffer+len,
436                                    " Capacity [MB]:\t%-6d    \tType:          \t%s\n",
437                                    pcdi->ld_blkcnt/(1024*1024/pcdi->ld_blksize),
438                                    hrec);
439                     len += size;  pos = begin + len;
440                 } else {
441                     size = sprintf(buffer+len,
442                                    " Slave Number: \t%-2d        \tStatus:        \t%s\n",
443                                    drv_no & 0x7fff, hrec);
444                     len += size;  pos = begin + len;
445                 }
446                 drv_no = pcdi->ld_slave;
447                 if (pos < offset) {
448                     len = 0;
449                     begin = pos;
450                 }
451                 if (pos > offset + length)
452                     goto stop_output;
453             } while (drv_no != -1);
454              
455             if (is_mirr) {
456                 size = sprintf(buffer+len,
457                                " Missing Drv.: \t%-2d        \tInvalid Drv.:  \t%d\n",
458                                no_mdrv - j - k, k);
459                 len += size;  pos = begin + len;
460             }
461               
462             if (!ha->hdr[i].is_arraydrv)
463                 strcpy(hrec, "--");
464             else
465                 sprintf(hrec, "%d", ha->hdr[i].master_no);
466             size = sprintf(buffer+len,
467                            " To Array Drv.:\t%s\n", hrec);
468             len += size;  pos = begin + len;
469             if (pos < offset) {
470                 len = 0;
471                 begin = pos;
472             }
473             if (pos > offset + length)
474                 goto stop_output;
475         }       
476         gdth_ioctl_free(ha, GDTH_SCRATCH, buf, paddr);
477         
478         if (!flag) {
479             size = sprintf(buffer+len, "\n --\n");
480             len += size;  pos = begin + len;
481         }   
482
483         /* 4. about array drives */
484         size = sprintf(buffer+len,"\nArray Drives:");
485         len += size;  pos = begin + len;
486         flag = FALSE;
487
488         buf = gdth_ioctl_alloc(ha, GDTH_SCRATCH, FALSE, &paddr);
489         if (!buf) 
490             goto stop_output;
491         for (i = 0; i < MAX_LDRIVES; ++i) {
492             if (!(ha->hdr[i].is_arraydrv && ha->hdr[i].is_master))
493                 continue;
494             /* 4.a array drive info */
495             TRACE2(("array_info() drive no %d\n",i));
496             pai = (gdth_arrayinf_str *)buf;
497             gdtcmd->Service = CACHESERVICE;
498             gdtcmd->OpCode = GDT_IOCTL;
499             gdtcmd->u.ioctl.p_param = paddr;
500             gdtcmd->u.ioctl.param_size = sizeof(gdth_arrayinf_str);
501             gdtcmd->u.ioctl.subfunc = ARRAY_INFO | LA_CTRL_PATTERN;
502             gdtcmd->u.ioctl.channel = i;
503             if (gdth_execute(host, gdtcmd, cmnd, 30, NULL) == S_OK) {
504                 if (pai->ai_state == 0)
505                     strcpy(hrec, "idle");
506                 else if (pai->ai_state == 2)
507                     strcpy(hrec, "build");
508                 else if (pai->ai_state == 4)
509                     strcpy(hrec, "ready");
510                 else if (pai->ai_state == 6)
511                     strcpy(hrec, "fail");
512                 else if (pai->ai_state == 8 || pai->ai_state == 10)
513                     strcpy(hrec, "rebuild");
514                 else
515                     strcpy(hrec, "error");
516                 if (pai->ai_ext_state & 0x10)
517                     strcat(hrec, "/expand");
518                 else if (pai->ai_ext_state & 0x1)
519                     strcat(hrec, "/patch");
520                 size = sprintf(buffer+len,
521                                "\n Number:       \t%-2d        \tStatus:        \t%s\n",
522                                i,hrec);
523                 len += size;  pos = begin + len;
524                 flag = TRUE;
525
526                 if (pai->ai_type == 0)
527                     strcpy(hrec, "RAID-0");
528                 else if (pai->ai_type == 4)
529                     strcpy(hrec, "RAID-4");
530                 else if (pai->ai_type == 5)
531                     strcpy(hrec, "RAID-5");
532                 else 
533                     strcpy(hrec, "RAID-10");
534                 size = sprintf(buffer+len,
535                                " Capacity [MB]:\t%-6d    \tType:          \t%s\n",
536                                pai->ai_size/(1024*1024/pai->ai_secsize),
537                                hrec);
538                 len += size;  pos = begin + len;
539                 if (pos < offset) {
540                     len = 0;
541                     begin = pos;
542                 }
543                 if (pos > offset + length)
544                     goto stop_output;
545             }
546         }
547         gdth_ioctl_free(ha, GDTH_SCRATCH, buf, paddr);
548         
549         if (!flag) {
550             size = sprintf(buffer+len, "\n --\n");
551             len += size;  pos = begin + len;
552         }
553
554         /* 5. about host drives */
555         size = sprintf(buffer+len,"\nHost Drives:");
556         len += size;  pos = begin + len;
557         flag = FALSE;
558
559         buf = gdth_ioctl_alloc(ha, sizeof(gdth_hget_str), FALSE, &paddr);
560         if (!buf) 
561             goto stop_output;
562         for (i = 0; i < MAX_LDRIVES; ++i) {
563             if (!ha->hdr[i].is_logdrv || 
564                 (ha->hdr[i].is_arraydrv && !ha->hdr[i].is_master))
565                 continue;
566             /* 5.a get host drive list */
567             TRACE2(("host_get() drv_no %d\n",i));           
568             phg = (gdth_hget_str *)buf;
569             gdtcmd->Service = CACHESERVICE;
570             gdtcmd->OpCode = GDT_IOCTL;
571             gdtcmd->u.ioctl.p_param = paddr;
572             gdtcmd->u.ioctl.param_size = sizeof(gdth_hget_str);
573             gdtcmd->u.ioctl.subfunc = HOST_GET | LA_CTRL_PATTERN;
574             gdtcmd->u.ioctl.channel = i;
575             phg->entries = MAX_HDRIVES;
576             phg->offset = GDTOFFSOF(gdth_hget_str, entry[0]); 
577             if (gdth_execute(host, gdtcmd, cmnd, 30, NULL) == S_OK) {
578                 ha->hdr[i].ldr_no = i;
579                 ha->hdr[i].rw_attribs = 0;
580                 ha->hdr[i].start_sec = 0;
581             } else {
582                 for (j = 0; j < phg->entries; ++j) {
583                     k = phg->entry[j].host_drive;
584                     if (k >= MAX_LDRIVES)
585                         continue;
586                     ha->hdr[k].ldr_no = phg->entry[j].log_drive;
587                     ha->hdr[k].rw_attribs = phg->entry[j].rw_attribs;
588                     ha->hdr[k].start_sec = phg->entry[j].start_sec;
589                 }
590             }
591         }
592         gdth_ioctl_free(ha, sizeof(gdth_hget_str), buf, paddr);
593
594         for (i = 0; i < MAX_HDRIVES; ++i) {
595             if (!(ha->hdr[i].present))
596                 continue;
597               
598             size = sprintf(buffer+len,
599                            "\n Number:       \t%-2d        \tArr/Log. Drive:\t%d\n",
600                            i, ha->hdr[i].ldr_no);
601             len += size;  pos = begin + len;
602             flag = TRUE;
603
604             size = sprintf(buffer+len,
605                            " Capacity [MB]:\t%-6d    \tStart Sector:  \t%d\n",
606                            (ulong32)(ha->hdr[i].size/2048), ha->hdr[i].start_sec);
607             len += size;  pos = begin + len;
608             if (pos < offset) {
609                 len = 0;
610                 begin = pos;
611             }
612             if (pos > offset + length)
613                 goto stop_output;
614         }
615         
616         if (!flag) {
617             size = sprintf(buffer+len, "\n --\n");
618             len += size;  pos = begin + len;
619         }
620     }
621
622     /* controller events */
623     size = sprintf(buffer+len,"\nController Events:\n");
624     len += size;  pos = begin + len;
625
626     for (id = -1;;) {
627         id = gdth_read_event(ha, id, estr);
628         if (estr->event_source == 0)
629             break;
630         if (estr->event_data.eu.driver.ionode == ha->hanum &&
631             estr->event_source == ES_ASYNC) { 
632             gdth_log_event(&estr->event_data, hrec);
633             do_gettimeofday(&tv);
634             sec = (int)(tv.tv_sec - estr->first_stamp);
635             if (sec < 0) sec = 0;
636             size = sprintf(buffer+len," date- %02d:%02d:%02d\t%s\n",
637                            sec/3600, sec%3600/60, sec%60, hrec);
638             len += size;  pos = begin + len;
639             if (pos < offset) {
640                 len = 0;
641                 begin = pos;
642             }
643             if (pos > offset + length)
644                 goto stop_output;
645         }
646         if (id == -1)
647             break;
648     }
649
650 stop_output:
651     *start = buffer +(offset-begin);
652     len -= (offset-begin);
653     if (len > length)
654         len = length;
655     TRACE2(("get_info() len %d pos %d begin %d offset %d length %d size %d\n",
656             len,(int)pos,(int)begin,(int)offset,length,size));
657     rc = len;
658
659 free_fail:
660     kfree(gdtcmd);
661     kfree(estr);
662     return rc;
663 }
664
665 static char *gdth_ioctl_alloc(gdth_ha_str *ha, int size, int scratch,
666                               ulong64 *paddr)
667 {
668     ulong flags;
669     char *ret_val;
670
671     if (size == 0)
672         return NULL;
673
674     spin_lock_irqsave(&ha->smp_lock, flags);
675
676     if (!ha->scratch_busy && size <= GDTH_SCRATCH) {
677         ha->scratch_busy = TRUE;
678         ret_val = ha->pscratch;
679         *paddr = ha->scratch_phys;
680     } else if (scratch) {
681         ret_val = NULL;
682     } else {
683         dma_addr_t dma_addr;
684
685         ret_val = pci_alloc_consistent(ha->pdev, size, &dma_addr);
686         *paddr = dma_addr;
687     }
688
689     spin_unlock_irqrestore(&ha->smp_lock, flags);
690     return ret_val;
691 }
692
693 static void gdth_ioctl_free(gdth_ha_str *ha, int size, char *buf, ulong64 paddr)
694 {
695     ulong flags;
696
697     spin_lock_irqsave(&ha->smp_lock, flags);
698
699     if (buf == ha->pscratch) {
700         ha->scratch_busy = FALSE;
701     } else {
702         pci_free_consistent(ha->pdev, size, buf, paddr);
703     }
704
705     spin_unlock_irqrestore(&ha->smp_lock, flags);
706 }
707
708 #ifdef GDTH_IOCTL_PROC
709 static int gdth_ioctl_check_bin(gdth_ha_str *ha, ushort size)
710 {
711     ulong flags;
712     int ret_val;
713
714     spin_lock_irqsave(&ha->smp_lock, flags);
715
716     ret_val = FALSE;
717     if (ha->scratch_busy) {
718         if (((gdth_iord_str *)ha->pscratch)->size == (ulong32)size)
719             ret_val = TRUE;
720     }
721     spin_unlock_irqrestore(&ha->smp_lock, flags);
722     return ret_val;
723 }
724 #endif
725
726 static void gdth_wait_completion(gdth_ha_str *ha, int busnum, int id)
727 {
728     ulong flags;
729     int i;
730     Scsi_Cmnd *scp;
731     struct gdth_cmndinfo *cmndinfo;
732     unchar b, t;
733
734     spin_lock_irqsave(&ha->smp_lock, flags);
735
736     for (i = 0; i < GDTH_MAXCMDS; ++i) {
737         scp = ha->cmd_tab[i].cmnd;
738         cmndinfo = gdth_cmnd_priv(scp);
739
740         b = scp->device->channel;
741         t = scp->device->id;
742         if (!SPECIAL_SCP(scp) && t == (unchar)id && 
743             b == (unchar)busnum) {
744             cmndinfo->wait_for_completion = 0;
745             spin_unlock_irqrestore(&ha->smp_lock, flags);
746             while (!cmndinfo->wait_for_completion)
747                 barrier();
748             spin_lock_irqsave(&ha->smp_lock, flags);
749         }
750     }
751     spin_unlock_irqrestore(&ha->smp_lock, flags);
752 }
753
754 static void gdth_stop_timeout(gdth_ha_str *ha, int busnum, int id)
755 {
756     ulong flags;
757     Scsi_Cmnd *scp;
758     unchar b, t;
759
760     spin_lock_irqsave(&ha->smp_lock, flags);
761
762     for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) {
763         struct gdth_cmndinfo *cmndinfo = gdth_cmnd_priv(scp);
764         if (!cmndinfo->internal_command) {
765             b = scp->device->channel;
766             t = scp->device->id;
767             if (t == (unchar)id && b == (unchar)busnum) {
768                 TRACE2(("gdth_stop_timeout(): update_timeout()\n"));
769                 cmndinfo->timeout = gdth_update_timeout(scp, 0);
770             }
771         }
772     }
773     spin_unlock_irqrestore(&ha->smp_lock, flags);
774 }
775
776 static void gdth_start_timeout(gdth_ha_str *ha, int busnum, int id)
777 {
778     ulong flags;
779     Scsi_Cmnd *scp;
780     unchar b, t;
781
782     spin_lock_irqsave(&ha->smp_lock, flags);
783
784     for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) {
785         struct gdth_cmndinfo *cmndinfo = gdth_cmnd_priv(scp);
786         if (!cmndinfo->internal_command) {
787             b = scp->device->channel;
788             t = scp->device->id;
789             if (t == (unchar)id && b == (unchar)busnum) {
790                 TRACE2(("gdth_start_timeout(): update_timeout()\n"));
791                 gdth_update_timeout(scp, cmndinfo->timeout);
792             }
793         }
794     }
795     spin_unlock_irqrestore(&ha->smp_lock, flags);
796 }
797
798 static int gdth_update_timeout(Scsi_Cmnd *scp, int timeout)
799 {
800     int oldto;
801
802     oldto = scp->timeout_per_command;
803     scp->timeout_per_command = timeout;
804
805     if (timeout == 0) {
806         del_timer(&scp->eh_timeout);
807         scp->eh_timeout.data = (unsigned long) NULL;
808         scp->eh_timeout.expires = 0;
809     } else {
810         if (scp->eh_timeout.data != (unsigned long) NULL) 
811             del_timer(&scp->eh_timeout);
812         scp->eh_timeout.data = (unsigned long) scp;
813         scp->eh_timeout.expires = jiffies + timeout;
814         add_timer(&scp->eh_timeout);
815     }
816
817     return oldto;
818 }