/* * -- tapealert_mts - tapealert magnetic tape * If an error was produced on the general magnetic tape interface, * then request the log sense tapealert page 0x2e for processing. */ int /* 0 successful */ tapealert_mts( char *fn, /* source filename */ int ln, /* source file line number */ int fd, /* device file descriptor */ dev_ent_t *un, /* device */ short mt_erreg) /* mtio error register */ { if (un == NULL) { return (1); } /* inspect mtio for unrecovered check condition */ if (UNRECOVERED_ERROR(mt_erreg)) { return (tapealert(fn, ln, fd, un, NULL, 0)); } return (0); }
/* * -- tapealert_skey - tapealert sense key * If a scsi command produced an unrecovered check condition, then * request the log sense tapealert page 0x2e for processing. */ int /* 0 successful */ tapealert_skey( char *fn, /* source filename */ int ln, /* source file line number */ int fd, /* device file descriptor */ dev_ent_t *un) /* device */ { sam_extended_sense_t *sense; if (un == NULL) { return (1); } /* inspect sense data for unrecovered check condition */ sense = (sam_extended_sense_t *)SHM_REF_ADDR(un->sense); if (UNRECOVERED_ERROR(sense->es_key)) { return (tapealert(fn, ln, fd, un, NULL, 0)); } return (0); }
// TODO, only handles drives where the interleave pattern is the same on all tracks // TODO, this won't work when heads >= 8 for WD1003 which truncates head // number in header to 3 bits. static void analyze_sectors(DRIVE_PARAMS *drive_params, int cyl, void *deltas, int max_deltas) { int msg_mask_hold; // We return a pointer to this so it must be static (may be better to malloc) static uint8_t interleave[MAX_SECTORS]; int unknown_interleave; int head_mismatch = 0; SECTOR_STATUS sector_status_list[MAX_SECTORS]; int head; int found_header; int unrecovered_error; int max_sector, min_sector, last_good_head; int i; SECTOR_DECODE_STATUS status; // TODD, this should also detect the WD controllers that support 16 heads but only // put 0-7 in the header. max_sector = 0; min_sector = MAX_SECTORS; last_good_head = -1; unrecovered_error = 0; unknown_interleave = 0; memset(interleave, 255, sizeof(interleave)); for (head = 0; head < MAX_HEAD && !head_mismatch; head++) { int found_bad_header; int err_count = 0; msg_mask_hold = msg_set_err_mask(decode_errors); // Try to get a good read. sector_status_list will be the best from // all the reads. do { drive_read_track(drive_params, cyl, head, deltas, max_deltas); mfm_init_sector_status_list(sector_status_list, drive_params->num_sectors); status = mfm_decode_track(drive_params, cyl, head, deltas, NULL, sector_status_list); if (UNRECOVERED_ERROR(status) && head == 8 && drive_params->controller == CONTROLLER_WD_1006) { int good_header = 0; for (i = 0; i < drive_params->num_sectors; i++) { if ((sector_status_list[i].status & SECT_HEADER_FOUND) && !(sector_status_list[i].status & SECT_BAD_HEADER)) { good_header = 1; } } // Mightyframe encodes head 8-15 differently. If we don't find // any good headers on head 8 see if its a Mightyframe. if (!good_header && head == 8) { drive_params->controller = CONTROLLER_MIGHTYFRAME; status = mfm_decode_track(drive_params, cyl, head, deltas, NULL, sector_status_list); for (i = 0; i < drive_params->num_sectors; i++) { if ((sector_status_list[i].status & SECT_HEADER_FOUND) && !(sector_status_list[i].status & SECT_BAD_HEADER)) { good_header = 1; } } if (good_header) { msg(MSG_FORMAT,"Changed controller type to %s\n", mfm_controller_info[drive_params->controller].name); } else { drive_params->controller = CONTROLLER_WD_1006; status = mfm_decode_track(drive_params, cyl, head, deltas, NULL, sector_status_list); } } } } while (UNRECOVERED_ERROR(status) && ++err_count < 8); msg_set_err_mask(msg_mask_hold); if (UNRECOVERED_ERROR(status)) { unrecovered_error = 1; } found_bad_header = 0; found_header = 0; for (i = 0; i < drive_params->num_sectors; i++) { // If we missed a header after finding one, stop looking at // interleave since it is likely to be wrong. With // read errors we still may get confused. if ((sector_status_list[i].status & SECT_BAD_HEADER) && found_header) { found_bad_header = 1; } if (sector_status_list[i].status & SECT_HEADER_FOUND) { found_header = 1; if (!found_bad_header) { if (interleave[sector_status_list[i].logical_sector] != 255 && interleave[sector_status_list[i].logical_sector] != sector_status_list[i].sector && !unknown_interleave) { msg(MSG_ERR, "Interleave mismatch previous entry %d, %d was %d now %d\n", i, sector_status_list[i].logical_sector, interleave[sector_status_list[i].logical_sector], sector_status_list[i].sector); unknown_interleave = 1; } interleave[sector_status_list[i].logical_sector] = sector_status_list[i].sector; } max_sector = MAX(max_sector, sector_status_list[i].sector); min_sector = MIN(min_sector, sector_status_list[i].sector); if (sector_status_list[i].head == head) { last_good_head = head; } else { if (!head_mismatch) { msg(MSG_INFO, "Selected head %d found %d, last good head found %d\n", head, sector_status_list[i].head, last_good_head); head_mismatch = 1; } } } } } // If we had a read error but got some good error warn. If nothing readable // assume we were trying to read an invalid head if (unrecovered_error && found_header) { msg(MSG_ERR, "Read errors trying to determine sector numbering, results may be in error\n"); } if (last_good_head == -1) { msg(MSG_FATAL, "Unable to determine number of heads\n"); exit(1); } // We store number of heads, not maximum head number (starting at 0) drive_params->num_head = last_good_head+1; drive_params->num_sectors = max_sector - min_sector + 1; drive_params->first_sector_number = min_sector; msg(MSG_INFO, "Number of heads %d number of sectors %d first sector %d\n", drive_params->num_head, drive_params->num_sectors, drive_params->first_sector_number); if (unknown_interleave) { msg(MSG_ERR, "Unable to determine interleave. Interleave value is not required\n"); drive_params->sector_numbers = NULL; } else { msg(MSG_INFO, "Interleave (not checked):"); for (i = 0; i < drive_params->num_sectors; i++) { msg(MSG_INFO, " %d",interleave[i]); } msg(MSG_INFO, "\n"); // Too many drives have cylinders with different interleave (spare, // for testing) that cause confusing errors so checking is disabled // unless users specified directly //drive_params->sector_numbers = interleave; drive_params->sector_numbers = NULL; } }
// Try to find the sector size and CRC parameters for the data portion of // sectors. // The data to analyze has already been read before routine called. // // drive_params: Drive parameters determined so far and return what we have determined // deltas: MFM delta time transition data to analyze // return: 0 ok, 1 multiple matches found, 2 unable to find format static int analyze_data(DRIVE_PARAMS *drive_params, int cyl, int head, void *deltas, int max_deltas) { // Loop variables int poly, init, size_ndx; int i; // The best match CRC and sector size info so far CRC_INFO data_crc_info; // And read status SECTOR_DECODE_STATUS status; int sector_size = 0; // Numbers of good sectors found int good_data_count, previous_good_data_count = 0; // Variable to restore global error print mask int msg_mask_hold; // The status of each sector decoded. SECTOR_STATUS sector_status_list[MAX_SECTORS]; // Return code int rc = 0; drive_read_track(drive_params, cyl, head, deltas, max_deltas); data_crc_info.poly = 0; // Try an exhaustive search of all the formats we know about. If we get too // many we may have to try something smarter. for (poly = mfm_controller_info[drive_params->controller].data_start_poly; poly < mfm_controller_info[drive_params->controller].data_end_poly; poly++) { drive_params->data_crc.poly = mfm_all_poly[poly].poly; drive_params->data_crc.length = mfm_all_poly[poly].length; // This sometimes gets false corrections when using wrong polynomial // We put it back when we save the best value. drive_params->data_crc.ecc_max_span = 0; for (init = mfm_controller_info[drive_params->controller].start_init; init < mfm_controller_info[drive_params->controller].end_init; init++) { // If not correct size don't try this initial value if (!(mfm_all_init[init].length == -1 || mfm_all_init[init].length == drive_params->data_crc.length)) { continue; } drive_params->data_crc.init_value = trim_value(mfm_all_init[init].value, drive_params->data_crc.length); for (size_ndx = 0; mfm_all_sector_size[size_ndx] != -1; size_ndx++) { drive_params->sector_size = mfm_all_sector_size[size_ndx]; mfm_init_sector_status_list(sector_status_list, drive_params->num_sectors); msg_mask_hold = msg_set_err_mask(decode_errors); // Decode track status = mfm_decode_track(drive_params, cyl, head, deltas, NULL, sector_status_list); msg_set_err_mask(msg_mask_hold); if (status & SECT_ZERO_DATA_CRC) { msg(MSG_DEBUG, "Found zero CRC data size %d:\n", drive_params->sector_size); print_crc_info(&drive_params->data_crc, MSG_DEBUG); } // Now find out how many good sectors we got with these parameters good_data_count = 0; for (i = 0; i < drive_params->num_sectors; i++) { if (!UNRECOVERED_ERROR(sector_status_list[i].status)) { good_data_count++; } } // If we found a good sector if (good_data_count > 0) { // If we have a previous match print both if (data_crc_info.poly != 0) { msg(MSG_ERR_SERIOUS, "Found multiple matching data CRC parameters. Largest matches will be used:\n"); msg(MSG_ERR_SERIOUS, "Matches %d sector size %d ", good_data_count, drive_params->sector_size); print_crc_info(&drive_params->data_crc, MSG_ERR_SERIOUS); msg(MSG_ERR_SERIOUS, "Matches %d sector size %d ", previous_good_data_count, sector_size); print_crc_info(&data_crc_info, MSG_ERR_SERIOUS); rc = 1; } // And keep the best if (good_data_count > previous_good_data_count) { data_crc_info = drive_params->data_crc; data_crc_info.ecc_max_span = mfm_all_poly[poly].ecc_span; sector_size = drive_params->sector_size; previous_good_data_count = good_data_count; } } } } } // Print what we found and put it in drive_params if (data_crc_info.poly != 0) { msg(MSG_INFO, "Sector length %d, Data CRC Information:\n", sector_size); print_crc_info(&data_crc_info, MSG_INFO); } else { // This is fatal since if we can't continue to next analysis msg(MSG_FATAL, "Unable to determine DATA CRC type\n"); rc = 2; } drive_params->sector_size = sector_size; drive_params->data_crc = data_crc_info; return rc; }