/* * This file is part of the Aaru Remote Server. * Copyright (c) 2019-2021 Natalia Portillo. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "../aaruremote.h" #include "freebsd.h" static u_int32_t AtaProtocolToCamFlags(uint8_t protocol) { switch(protocol) { case AARUREMOTE_ATA_PROTOCOL_DEVICE_DIAGNOSTIC: case AARUREMOTE_ATA_PROTOCOL_DEVICE_RESET: case AARUREMOTE_ATA_PROTOCOL_HARD_RESET: case AARUREMOTE_ATA_PROTOCOL_NO_DATA: case AARUREMOTE_ATA_PROTOCOL_SOFT_RESET: case AARUREMOTE_ATA_PROTOCOL_RETURN_RESPONSE: return CAM_DIR_NONE; case AARUREMOTE_ATA_PROTOCOL_PIO_IN: case AARUREMOTE_ATA_PROTOCOL_UDMA_IN: return CAM_DIR_IN; case AARUREMOTE_ATA_PROTOCOL_PIO_OUT: case AARUREMOTE_ATA_PROTOCOL_UDMA_OUT: return CAM_DIR_OUT; default: return 0; } } int32_t SendAtaChsCommand(void *device_ctx, AtaRegistersChs registers, AtaErrorRegistersChs *error_registers, uint8_t protocol, uint8_t transfer_register, char *buffer, uint32_t timeout, uint8_t transfer_blocks, uint32_t *duration, uint32_t *sense, uint32_t *buf_len) { DeviceContext *ctx = device_ctx; *duration = 0; *sense = false; union ccb *camccb; int error; int clock_error; struct timespec start_tp; struct timespec end_tp; double start, end; if(!ctx) return -1; if(!ctx->device) return -1; camccb = cam_getccb(ctx->device); if(!camccb) return -1; camccb->ccb_h.func_code = XPT_ATA_IO; camccb->ccb_h.flags = AtaProtocolToCamFlags(protocol); camccb->ccb_h.xflags = 0; camccb->ccb_h.retry_count = 1; camccb->ccb_h.cbfcnp = NULL; camccb->ccb_h.timeout = timeout; camccb->ataio.data_ptr = (u_int8_t *)buffer; camccb->ataio.dxfer_len = *buf_len; camccb->ccb_h.flags |= CAM_DEV_QFRZDIS; camccb->ataio.cmd.flags = CAM_ATAIO_NEEDRESULT; switch(protocol) { case AARUREMOTE_ATA_PROTOCOL_DMA: case AARUREMOTE_ATA_PROTOCOL_DMA_QUEUED: case AARUREMOTE_ATA_PROTOCOL_UDMA_IN: case AARUREMOTE_ATA_PROTOCOL_UDMA_OUT: camccb->ataio.cmd.flags |= CAM_ATAIO_DMA; break; case AARUREMOTE_ATA_PROTOCOL_FPDMA: camccb->ataio.cmd.flags |= CAM_ATAIO_FPDMA; break; } camccb->ataio.cmd.command = registers.command; camccb->ataio.cmd.lba_high = registers.cylinder_high; camccb->ataio.cmd.lba_mid = registers.cylinder_low; camccb->ataio.cmd.device = 0x40 | registers.device_head; camccb->ataio.cmd.features = registers.feature; camccb->ataio.cmd.sector_count = registers.sector_count; camccb->ataio.cmd.lba_low = registers.sector; clock_error = clock_gettime(CLOCK_REALTIME_PRECISE, &start_tp); error = cam_send_ccb(ctx->device, camccb); if(!clock_error) clock_error = clock_gettime(CLOCK_REALTIME_PRECISE, &end_tp); if(!clock_error) { start = (double)start_tp.tv_sec * 1000.0; start += (double)start_tp.tv_nsec / 1000000.0; end = (double)end_tp.tv_sec * 1000.0; end += (double)end_tp.tv_nsec / 1000000.0; *duration = (uint32_t)(end - start); } if(error < 0) error = errno; if((camccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP && (camccb->ccb_h.status & CAM_STATUS_MASK) != CAM_ATA_STATUS_ERROR) { error = errno; *sense = true; } if((camccb->ccb_h.status & CAM_STATUS_MASK) == CAM_ATA_STATUS_ERROR) *sense = true; error_registers->cylinder_high = camccb->ataio.res.lba_high; error_registers->cylinder_low = camccb->ataio.res.lba_mid; error_registers->device_head = camccb->ataio.res.device; error_registers->error = camccb->ataio.res.error; error_registers->sector = camccb->ataio.res.lba_low; error_registers->sector_count = camccb->ataio.res.sector_count; error_registers->status = camccb->ataio.res.status; cam_freeccb(camccb); *buf_len = camccb->ataio.dxfer_len; *sense |= error_registers->error || (error_registers->status & 0xA5) || error; return error; } int32_t SendAtaLba28Command(void *device_ctx, AtaRegistersLba28 registers, AtaErrorRegistersLba28 *error_registers, uint8_t protocol, uint8_t transfer_register, char *buffer, uint32_t timeout, uint8_t transfer_blocks, uint32_t *duration, uint32_t *sense, uint32_t *buf_len) { DeviceContext *ctx = device_ctx; *duration = 0; *sense = false; union ccb *camccb; int error; int clock_error; struct timespec start_tp; struct timespec end_tp; double start, end; if(!ctx) return -1; if(!ctx->device) return -1; camccb = cam_getccb(ctx->device); if(!camccb) return -1; camccb->ccb_h.func_code = XPT_ATA_IO; camccb->ccb_h.flags = AtaProtocolToCamFlags(protocol); camccb->ccb_h.xflags = 0; camccb->ccb_h.retry_count = 1; camccb->ccb_h.cbfcnp = NULL; camccb->ccb_h.timeout = timeout; camccb->ataio.data_ptr = (u_int8_t *)buffer; camccb->ataio.dxfer_len = *buf_len; camccb->ccb_h.flags |= CAM_DEV_QFRZDIS; camccb->ataio.cmd.flags = CAM_ATAIO_NEEDRESULT; switch(protocol) { case AARUREMOTE_ATA_PROTOCOL_DMA: case AARUREMOTE_ATA_PROTOCOL_DMA_QUEUED: case AARUREMOTE_ATA_PROTOCOL_UDMA_IN: case AARUREMOTE_ATA_PROTOCOL_UDMA_OUT: camccb->ataio.cmd.flags |= CAM_ATAIO_DMA; break; case AARUREMOTE_ATA_PROTOCOL_FPDMA: camccb->ataio.cmd.flags |= CAM_ATAIO_FPDMA; break; } camccb->ataio.cmd.command = registers.command; camccb->ataio.cmd.lba_high = registers.lba_high; camccb->ataio.cmd.lba_mid = registers.lba_mid; camccb->ataio.cmd.device = 0x40 | registers.device_head; camccb->ataio.cmd.features = registers.feature; camccb->ataio.cmd.sector_count = registers.sector_count; camccb->ataio.cmd.lba_low = registers.lba_low; clock_error = clock_gettime(CLOCK_REALTIME_PRECISE, &start_tp); error = cam_send_ccb(ctx->device, camccb); if(!clock_error) clock_error = clock_gettime(CLOCK_REALTIME_PRECISE, &end_tp); if(!clock_error) { start = (double)start_tp.tv_sec * 1000.0; start += (double)start_tp.tv_nsec / 1000000.0; end = (double)end_tp.tv_sec * 1000.0; end += (double)end_tp.tv_nsec / 1000000.0; *duration = (uint32_t)(end - start); } if(error < 0) error = errno; if((camccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP && (camccb->ccb_h.status & CAM_STATUS_MASK) != CAM_ATA_STATUS_ERROR) { error = errno; *sense = true; } if((camccb->ccb_h.status & CAM_STATUS_MASK) == CAM_ATA_STATUS_ERROR) *sense = true; error_registers->lba_high = camccb->ataio.res.lba_high; error_registers->lba_mid = camccb->ataio.res.lba_mid; error_registers->device_head = camccb->ataio.res.device; error_registers->error = camccb->ataio.res.error; error_registers->lba_low = camccb->ataio.res.lba_low; error_registers->sector_count = camccb->ataio.res.sector_count; error_registers->status = camccb->ataio.res.status; cam_freeccb(camccb); *buf_len = camccb->ataio.dxfer_len; *sense |= error_registers->error || (error_registers->status & 0xA5) || error; return error; } int32_t SendAtaLba48Command(void *device_ctx, AtaRegistersLba48 registers, AtaErrorRegistersLba48 *error_registers, uint8_t protocol, uint8_t transfer_register, char *buffer, uint32_t timeout, uint8_t transfer_blocks, uint32_t *duration, uint32_t *sense, uint32_t *buf_len) { DeviceContext *ctx = device_ctx; *duration = 0; *sense = false; union ccb *camccb; int error; int clock_error; struct timespec start_tp; struct timespec end_tp; double start, end; if(!ctx) return -1; if(!ctx->device) return -1; camccb = cam_getccb(ctx->device); if(!camccb) return -1; // TODO CHECK: 48-bit ATA CAM commands can crash FreeBSD < 9.2-RELEASE camccb->ccb_h.func_code = XPT_ATA_IO; camccb->ccb_h.flags = AtaProtocolToCamFlags(protocol); camccb->ccb_h.xflags = 0; camccb->ccb_h.retry_count = 1; camccb->ccb_h.cbfcnp = NULL; camccb->ccb_h.timeout = timeout; camccb->ataio.data_ptr = (u_int8_t *)buffer; camccb->ataio.dxfer_len = *buf_len; camccb->ccb_h.flags |= CAM_DEV_QFRZDIS; camccb->ataio.cmd.flags = CAM_ATAIO_NEEDRESULT | CAM_ATAIO_48BIT; switch(protocol) { case AARUREMOTE_ATA_PROTOCOL_DMA: case AARUREMOTE_ATA_PROTOCOL_DMA_QUEUED: case AARUREMOTE_ATA_PROTOCOL_UDMA_IN: case AARUREMOTE_ATA_PROTOCOL_UDMA_OUT: camccb->ataio.cmd.flags |= CAM_ATAIO_DMA; break; case AARUREMOTE_ATA_PROTOCOL_FPDMA: camccb->ataio.cmd.flags |= CAM_ATAIO_FPDMA; break; } camccb->ataio.cmd.command = registers.command; camccb->ataio.cmd.device = 0x40 | registers.device_head; camccb->ataio.cmd.lba_high_exp = registers.lba_high_cur; camccb->ataio.cmd.lba_high = registers.lba_high_prev; camccb->ataio.cmd.lba_mid_exp = registers.lba_mid_cur; camccb->ataio.cmd.lba_mid = registers.lba_mid_prev; camccb->ataio.cmd.lba_low_exp = registers.lba_low_cur; camccb->ataio.cmd.lba_low = registers.lba_low_prev; camccb->ataio.cmd.features_exp = (registers.feature & 0xFF00) >> 8; camccb->ataio.cmd.features = registers.feature & 0xFF; camccb->ataio.cmd.sector_count_exp = (registers.sector_count & 0xFF00) >> 8; camccb->ataio.cmd.sector_count = registers.sector_count & 0xFF; clock_error = clock_gettime(CLOCK_REALTIME_PRECISE, &start_tp); error = cam_send_ccb(ctx->device, camccb); if(!clock_error) clock_error = clock_gettime(CLOCK_REALTIME_PRECISE, &end_tp); if(!clock_error) { start = (double)start_tp.tv_sec * 1000.0; start += (double)start_tp.tv_nsec / 1000000.0; end = (double)end_tp.tv_sec * 1000.0; end += (double)end_tp.tv_nsec / 1000000.0; *duration = (uint32_t)(end - start); } if(error < 0) error = errno; if((camccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP && (camccb->ccb_h.status & CAM_STATUS_MASK) != CAM_ATA_STATUS_ERROR) { error = errno; *sense = true; } if((camccb->ccb_h.status & CAM_STATUS_MASK) == CAM_ATA_STATUS_ERROR) *sense = true; error_registers->lba_high = (camccb->ataio.res.lba_high_exp << 8) + camccb->ataio.res.lba_high; error_registers->lba_mid = (camccb->ataio.res.lba_mid_exp << 8) + camccb->ataio.res.lba_mid; error_registers->lba_low = (camccb->ataio.res.lba_low_exp << 8) + camccb->ataio.res.lba_low; error_registers->sector_count = (camccb->ataio.res.sector_count_exp << 8) + camccb->ataio.res.sector_count; error_registers->device_head = camccb->ataio.res.device; error_registers->error = camccb->ataio.res.error; error_registers->status = camccb->ataio.res.status; cam_freeccb(camccb); *buf_len = camccb->ataio.dxfer_len; *sense |= error_registers->error || (error_registers->status & 0xA5) || error; return error; }