diff options
Diffstat (limited to 'drivers/scsi/aic7xxx/aic79xx_proc.c')
-rw-r--r-- | drivers/scsi/aic7xxx/aic79xx_proc.c | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/drivers/scsi/aic7xxx/aic79xx_proc.c b/drivers/scsi/aic7xxx/aic79xx_proc.c new file mode 100644 index 0000000..e01cd61 --- /dev/null +++ b/drivers/scsi/aic7xxx/aic79xx_proc.c @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2000-2001 Adaptec Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * String handling code courtesy of Gerard Roudier's <groudier@club-internet.fr> + * sym driver. + * + * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_proc.c#19 $ + */ +#include "aic79xx_osm.h" +#include "aic79xx_inline.h" + +static void copy_mem_info(struct info_str *info, char *data, int len); +static int copy_info(struct info_str *info, char *fmt, ...); +static void ahd_dump_target_state(struct ahd_softc *ahd, + struct info_str *info, + u_int our_id, char channel, + u_int target_id, u_int target_offset); +static void ahd_dump_device_state(struct info_str *info, + struct ahd_linux_device *dev); +static int ahd_proc_write_seeprom(struct ahd_softc *ahd, + char *buffer, int length); + +static void +copy_mem_info(struct info_str *info, char *data, int len) +{ + if (info->pos + len > info->offset + info->length) + len = info->offset + info->length - info->pos; + + if (info->pos + len < info->offset) { + info->pos += len; + return; + } + + if (info->pos < info->offset) { + off_t partial; + + partial = info->offset - info->pos; + data += partial; + info->pos += partial; + len -= partial; + } + + if (len > 0) { + memcpy(info->buffer, data, len); + info->pos += len; + info->buffer += len; + } +} + +static int +copy_info(struct info_str *info, char *fmt, ...) +{ + va_list args; + char buf[256]; + int len; + + va_start(args, fmt); + len = vsprintf(buf, fmt, args); + va_end(args); + + copy_mem_info(info, buf, len); + return (len); +} + +void +ahd_format_transinfo(struct info_str *info, struct ahd_transinfo *tinfo) +{ + u_int speed; + u_int freq; + u_int mb; + + if (tinfo->period == AHD_PERIOD_UNKNOWN) { + copy_info(info, "Renegotiation Pending\n"); + return; + } + speed = 3300; + freq = 0; + if (tinfo->offset != 0) { + freq = aic_calc_syncsrate(tinfo->period); + speed = freq; + } + speed *= (0x01 << tinfo->width); + mb = speed / 1000; + if (mb > 0) + copy_info(info, "%d.%03dMB/s transfers", mb, speed % 1000); + else + copy_info(info, "%dKB/s transfers", speed); + + if (freq != 0) { + int printed_options; + + printed_options = 0; + copy_info(info, " (%d.%03dMHz", freq / 1000, freq % 1000); + if ((tinfo->ppr_options & MSG_EXT_PPR_RD_STRM) != 0) { + copy_info(info, " RDSTRM"); + printed_options++; + } + if ((tinfo->ppr_options & MSG_EXT_PPR_DT_REQ) != 0) { + copy_info(info, "%s", printed_options ? "|DT" : " DT"); + printed_options++; + } + if ((tinfo->ppr_options & MSG_EXT_PPR_IU_REQ) != 0) { + copy_info(info, "%s", printed_options ? "|IU" : " IU"); + printed_options++; + } + if ((tinfo->ppr_options & MSG_EXT_PPR_RTI) != 0) { + copy_info(info, "%s", + printed_options ? "|RTI" : " RTI"); + printed_options++; + } + if ((tinfo->ppr_options & MSG_EXT_PPR_QAS_REQ) != 0) { + copy_info(info, "%s", + printed_options ? "|QAS" : " QAS"); + printed_options++; + } + } + + if (tinfo->width > 0) { + if (freq != 0) { + copy_info(info, ", "); + } else { + copy_info(info, " ("); + } + copy_info(info, "%dbit)", 8 * (0x01 << tinfo->width)); + } else if (freq != 0) { + copy_info(info, ")"); + } + copy_info(info, "\n"); +} + +static void +ahd_dump_target_state(struct ahd_softc *ahd, struct info_str *info, + u_int our_id, char channel, u_int target_id, + u_int target_offset) +{ + struct ahd_linux_target *targ; + struct ahd_initiator_tinfo *tinfo; + struct ahd_tmode_tstate *tstate; + int lun; + + tinfo = ahd_fetch_transinfo(ahd, channel, our_id, + target_id, &tstate); + copy_info(info, "Target %d Negotiation Settings\n", target_id); + copy_info(info, "\tUser: "); + ahd_format_transinfo(info, &tinfo->user); + targ = ahd->platform_data->targets[target_offset]; + if (targ == NULL) + return; + + copy_info(info, "\tGoal: "); + ahd_format_transinfo(info, &tinfo->goal); + copy_info(info, "\tCurr: "); + ahd_format_transinfo(info, &tinfo->curr); + copy_info(info, "\tTransmission Errors %ld\n", targ->errors_detected); + + for (lun = 0; lun < AHD_NUM_LUNS; lun++) { + struct ahd_linux_device *dev; + + dev = targ->devices[lun]; + + if (dev == NULL) + continue; + + ahd_dump_device_state(info, dev); + } +} + +static void +ahd_dump_device_state(struct info_str *info, struct ahd_linux_device *dev) +{ + copy_info(info, "\tChannel %c Target %d Lun %d Settings\n", + dev->target->channel + 'A', dev->target->target, dev->lun); + + copy_info(info, "\t\tCommands Queued %ld\n", dev->commands_issued); + copy_info(info, "\t\tCommands Active %d\n", dev->active); + copy_info(info, "\t\tCommand Openings %d\n", dev->openings); + copy_info(info, "\t\tMax Tagged Openings %d\n", dev->maxtags); + copy_info(info, "\t\tDevice Queue Frozen Count %d\n", dev->qfrozen); +} + +static int +ahd_proc_write_seeprom(struct ahd_softc *ahd, char *buffer, int length) +{ + ahd_mode_state saved_modes; + int have_seeprom; + u_long s; + int paused; + int written; + + /* Default to failure. */ + written = -EINVAL; + ahd_lock(ahd, &s); + paused = ahd_is_paused(ahd); + if (!paused) + ahd_pause(ahd); + + saved_modes = ahd_save_modes(ahd); + ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI); + if (length != sizeof(struct seeprom_config)) { + printf("ahd_proc_write_seeprom: incorrect buffer size\n"); + goto done; + } + + have_seeprom = ahd_verify_cksum((struct seeprom_config*)buffer); + if (have_seeprom == 0) { + printf("ahd_proc_write_seeprom: cksum verification failed\n"); + goto done; + } + + have_seeprom = ahd_acquire_seeprom(ahd); + if (!have_seeprom) { + printf("ahd_proc_write_seeprom: No Serial EEPROM\n"); + goto done; + } else { + u_int start_addr; + + if (ahd->seep_config == NULL) { + ahd->seep_config = malloc(sizeof(*ahd->seep_config), + M_DEVBUF, M_NOWAIT); + if (ahd->seep_config == NULL) { + printf("aic79xx: Unable to allocate serial " + "eeprom buffer. Write failing\n"); + goto done; + } + } + printf("aic79xx: Writing Serial EEPROM\n"); + start_addr = 32 * (ahd->channel - 'A'); + ahd_write_seeprom(ahd, (u_int16_t *)buffer, start_addr, + sizeof(struct seeprom_config)/2); + ahd_read_seeprom(ahd, (uint16_t *)ahd->seep_config, + start_addr, sizeof(struct seeprom_config)/2, + /*ByteStream*/FALSE); + ahd_release_seeprom(ahd); + written = length; + } + +done: + ahd_restore_modes(ahd, saved_modes); + if (!paused) + ahd_unpause(ahd); + ahd_unlock(ahd, &s); + return (written); +} +/* + * Return information to handle /proc support for the driver. + */ +int +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +ahd_linux_proc_info(char *buffer, char **start, off_t offset, + int length, int hostno, int inout) +#else +ahd_linux_proc_info(struct Scsi_Host *shost, char *buffer, char **start, + off_t offset, int length, int inout) +#endif +{ + struct ahd_softc *ahd; + struct info_str info; + char ahd_info[256]; + u_long l; + u_int max_targ; + u_int i; + int retval; + + retval = -EINVAL; + ahd_list_lock(&l); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + TAILQ_FOREACH(ahd, &ahd_tailq, links) { + if (ahd->platform_data->host->host_no == hostno) + break; + } +#else + ahd = ahd_find_softc(*(struct ahd_softc **)shost->hostdata); +#endif + + if (ahd == NULL) + goto done; + + /* Has data been written to the file? */ + if (inout == TRUE) { + retval = ahd_proc_write_seeprom(ahd, buffer, length); + goto done; + } + + if (start) + *start = buffer; + + info.buffer = buffer; + info.length = length; + info.offset = offset; + info.pos = 0; + + copy_info(&info, "Adaptec AIC79xx driver version: %s\n", + AIC79XX_DRIVER_VERSION); + copy_info(&info, "%s\n", ahd->description); + ahd_controller_info(ahd, ahd_info); + copy_info(&info, "%s\n", ahd_info); + copy_info(&info, "Allocated SCBs: %d, SG List Length: %d\n\n", + ahd->scb_data.numscbs, AHD_NSEG); + + max_targ = 15; + + if (ahd->seep_config == NULL) + copy_info(&info, "No Serial EEPROM\n"); + else { + copy_info(&info, "Serial EEPROM:\n"); + for (i = 0; i < sizeof(*ahd->seep_config)/2; i++) { + if (((i % 8) == 0) && (i != 0)) { + copy_info(&info, "\n"); + } + copy_info(&info, "0x%.4x ", + ((uint16_t*)ahd->seep_config)[i]); + } + copy_info(&info, "\n"); + } + copy_info(&info, "\n"); + + if ((ahd->features & AHD_WIDE) == 0) + max_targ = 7; + + for (i = 0; i <= max_targ; i++) { + + ahd_dump_target_state(ahd, &info, ahd->our_id, 'A', + /*target_id*/i, /*target_offset*/i); + } + retval = info.pos > info.offset ? info.pos - info.offset : 0; +done: + ahd_list_unlock(&l); + return (retval); +} |