int cd_setchan(struct cd_softc *cd, int p0, int p1, int p2, int p3, int flags) { union scsi_mode_sense_buf *data; struct cd_audio_page *audio = NULL; int error, big; data = malloc(sizeof(*data), M_TEMP, M_NOWAIT); if (data == NULL) return (ENOMEM); error = scsi_do_mode_sense(cd->sc_link, AUDIO_PAGE, data, (void **)&audio, NULL, NULL, NULL, sizeof(*audio), flags, &big); if (error == 0 && audio == NULL) error = EIO; if (error == 0) { audio->port[LEFT_PORT].channels = p0; audio->port[RIGHT_PORT].channels = p1; audio->port[2].channels = p2; audio->port[3].channels = p3; if (big) error = scsi_mode_select_big(cd->sc_link, SMS_PF, &data->hdr_big, flags, 20000); else error = scsi_mode_select(cd->sc_link, SMS_PF, &data->hdr, flags, 20000); } free(data, M_TEMP); return (error); }
int cd_setvol(struct cd_softc *cd, const struct ioc_vol *arg, int flags) { union scsi_mode_sense_buf *data; struct cd_audio_page *audio = NULL; u_int8_t mask_volume[4]; int error, big; data = malloc(sizeof(*data), M_TEMP, M_NOWAIT); if (data == NULL) return (ENOMEM); error = scsi_do_mode_sense(cd->sc_link, AUDIO_PAGE | SMS_PAGE_CTRL_CHANGEABLE, data, (void **)&audio, NULL, NULL, NULL, sizeof(*audio), flags, NULL); if (error == 0 && audio == NULL) error = EIO; if (error != 0) { free(data, M_TEMP); return (error); } mask_volume[0] = audio->port[0].volume; mask_volume[1] = audio->port[1].volume; mask_volume[2] = audio->port[2].volume; mask_volume[3] = audio->port[3].volume; error = scsi_do_mode_sense(cd->sc_link, AUDIO_PAGE, data, (void **)&audio, NULL, NULL, NULL, sizeof(*audio), flags, &big); if (error == 0 && audio == NULL) error = EIO; if (error != 0) { free(data, M_TEMP); return (error); } audio->port[0].volume = arg->vol[0] & mask_volume[0]; audio->port[1].volume = arg->vol[1] & mask_volume[1]; audio->port[2].volume = arg->vol[2] & mask_volume[2]; audio->port[3].volume = arg->vol[3] & mask_volume[3]; if (big) error = scsi_mode_select_big(cd->sc_link, SMS_PF, &data->hdr_big, flags, 20000); else error = scsi_mode_select(cd->sc_link, SMS_PF, &data->hdr, flags, 20000); free(data, M_TEMP); return (error); }
static ssize_t sd_store_cache_type(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int i, ct = -1, rcd, wce, sp; struct scsi_disk *sdkp = to_scsi_disk(dev); struct scsi_device *sdp = sdkp->device; char buffer[64]; char *buffer_data; struct scsi_mode_data data; struct scsi_sense_hdr sshdr; int len; if (sdp->type != TYPE_DISK) /* no cache control on RBC devices; theoretically they * can do it, but there's probably so many exceptions * it's not worth the risk */ return -EINVAL; for (i = 0; i < ARRAY_SIZE(sd_cache_types); i++) { len = strlen(sd_cache_types[i]); if (strncmp(sd_cache_types[i], buf, len) == 0 && buf[len] == '\n') { ct = i; break; } } if (ct < 0) return -EINVAL; rcd = ct & 0x01 ? 1 : 0; wce = ct & 0x02 ? 1 : 0; if (scsi_mode_sense(sdp, 0x08, 8, buffer, sizeof(buffer), SD_TIMEOUT, SD_MAX_RETRIES, &data, NULL)) return -EINVAL; len = min_t(size_t, sizeof(buffer), data.length - data.header_length - data.block_descriptor_length); buffer_data = buffer + data.header_length + data.block_descriptor_length; buffer_data[2] &= ~0x05; buffer_data[2] |= wce << 2 | rcd; sp = buffer_data[0] & 0x80 ? 1 : 0; if (scsi_mode_select(sdp, 1, sp, 8, buffer_data, len, SD_TIMEOUT, SD_MAX_RETRIES, &data, &sshdr)) { if (scsi_sense_valid(&sshdr)) sd_print_sense_hdr(sdkp, &sshdr); return -EINVAL; } revalidate_disk(sdkp->disk); return count; }
/* * Clear the GLTSD bit, indicating log pages should be saved to non-volatile * storage. */ static int clear_gltsd(ds_scsi_info_t *sip) { scsi_ms_hdrs_t hdrs, junk_hdrs; struct mode_control_scsi3 control_pg_cur, control_pg_chg; int result; uint_t skey, asc, ascq; bzero(&hdrs, sizeof (hdrs)); bzero(&control_pg_cur, sizeof (control_pg_cur)); bzero(&control_pg_chg, sizeof (control_pg_chg)); result = scsi_mode_sense(sip, MODEPAGE_CTRL_MODE, PC_CURRENT, &control_pg_cur, MODEPAGE_CTRL_MODE_LEN, &hdrs, &skey, &asc, &ascq); if (result != 0) { printf("failed to read Control mode page (KEY=0x%x " "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq); } else if (control_pg_cur.mode_page.length != PAGELENGTH_MODE_CONTROL_SCSI3) { printf("SCSI-3 control mode page not supported\n"); } else if ((result = scsi_mode_sense(sip, MODEPAGE_CTRL_MODE, PC_CHANGEABLE, &control_pg_chg, MODEPAGE_CTRL_MODE_LEN, &junk_hdrs, &skey, &asc, &ascq)) != 0) { printf("failed to read changeable Control mode page (KEY=0x%x " "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq); } else if (control_pg_cur.gltsd && !GLTSD_CHANGEABLE(control_pg_chg)) { printf("gltsd is set and not changeable\n"); if (nvlist_add_boolean_value(sip->si_dsp->ds_state, "gltsd", control_pg_cur.gltsd) != 0) return (scsi_set_errno(sip, EDS_NOMEM)); } else if (control_pg_cur.gltsd) { control_pg_cur.gltsd = 0; result = scsi_mode_select(sip, MODEPAGE_CTRL_MODE, MODE_SELECT_PF, &control_pg_cur, MODEPAGE_CTRL_MODE_LEN, &hdrs, &skey, &asc, &ascq); if (result != 0) printf("failed to enable GLTSD (KEY=0x%x " "ASC=0x%x ASCQ=0x%x\n", skey, asc, ascq); if (nvlist_add_boolean_value(sip->si_dsp->ds_state, "gltsd", control_pg_cur.gltsd) != 0) return (scsi_set_errno(sip, EDS_NOMEM)); } return (0); }
int cd_set_pa_immed(struct cd_softc *cd, int flags) { union scsi_mode_sense_buf *data; struct cd_audio_page *audio = NULL; int error, oflags, big; if (cd->sc_link->flags & SDEV_ATAPI) /* XXX Noop? */ return (0); data = malloc(sizeof(*data), M_TEMP, M_NOWAIT); if (data == NULL) return (ENOMEM); error = scsi_do_mode_sense(cd->sc_link, AUDIO_PAGE, data, (void **)&audio, NULL, NULL, NULL, sizeof(*audio), flags, &big); if (error == 0 && audio == NULL) error = EIO; if (error == 0) { oflags = audio->flags; audio->flags &= ~CD_PA_SOTC; audio->flags |= CD_PA_IMMED; if (audio->flags != oflags) { if (big) error = scsi_mode_select_big(cd->sc_link, SMS_PF, &data->hdr_big, flags, 20000); else error = scsi_mode_select(cd->sc_link, SMS_PF, &data->hdr, flags, 20000); } } free(data, M_TEMP); return (error); }
int sd_ioctl_cache(struct sd_softc *sc, long cmd, struct dk_cache *dkc) { union scsi_mode_sense_buf *buf; struct page_caching_mode *mode = NULL; u_int wrcache, rdcache; int big; int rv; if (ISSET(sc->sc_link->flags, SDEV_UMASS)) return (EOPNOTSUPP); /* see if the adapter has special handling */ rv = scsi_do_ioctl(sc->sc_link, cmd, (caddr_t)dkc, 0); if (rv != ENOTTY) return (rv); buf = dma_alloc(sizeof(*buf), PR_WAITOK); if (buf == NULL) return (ENOMEM); rv = scsi_do_mode_sense(sc->sc_link, PAGE_CACHING_MODE, buf, (void **)&mode, NULL, NULL, NULL, sizeof(*mode) - 4, scsi_autoconf | SCSI_SILENT, &big); if (rv != 0) goto done; if ((mode == NULL) || (!DISK_PGCODE(mode, PAGE_CACHING_MODE))) { rv = EIO; goto done; } wrcache = (ISSET(mode->flags, PG_CACHE_FL_WCE) ? 1 : 0); rdcache = (ISSET(mode->flags, PG_CACHE_FL_RCD) ? 0 : 1); switch (cmd) { case DIOCGCACHE: dkc->wrcache = wrcache; dkc->rdcache = rdcache; break; case DIOCSCACHE: if (dkc->wrcache == wrcache && dkc->rdcache == rdcache) break; if (dkc->wrcache) SET(mode->flags, PG_CACHE_FL_WCE); else CLR(mode->flags, PG_CACHE_FL_WCE); if (dkc->rdcache) CLR(mode->flags, PG_CACHE_FL_RCD); else SET(mode->flags, PG_CACHE_FL_RCD); if (big) { rv = scsi_mode_select_big(sc->sc_link, SMS_PF, &buf->hdr_big, scsi_autoconf | SCSI_SILENT, 20000); } else { rv = scsi_mode_select(sc->sc_link, SMS_PF, &buf->hdr, scsi_autoconf | SCSI_SILENT, 20000); } break; } done: dma_free(buf, sizeof(*buf)); return (rv); }
/* * Enable IE reporting. We prefer the following settings: * * (1) DEXCPT = 0 * (3) MRIE = 6 (IE_REPORT_ON_REQUEST) * (4) EWASC = 1 * (6) REPORT COUNT = 0x00000001 * (7) LOGERR = 1 * * However, not all drives support changing these values, and the current state * may be useful enough as-is. For example, some drives support IE logging, but * don't support changing the MRIE. In this case, we can still use the * information provided by the log page. */ static int scsi_enable_ie(ds_scsi_info_t *sip, boolean_t *changed) { scsi_ie_page_t new_iec_page; scsi_ms_hdrs_t hdrs; uint_t skey, asc, ascq; if (!(sip->si_supp_mode & MODEPAGE_SUPP_IEC)) return (0); bzero(&new_iec_page, sizeof (new_iec_page)); bzero(&hdrs, sizeof (hdrs)); (void) memcpy(&new_iec_page, &sip->si_iec_current, sizeof (new_iec_page)); if (IEC_IE_CHANGEABLE(sip->si_iec_changeable)) new_iec_page.ie_dexcpt = 0; if (IEC_MRIE_CHANGEABLE(sip->si_iec_changeable)) new_iec_page.ie_mrie = IE_REPORT_ON_REQUEST; /* * We only want to enable warning reporting if we are able to change the * mrie to report on request. Otherwise, we risk unnecessarily * interrupting normal SCSI commands with a CHECK CONDITION code. */ if (IEC_EWASC_CHANGEABLE(sip->si_iec_changeable)) { if (new_iec_page.ie_mrie == IE_REPORT_ON_REQUEST) new_iec_page.ie_ewasc = 1; else new_iec_page.ie_ewasc = 0; } if (IEC_RPTCNT_CHANGEABLE(sip->si_iec_changeable)) new_iec_page.ie_report_count = BE_32(1); if (IEC_LOGERR_CHANGEABLE(sip->si_iec_changeable)) new_iec_page.ie_logerr = 1; /* * Now compare the new mode page with the existing one. * if there's no difference, there's no need for a mode select */ if (memcmp(&new_iec_page, &sip->si_iec_current, MODEPAGE_INFO_EXCPT_LEN) == 0) { *changed = B_FALSE; } else { (void) memcpy(&hdrs, &sip->si_hdrs, sizeof (sip->si_hdrs)); if (scsi_mode_select(sip, MODEPAGE_INFO_EXCPT, MODE_SELECT_PF, &new_iec_page, MODEPAGE_INFO_EXCPT_LEN, &hdrs, &skey, &asc, &ascq) == 0) { *changed = B_TRUE; } else { printf("failed to enable IE (KEY=0x%x " "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq); *changed = B_FALSE; } } if (nvlist_add_boolean_value(sip->si_state_iec, "changed", *changed) != 0) return (scsi_set_errno(sip, EDS_NOMEM)); return (0); }
void DevOperate(DiscDCB *dcb, DiscReq *req) { BYTE capacity_data[CAPACITY_LENGTH]; WORD command_status,second_status; WORD block_addr, block_len; WORD done = 0; WORD size; BYTE *buf = req->Buf; INT res; BYTE sense_data[SENSE_LENGTH]; FormatReq *freq; Wait(&dcb->Lock); /* Select the appropriate command */ switch( req->DevReq.Request & FG_Mask) { case FG_Read: #ifdef DEBUG IOdebug("read pos = %d size = %d",req->Pos / dcb->SectorSize,req->Size / dcb->SectorSize); #endif size = req->Size; while( done < size ) { WORD tfr = size - done; WORD position = (req->Pos / dcb->SectorSize) + (done/dcb->SectorSize); command_status = 8;/* make loop happen once*/ if ( tfr > MAX_TFR ) { tfr = MAX_TFR; } while (command_status == 8) { res = scsi_read(dcb->Link,dcb->Table,dcb->DeviceId,0,position,tfr,dcb->SectorSize,buf,&command_status); } if ( ( res < 0 ) || ( command_status ne 0 )) { command_status = 8; while (command_status == 8) { res = scsi_read(dcb->Link,dcb->Table,dcb->DeviceId,0,position,tfr,dcb->SectorSize,buf,&command_status); } } done += tfr; buf += tfr; } req->DevReq.Result = command_status; if ( req->DevReq.Result eq 0 ) req->Actual = req->Size; else req->Actual = 0; break; case FG_Write: #ifdef DEBUG IOdebug("write pos = %d size = %d",req->Pos / dcb->SectorSize,req->Size / dcb->SectorSize); #endif size = req->Size; while( done < size ) { WORD tfr = size - done; WORD position = (req->Pos / dcb->SectorSize) + (done/dcb->SectorSize); command_status = 8; /* make loop happen once*/ if ( tfr > MAX_TFR ) { tfr = MAX_TFR; } while (command_status == 8) { res = scsi_write(dcb->Link,dcb->Table,dcb->DeviceId,0,position,tfr,dcb->SectorSize,buf,&command_status); } if ( ( res < 0 ) || ( command_status ne 0 )) { command_status = 8; while (command_status == 8) { res = scsi_write(dcb->Link,dcb->Table,dcb->DeviceId,0,position,tfr,dcb->SectorSize,buf,&command_status); } } done += tfr; buf += tfr; } req->DevReq.Result = command_status; if ( req->DevReq.Result eq 0 ) req->Actual = req->Size; else req->Actual = 0; break; case FG_GetSize: #ifdef DEBUG IOdebug("read capacity request"); #endif command_status = 8; while (command_status == 8) { scsi_read_capacity( dcb->Link, dcb->Table, dcb->DeviceId, 0, /* lun */ 8, /* capacity length */ capacity_data, &command_status); } if ( command_status eq 0 ) { block_addr = capacity_data[0] * 0x1000000 + capacity_data[1] * 0x10000 + capacity_data[2] * 0x100 + capacity_data[3]; block_len = capacity_data[4] * 0x1000000 + capacity_data[5] * 0x10000 + capacity_data[6] * 0x100 + capacity_data[7]; req->DevReq.Result = block_addr * block_len; } break; case FG_Format: IOdebug("\rFormatting disk please wait ...."); freq = (FormatReq *)req; command_status = 8;/* so the loop happens once*/ while (command_status == 8) { scsi_mode_select( dcb->Link, dcb->Table, dcb->DeviceId, 0, 0, /* format whole disk */ dcb->SectorSize, &command_status); } command_status = 8;/* so the loop happens once*/ while (command_status == 8) { scsi_format( dcb->Link, dcb->Table, dcb->DeviceId, 0, freq->Interleave, &command_status); } /* If the disk supports the busy status we need to wait until busy goes away before giving the message that we are verifying */ command_status = 8; while (command_status == 8) { scsi_test_unit_ready( dcb->Link, dcb->Table, dcb->DeviceId, 0, &command_status); } /* some disks will queue one command so we need to wait twice to cope with this */ command_status = 8; while (command_status != 0) { IOdebug("status = %d",command_status); scsi_test_unit_ready( dcb->Link, dcb->Table, dcb->DeviceId, 0, &command_status); scsi_request_sense( dcb->Link, dcb->Table, dcb->DeviceId, 0, SENSE_LENGTH, sense_data, &command_status); IOdebug("sense =%x",sense_data[2]); } { WORD total_blocks; WORD i,j; WORD ten_cent,done = 0; BYTE *data; IOdebug("\rVerifying disk please wait ...."); data = Malloc(dcb->SectorSize); command_status = 8; /* do at least once*/ while (command_status == 8 ) { res = scsi_write(dcb->Link,dcb->Table,dcb->DeviceId,0,1,dcb->SectorSize,dcb->SectorSize,data,&command_status); } total_blocks = (dcb->SectorsPerTrack * dcb->TracksPerCyl * dcb->Cylinders); /* IOdebug("\rdisk size is %d blocks",total_blocks);*/ ten_cent = total_blocks / 10; for ( i = 1; i < total_blocks; i++) { command_status = 8; /* you know by now*/ while (command_status == 8) { res = scsi_write_quick(dcb->Link,dcb->Table,dcb->DeviceId,0,i,&command_status); } second_status = 8; while (second_status == 8) { res = scsi_read_quick(dcb->Link,dcb->Table,dcb->DeviceId,0,i,&second_status); } if (( i % ten_cent ) eq 0) { done += 10; IOdebug("\rVerified %d percent of disk\v",done); } if ((command_status ne 0) || (second_status ne 0)) { command_status = 8; while (command_status == 8 ) { scsi_write_quick(dcb->Link,dcb->Table,dcb->DeviceId,0,i,&command_status); } second_status = 8; while (second_status == 8) { scsi_read_quick(dcb->Link,dcb->Table,dcb->DeviceId,0,i,&second_status); } if (( command_status ne 0 ) || (second_status ne 0)) { IOdebug("Verifier found bad block at %d",i); for( j = 0; j < 10; j++) { command_status = 8; while (command_status == 8) { scsi_reassign_block(dcb->Link,dcb->Table,dcb->DeviceId,0,i,&command_status); } command_status = 8; while (command_status == 8) { scsi_write_quick(dcb->Link,dcb->Table,dcb->DeviceId,0,i,&command_status); } second_status = 8; while (second_status == 8) { scsi_read_quick(dcb->Link,dcb->Table,dcb->DeviceId,0,i,&second_status); } if ((command_status eq 0) && (second_status eq 0))break; } if (( command_status eq 0 ) && (second_status eq 0)) IOdebug("Block %d reassigned OK",i); else IOdebug("Failed to reassign block %d",i); } } } } IOdebug("Verification complete "); req->DevReq.Result = 0; break; default: break; } /* Unlock the driver */ Signal(&dcb->Lock); /* Client action */ (*req->DevReq.Action)(req); return; }