/* * scsi_freebsd.cpp - SCSI Manager, FreeBSD SCSI Driver implementation * Copyright (C) 1999 Orlando Bassotto * * Basilisk II (C) 1997-2005 Christian Bauer * * 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; either version 2 of the License, or * (at your option) any later version. * * 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * 29-Jun-1999 Started * 05-Jul-1999 Changed from array to queue removing the limit of 8 * devices. * Implemented old SCSI management for FreeBSD 2.x. * (Note: This implementation hasn't been tested; * I don't own anymore a machine with FreeBSD 2.x, * so if something goes wrong, please mail me to * future@mediabit.net). */ #include #include #include #include #include #include #include #include #include #include #ifdef CAM #include #include #include #include #include #include #include #include #else /* !CAM */ #include #include #endif /* !CAM */ #include "sysdeps.h" #include "main.h" #include "prefs.h" #include "user_strings.h" #include "scsi.h" #define DEBUG 0 #include "debug.h" #undef u_int8_t #define u_int8_t unsigned char typedef struct _SCSIDevice { int controller; // SCSI Controller int controller_bus; // SCSI Controller Bus char controller_name[33]; // SCSI Controller name int mac_unit; // Macintosh SCSI ID (remapped) int faked_controller; // "Faked" SCSI Controller (Always 0) int faked_unit; // "Faked" SCSI ID int unit; // Real SCSI ID int lun; // Real SCSI LUN u_int8_t vendor[16]; // SCSI Vendor u_int8_t product[48]; // SCSI Product u_int8_t revision[16]; // SCSI Revision char device[33]; // SCSI Device #ifdef CAM char pass_device[33]; // SCSI Pass Device #else /* !CAM */ int dev_fd; // Device File Descriptor #endif /* !CAM */ void* dev_ptr; // Pointer to CAM/SCSI structure bool enabled; // Device enabled ? struct _SCSIDevice* next; // Pointer to the next device } SCSIDevice; static int nDevices = 0; static SCSIDevice* Devices = NULL; static uint32 buffer_size; static uint8* buffer = NULL; static uint8 the_cmd[12]; static int the_cmd_len; static SCSIDevice* CurrentDevice = NULL; inline static SCSIDevice* _GetSCSIDeviceByID(int id) { SCSIDevice* aux = Devices; while(aux) { if(aux->faked_unit==id) { return aux; } aux = aux->next; } return NULL; } inline static SCSIDevice* _GetSCSIDeviceByIDLUN(int id, int lun) { SCSIDevice* aux = Devices; while(aux) { if(aux->faked_unit==id&&aux->lun==lun) { return aux; } aux = aux->next; } return NULL; } inline static SCSIDevice* _GetSCSIDeviceByMacID(int id) { SCSIDevice* aux = Devices; while(aux) { if(aux->mac_unit==id) { return aux; } aux = aux->next; } return NULL; } inline static SCSIDevice* _GetSCSIDeviceByMacIDLUN(int id, int lun) { SCSIDevice* aux = Devices; while(aux) { if(aux->mac_unit==id&&aux->lun==lun) { return aux; } aux = aux->next; } return NULL; } inline static SCSIDevice* _AllocNewDevice() { SCSIDevice* aux; aux = new SCSIDevice; if(aux==NULL) return NULL; memset(aux, 0, sizeof(SCSIDevice)); aux->next = Devices; Devices = aux; return aux; } #ifdef CAM inline static struct cam_device* _GetCurrentSCSIDevice() { if(CurrentDevice==NULL) return NULL; return (struct cam_device*)CurrentDevice->dev_ptr; } #else /* !CAM */ inline static struct scsireq* _GetCurrentSCSIDevice() { if(CurrentDevice==NULL) return NULL; return (struct scsireq*)CurrentDevice->dev_ptr; } #endif /* !CAM */ /* * _Build_SCSI_Controller() * * This function builds a virtual SCSI Controller (Controller=0) * where keeps all the devices found, this is due the fact * I have two SCSI controllers in my PC. :-) * Use scsidump in contrib/ to see how is remapped your * SCSI device (only if you have more than one controller, * that's for sure :-). * If you have only one controller, remapping does not take act. */ #define GET_FREE_ID(id) \ { \ for(int x=0;x<32;x++) { \ if(!(busyIDs&(1<<(x+1)))) { \ id = x; \ break; \ } \ } \ } static void _Build_SCSI_Controller() { unsigned int id = 0; unsigned long long busyIDs = 0x0ll; SCSIDevice* aux, * dev; // What IDs are busy? dev = Devices; while(dev) { dev->enabled = false; dev->faked_controller = 0; dev->faked_unit = dev->unit; busyIDs |= (1 << (dev->unit+1)); dev = dev->next; } // Find out the duplicate IDs and remap them dev = Devices, aux = NULL; while(dev) { aux = dev; while(aux) { SCSIDevice* dev1, * dev2; dev1 = dev, dev2 = aux; if(dev1->controller!=dev2->controller&& dev1->unit==dev2->unit) { int free_id; GET_FREE_ID(free_id); busyIDs |= (1<<(free_id+1)); dev1->faked_unit = free_id; } aux = aux->next; } dev = dev->next; } // Now reorder the queue #if 0 dev = Devices; while(dev) { aux = dev; while(aux) { SCSIDevice* dev1, * dev2; dev1 = dev, dev2 = aux; if(dev1->faked_unit>dev2->faked_unit) { SCSIDevice tmp; memcpy(&tmp, dev1, sizeof(SCSIDevice)); memcpy(dev1, dev2, sizeof(SCSIDevice)); memcpy(dev2, &tmp, sizeof(SCSIDevice)); } aux = aux->next; } dev = dev->next; } #endif // Now open the selected SCSI devices :-) for(int n=0;n<8;n++) { char tmp[25]; snprintf(tmp, sizeof(tmp), "scsi%d", n); const char* scsi = PrefsFindString(tmp); if(scsi) { int id, lun; // The format is: RemappedID (or FakedID)/LUN sscanf(scsi, "%d/%d", &id, &lun); SCSIDevice* dev = _GetSCSIDeviceByIDLUN(id, lun); if(dev==NULL) continue; dev->enabled = true; dev->mac_unit = n; #ifdef CAM struct cam_device* cam; cam = cam_open_btl(dev->controller, dev->unit, dev->lun, O_RDWR, NULL); if(cam==NULL) { fprintf(stderr, "Failed to open %d:%d:%d = %s!!!\n", dev->controller, dev->unit, dev->lun, cam_errbuf); } dev->dev_ptr = (void*)cam; #else /* !CAM */ dev->dev_fd = scsi_open(dev->device, O_RDWR); if(dev->dev_fd<0) { perror("Failed to open %d:%d:%d"); } else { dev->dev_ptr = (void*)scsireq_new(); } #endif /* !CAM */ } } } /* * Initialization */ void SCSIInit(void) { // Finds the SCSI hosts in the system filling the SCSIDevices queue. // "Stolen" from camcontrol.c // Copyright (C) 1997-99 Kenneth D. Merry // Old SCSI detection "stolen" from scsi.c // Copyright (C) 1993 Julian Elischer // int bufsize, fd; int need_close = 0; int error = 0; int skip_device = 0; SCSIDevice* Dev, * dev, * PrevDev = NULL; nDevices = 0; if(PrefsFindBool("noscsi")) goto no_scsi; #ifdef CAM union ccb ccb; if ((fd = open(XPT_DEVICE, O_RDWR)) == -1) { fprintf(stderr, "WARNING: Cannot open CAM device %s (%s)\n", XPT_DEVICE, strerror(errno)); goto no_scsi; } memset(&(&ccb.ccb_h)[1], 0, sizeof(struct ccb_dev_match)-sizeof(struct ccb_hdr)); ccb.ccb_h.func_code = XPT_DEV_MATCH; bufsize = sizeof(struct dev_match_result) * 100; ccb.cdm.match_buf_len = bufsize; ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize); ccb.cdm.num_matches = 0; ccb.cdm.num_patterns = 0; ccb.cdm.pattern_buf_len = 0; do { Dev = _AllocNewDevice(); if(ioctl(fd, CAMIOCOMMAND, &ccb)==-1) { fprintf(stderr, "Error sending CAMIOCOMMAND ioctl\n"); return; } if((ccb.ccb_h.status != CAM_REQ_CMP) || ((ccb.cdm.status != CAM_DEV_MATCH_LAST) && (ccb.cdm.status != CAM_DEV_MATCH_MORE))) { fprintf(stderr, "Got CAM error %#x, CDM error %d\n", ccb.ccb_h.status, ccb.cdm.status); return; } char current_controller_name[33]; int current_controller = -1; for(int i=0;ipath_id==-1) break; Dev->controller = bus_result->path_id; snprintf(Dev->controller_name, sizeof(Dev->controller_name), "%s%d", bus_result->dev_name, bus_result->unit_number); strncpy(current_controller_name, Dev->controller_name, sizeof(current_controller_name)); current_controller = Dev->controller; Dev->controller_bus = bus_result->bus_id; break; } case DEV_MATCH_DEVICE: { struct device_match_result* dev_result; char tmpstr[256]; dev_result = &ccb.cdm.matches[i].result.device_result; if(current_controller==-1||dev_result->target_id==-1) { skip_device = 1; break; } else skip_device = 0; cam_strvis(Dev->vendor, (u_int8_t*)dev_result->inq_data.vendor, sizeof(dev_result->inq_data.vendor), sizeof(Dev->vendor)); cam_strvis(Dev->product, (u_int8_t*)dev_result->inq_data.product, sizeof(dev_result->inq_data.product), sizeof(Dev->product)); cam_strvis(Dev->revision, (u_int8_t*)dev_result->inq_data.revision, sizeof(dev_result->inq_data.revision), sizeof(Dev->revision)); strncpy(Dev->controller_name, current_controller_name, sizeof(Dev->controller_name)); Dev->controller = current_controller; Dev->unit = dev_result->target_id; Dev->lun = dev_result->target_lun; break; } case DEV_MATCH_PERIPH: { struct periph_match_result* periph_result; periph_result = &ccb.cdm.matches[i].result.periph_result; if(skip_device != 0) break; if(need_close==1) { snprintf(Dev->device, sizeof(Dev->device), "%s%d*", periph_result->periph_name, periph_result->unit_number); need_close = 0; } else if(need_close==0) { snprintf(Dev->pass_device, sizeof(Dev->pass_device), "%s%d", periph_result->periph_name, periph_result->unit_number); need_close++; break; } else { need_close = 0; } PrevDev = Dev; Dev = _AllocNewDevice(); break; } } } } while (ccb.ccb_h.status == CAM_REQ_CMP && ccb.cdm.status == CAM_DEV_MATCH_MORE); /* Remove last one (ugly coding) */ Devices = PrevDev; delete Dev; end_loop: close(fd); #else /* !CAM */ /* * FreeBSD 2.x SCSI management is quiet different and * unfortunatly not flexible as CAM library in FreeBSD 3.x... * I probe only the first bus, LUN 0, and the * first 8 devices only. */ u_char* inq_buf; scsireq_t* scsireq; struct scsi_addr scsi; int ssc_fd; if((ssc_fd=open("/dev/ssc", O_RDWR))==-1) { fprintf(stderr, "Cannot open SCSI manager: /dev/ssc\n"); SCSIReset(); return; } inq_buf = (u_char*)malloc(96); if(inq_buf==NULL) { perror("malloc failed"); SCSIReset(); return; } scsireq = scsireq_build((scsireq_t*)dev->dev_ptr, 96, inq_buf, SCCMD_READ, "12 0 0 0 v 0", 96); addr.scbus = 0; addr.lun = 0; for(int n=0;n<8;n++) { addr.target = n; if(ioctl(ssc_fd, SCIOCADDR, &addr) != -1) { Dev = _AllocNewDevice(); Dev->controller = addr.scbus; Dev->lun = addr.lun; Dev->unit = addr.target; struct scsi_devinfo devInfo; devInfo.addr = addr; if(ioctl(ssc_fd, SCIOCGETDEVINFO, &devInfo) != -1) { strncpy(Dev->device, devInfo.devname, sizeof(Dev->device)); } strncpy(Dev->controller_name, "FreeBSD 2.x SCSI Manager", sizeof(Dev->controller_name)); if(scsireq_enter(ssc_fd, scsireq)!=-1) { Dev->vendor[sizeof(Dev->vendor)-1] = 0; Dev->product[sizeof(Dev->product)-1] = 0; Dev->revision[sizeof(Dev->revision)-1] = 0; scsireq_decode(scsireq, "s8 c8 c16 c4", Dev->vendor, Dev->product, Dev->revision); } } } free(inq_buf); close(ssc_fd); #endif /* !CAM */ _Build_SCSI_Controller(); // Print out the periph with ID:LUNs fprintf(stderr, "Device RealID FkdID MacID Enabled\n"); fprintf(stderr, "-------------------------------------------------------------\n"); // 012345678901234567890123456789012 0:0:0 0/0 0:0 Yes dev = Devices; while(dev) { char tmp[40]; snprintf(tmp, sizeof(tmp), "%s %s %s", dev->vendor, dev->product, dev->revision); fprintf(stderr, "%-33s %d:%d:%d %d/%d %d:%d %s\n", tmp, dev->controller, dev->unit, dev->lun, dev->faked_unit, dev->lun, dev->mac_unit, dev->lun, dev->enabled?"Yes":"No"); dev = dev->next; } no_scsi: // Reset SCSI bus SCSIReset(); } /* * Deinitialization */ void SCSIExit(void) { SCSIDevice* aux; while(Devices) { aux = Devices->next; if(Devices->dev_ptr!=NULL) { #ifdef CAM cam_close_device((struct cam_device*)Devices->dev_ptr); #else /* !CAM */ free(Devices->dev_ptr); // Is this right? close(Devices->dev_fd); // And this one? #endif /* !CAM */ } delete Devices; Devices = aux; } nDevices = 0; } /* * Set SCSI command to be sent by scsi_send_cmd() */ void scsi_set_cmd(int cmd_length, uint8 *cmd) { the_cmd_len = cmd_length; memset(the_cmd, 0, sizeof(the_cmd)); memcpy(the_cmd, cmd, the_cmd_len); } /* * Check for presence of SCSI target */ bool scsi_is_target_present(int id) { return (_GetSCSIDeviceByMacID(id)!=NULL&&_GetSCSIDeviceByMacID(id)->enabled); } /* * Set SCSI target (returns false on error) */ bool scsi_set_target(int id, int lun) { SCSIDevice* dev; dev = _GetSCSIDeviceByMacIDLUN(id, lun); if(dev==NULL) return false; CurrentDevice = dev; return true; } /* * Send SCSI command to active target (scsi_set_command() must have been called), * read/write data according to S/G table (returns false on error) */ static bool try_buffer(int size) { if(size <= buffer_size) { return true; } D(bug("Allocating buffer of %d bytes.\n", size)); uint8* new_buffer = (uint8*)valloc(size); if(new_buffer==NULL) { return false; } if(buffer!=NULL) free(buffer); buffer = new_buffer; buffer_size = size; return true; } bool scsi_send_cmd(size_t data_length, bool reading, int sg_size, uint8 **sg_ptr, uint32 *sg_len, uint16 *stat, uint32 timeout) { int value = 0; #ifdef CAM #ifdef VERBOSE_CAM_DEBUG D(bug("Sending command %x (len=%d) to SCSI Device %d:%d:%d\n", the_cmd[0], the_cmd_len, CurrentDevice->controller, CurrentDevice->unit, CurrentDevice->lun)); D(bug("DataLength: %d\n", data_length)); D(bug("Reading: %d\n", reading)); D(bug("SG Size: %d\n", sg_size)); D(bug("Timeout: %d\n", timeout)); #endif /* VERBOSE_CAM_DEBUG */ #endif /* CAM */ if(!try_buffer(data_length)) { char str[256]; sprintf(str, GetString(STR_SCSI_BUFFER_ERR), data_length); ErrorAlert(str); return false; } if(!reading) { uint8* buffer_ptr = buffer; for(int i=0;i0) { dir_flags = reading?CAM_DIR_IN:CAM_DIR_OUT; } ccb.ccb_h.path_id = CurrentDevice->controller; ccb.ccb_h.target_id = CurrentDevice->unit; ccb.ccb_h.target_lun = CurrentDevice->lun; cam_fill_csio(&ccb.csio, 0, NULL, dir_flags, MSG_SIMPLE_Q_TAG, (u_int8_t*)buffer, data_length, SSD_FULL_SIZE, the_cmd_len, (timeout?timeout:50)*1000); ccb.ccb_h.flags |= CAM_DEV_QFRZDIS; memcpy(ccb.csio.cdb_io.cdb_bytes, the_cmd, the_cmd_len); if(cam_send_ccb(device, &ccb)<0) { fprintf(stderr, "%d:%d:%d ", CurrentDevice->controller, CurrentDevice->unit, CurrentDevice->lun); perror("cam_send_ccb"); return false; } value = ccb.ccb_h.status; *stat = ccb.csio.scsi_status; if((value & CAM_STATUS_MASK) != CAM_REQ_CMP) { char tmp[4096]; if((value & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) { scsi_sense_string(device, &ccb.csio, tmp, sizeof(tmp)); fprintf(stderr, "SCSI Status Error:\n%s\n", tmp); return false; } } #else /* !CAM */ struct scsireq* scsireq = _GetCurrentSCSIDevice(); if(device==NULL) return false; int dir_flags = 0x00; if(data_length>0) dir_flags = reading?SCCMD_READ:SCCMD_WRITE; scsireq_reset(scsireq); scsireq->timeout = (timeout?timeout:50)*1000; scsireq_build(scsireq, data_length, (caddr_t)buffer, dir_flags, "0"); memcpy(scsireq->cmd, the_cmd, scsireq->cmdlen = the_cmd_len); int result = scsi_enter(dev->dev_fd, scsireq); if(SCSIREQ_ERROR(result)) { scsi_debug(stderr, result, scsireq); } *stat = scsireq->status; #endif /* !CAM */ if(reading) { uint8* buffer_ptr = buffer; for(int i=0;i