1 /* 2 * Copyright (c) 2000-2001 Adaptec Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions, and the following disclaimer, 10 * without modification. 11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12 * substantially similar to the "NO WARRANTY" disclaimer below 13 * ("Disclaimer") and any redistribution must be conditioned upon 14 * including a substantially similar Disclaimer requirement for further 15 * binary redistribution. 16 * 3. Neither the names of the above-listed copyright holders nor the names 17 * of any contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * Alternatively, this software may be distributed under the terms of the 21 * GNU General Public License ("GPL") version 2 as published by the Free 22 * Software Foundation. 23 * 24 * NO WARRANTY 25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR 28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 34 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGES. 36 * 37 * String handling code courtesy of Gerard Roudier's <groudier@club-internet.fr> 38 * sym driver. 39 * 40 * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_proc.c#19 $ 41 */ 42 #include "aic79xx_osm.h" 43 #include "aic79xx_inline.h" 44 45 static void copy_mem_info(struct info_str *info, char *data, int len); 46 static int copy_info(struct info_str *info, char *fmt, ...); 47 static void ahd_dump_target_state(struct ahd_softc *ahd, 48 struct info_str *info, 49 u_int our_id, char channel, 50 u_int target_id, u_int target_offset); 51 static void ahd_dump_device_state(struct info_str *info, 52 struct scsi_device *sdev); 53 static int ahd_proc_write_seeprom(struct ahd_softc *ahd, 54 char *buffer, int length); 55 56 static void 57 copy_mem_info(struct info_str *info, char *data, int len) 58 { 59 if (info->pos + len > info->offset + info->length) 60 len = info->offset + info->length - info->pos; 61 62 if (info->pos + len < info->offset) { 63 info->pos += len; 64 return; 65 } 66 67 if (info->pos < info->offset) { 68 off_t partial; 69 70 partial = info->offset - info->pos; 71 data += partial; 72 info->pos += partial; 73 len -= partial; 74 } 75 76 if (len > 0) { 77 memcpy(info->buffer, data, len); 78 info->pos += len; 79 info->buffer += len; 80 } 81 } 82 83 static int 84 copy_info(struct info_str *info, char *fmt, ...) 85 { 86 va_list args; 87 char buf[256]; 88 int len; 89 90 va_start(args, fmt); 91 len = vsprintf(buf, fmt, args); 92 va_end(args); 93 94 copy_mem_info(info, buf, len); 95 return (len); 96 } 97 98 void 99 ahd_format_transinfo(struct info_str *info, struct ahd_transinfo *tinfo) 100 { 101 u_int speed; 102 u_int freq; 103 u_int mb; 104 105 if (tinfo->period == AHD_PERIOD_UNKNOWN) { 106 copy_info(info, "Renegotiation Pending\n"); 107 return; 108 } 109 speed = 3300; 110 freq = 0; 111 if (tinfo->offset != 0) { 112 freq = aic_calc_syncsrate(tinfo->period); 113 speed = freq; 114 } 115 speed *= (0x01 << tinfo->width); 116 mb = speed / 1000; 117 if (mb > 0) 118 copy_info(info, "%d.%03dMB/s transfers", mb, speed % 1000); 119 else 120 copy_info(info, "%dKB/s transfers", speed); 121 122 if (freq != 0) { 123 int printed_options; 124 125 printed_options = 0; 126 copy_info(info, " (%d.%03dMHz", freq / 1000, freq % 1000); 127 if ((tinfo->ppr_options & MSG_EXT_PPR_RD_STRM) != 0) { 128 copy_info(info, " RDSTRM"); 129 printed_options++; 130 } 131 if ((tinfo->ppr_options & MSG_EXT_PPR_DT_REQ) != 0) { 132 copy_info(info, "%s", printed_options ? "|DT" : " DT"); 133 printed_options++; 134 } 135 if ((tinfo->ppr_options & MSG_EXT_PPR_IU_REQ) != 0) { 136 copy_info(info, "%s", printed_options ? "|IU" : " IU"); 137 printed_options++; 138 } 139 if ((tinfo->ppr_options & MSG_EXT_PPR_RTI) != 0) { 140 copy_info(info, "%s", 141 printed_options ? "|RTI" : " RTI"); 142 printed_options++; 143 } 144 if ((tinfo->ppr_options & MSG_EXT_PPR_QAS_REQ) != 0) { 145 copy_info(info, "%s", 146 printed_options ? "|QAS" : " QAS"); 147 printed_options++; 148 } 149 } 150 151 if (tinfo->width > 0) { 152 if (freq != 0) { 153 copy_info(info, ", "); 154 } else { 155 copy_info(info, " ("); 156 } 157 copy_info(info, "%dbit)", 8 * (0x01 << tinfo->width)); 158 } else if (freq != 0) { 159 copy_info(info, ")"); 160 } 161 copy_info(info, "\n"); 162 } 163 164 static void 165 ahd_dump_target_state(struct ahd_softc *ahd, struct info_str *info, 166 u_int our_id, char channel, u_int target_id, 167 u_int target_offset) 168 { 169 struct ahd_linux_target *targ; 170 struct scsi_target *starget; 171 struct ahd_initiator_tinfo *tinfo; 172 struct ahd_tmode_tstate *tstate; 173 int lun; 174 175 tinfo = ahd_fetch_transinfo(ahd, channel, our_id, 176 target_id, &tstate); 177 copy_info(info, "Target %d Negotiation Settings\n", target_id); 178 copy_info(info, "\tUser: "); 179 ahd_format_transinfo(info, &tinfo->user); 180 starget = ahd->platform_data->starget[target_offset]; 181 if (starget == NULL) 182 return; 183 targ = scsi_transport_target_data(starget); 184 185 copy_info(info, "\tGoal: "); 186 ahd_format_transinfo(info, &tinfo->goal); 187 copy_info(info, "\tCurr: "); 188 ahd_format_transinfo(info, &tinfo->curr); 189 190 for (lun = 0; lun < AHD_NUM_LUNS; lun++) { 191 struct scsi_device *dev; 192 193 dev = targ->sdev[lun]; 194 195 if (dev == NULL) 196 continue; 197 198 ahd_dump_device_state(info, dev); 199 } 200 } 201 202 static void 203 ahd_dump_device_state(struct info_str *info, struct scsi_device *sdev) 204 { 205 struct ahd_linux_device *dev = scsi_transport_device_data(sdev); 206 207 copy_info(info, "\tChannel %c Target %d Lun %d Settings\n", 208 sdev->sdev_target->channel + 'A', 209 sdev->sdev_target->id, sdev->lun); 210 211 copy_info(info, "\t\tCommands Queued %ld\n", dev->commands_issued); 212 copy_info(info, "\t\tCommands Active %d\n", dev->active); 213 copy_info(info, "\t\tCommand Openings %d\n", dev->openings); 214 copy_info(info, "\t\tMax Tagged Openings %d\n", dev->maxtags); 215 copy_info(info, "\t\tDevice Queue Frozen Count %d\n", dev->qfrozen); 216 } 217 218 static int 219 ahd_proc_write_seeprom(struct ahd_softc *ahd, char *buffer, int length) 220 { 221 ahd_mode_state saved_modes; 222 int have_seeprom; 223 u_long s; 224 int paused; 225 int written; 226 227 /* Default to failure. */ 228 written = -EINVAL; 229 ahd_lock(ahd, &s); 230 paused = ahd_is_paused(ahd); 231 if (!paused) 232 ahd_pause(ahd); 233 234 saved_modes = ahd_save_modes(ahd); 235 ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI); 236 if (length != sizeof(struct seeprom_config)) { 237 printf("ahd_proc_write_seeprom: incorrect buffer size\n"); 238 goto done; 239 } 240 241 have_seeprom = ahd_verify_cksum((struct seeprom_config*)buffer); 242 if (have_seeprom == 0) { 243 printf("ahd_proc_write_seeprom: cksum verification failed\n"); 244 goto done; 245 } 246 247 have_seeprom = ahd_acquire_seeprom(ahd); 248 if (!have_seeprom) { 249 printf("ahd_proc_write_seeprom: No Serial EEPROM\n"); 250 goto done; 251 } else { 252 u_int start_addr; 253 254 if (ahd->seep_config == NULL) { 255 ahd->seep_config = malloc(sizeof(*ahd->seep_config), 256 M_DEVBUF, M_NOWAIT); 257 if (ahd->seep_config == NULL) { 258 printf("aic79xx: Unable to allocate serial " 259 "eeprom buffer. Write failing\n"); 260 goto done; 261 } 262 } 263 printf("aic79xx: Writing Serial EEPROM\n"); 264 start_addr = 32 * (ahd->channel - 'A'); 265 ahd_write_seeprom(ahd, (u_int16_t *)buffer, start_addr, 266 sizeof(struct seeprom_config)/2); 267 ahd_read_seeprom(ahd, (uint16_t *)ahd->seep_config, 268 start_addr, sizeof(struct seeprom_config)/2, 269 /*ByteStream*/FALSE); 270 ahd_release_seeprom(ahd); 271 written = length; 272 } 273 274 done: 275 ahd_restore_modes(ahd, saved_modes); 276 if (!paused) 277 ahd_unpause(ahd); 278 ahd_unlock(ahd, &s); 279 return (written); 280 } 281 /* 282 * Return information to handle /proc support for the driver. 283 */ 284 int 285 ahd_linux_proc_info(struct Scsi_Host *shost, char *buffer, char **start, 286 off_t offset, int length, int inout) 287 { 288 struct ahd_softc *ahd; 289 struct info_str info; 290 char ahd_info[256]; 291 u_long l; 292 u_int max_targ; 293 u_int i; 294 int retval; 295 296 retval = -EINVAL; 297 ahd_list_lock(&l); 298 ahd = ahd_find_softc(*(struct ahd_softc **)shost->hostdata); 299 300 if (ahd == NULL) 301 goto done; 302 303 /* Has data been written to the file? */ 304 if (inout == TRUE) { 305 retval = ahd_proc_write_seeprom(ahd, buffer, length); 306 goto done; 307 } 308 309 if (start) 310 *start = buffer; 311 312 info.buffer = buffer; 313 info.length = length; 314 info.offset = offset; 315 info.pos = 0; 316 317 copy_info(&info, "Adaptec AIC79xx driver version: %s\n", 318 AIC79XX_DRIVER_VERSION); 319 copy_info(&info, "%s\n", ahd->description); 320 ahd_controller_info(ahd, ahd_info); 321 copy_info(&info, "%s\n", ahd_info); 322 copy_info(&info, "Allocated SCBs: %d, SG List Length: %d\n\n", 323 ahd->scb_data.numscbs, AHD_NSEG); 324 325 max_targ = 15; 326 327 if (ahd->seep_config == NULL) 328 copy_info(&info, "No Serial EEPROM\n"); 329 else { 330 copy_info(&info, "Serial EEPROM:\n"); 331 for (i = 0; i < sizeof(*ahd->seep_config)/2; i++) { 332 if (((i % 8) == 0) && (i != 0)) { 333 copy_info(&info, "\n"); 334 } 335 copy_info(&info, "0x%.4x ", 336 ((uint16_t*)ahd->seep_config)[i]); 337 } 338 copy_info(&info, "\n"); 339 } 340 copy_info(&info, "\n"); 341 342 if ((ahd->features & AHD_WIDE) == 0) 343 max_targ = 7; 344 345 for (i = 0; i <= max_targ; i++) { 346 347 ahd_dump_target_state(ahd, &info, ahd->our_id, 'A', 348 /*target_id*/i, /*target_offset*/i); 349 } 350 retval = info.pos > info.offset ? info.pos - info.offset : 0; 351 done: 352 ahd_list_unlock(&l); 353 return (retval); 354 } 355