X-Git-Url: https://git.proxmox.com/?p=mirror_smartmontools-debian.git;a=blobdiff_plain;f=os_freebsd.cpp;h=9c5db2bbf7eb8f8cd2ba6eee52635bd8a98a3a41;hp=445238ee78830219677eb70925a03d0362008bf2;hb=fb337c9cb7f2443edb74813df8f6d69501c0eef0;hpb=2127e1931eec4a688d41baf253744fc48ed8c989 diff --git a/os_freebsd.cpp b/os_freebsd.cpp index 445238e..9c5db2b 100644 --- a/os_freebsd.cpp +++ b/os_freebsd.cpp @@ -3,7 +3,7 @@ * * Home page of code is: http://smartmontools.sourceforge.net * - * Copyright (C) 2003-8 Eduard Martinescu + * Copyright (C) 2003-10 Eduard Martinescu * * 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 @@ -11,8 +11,8 @@ * any later version. * * You should have received a copy of the GNU General Public License - * (for example COPYING); if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * (for example COPYING); If not, see . + * */ #include @@ -20,10 +20,10 @@ #include #include #include +#include #include #include #include -#include #if defined(__DragonFly__) #include #else @@ -42,57 +42,41 @@ #include "scsicmds.h" #include "cciss.h" #include "utility.h" -#include "extern.h" #include "os_freebsd.h" #include "dev_interface.h" #include "dev_ata_cmd_set.h" +#include "dev_areca.h" #define USBDEV "/dev/usb" +#if defined(__FreeBSD_version) -#define CONTROLLER_UNKNOWN 0x00 -#define CONTROLLER_ATA 0x01 -#define CONTROLLER_SCSI 0x02 -#define CONTROLLER_3WARE 0x03 // set by -d option, but converted to one of three types below -#define CONTROLLER_3WARE_678K 0x04 // NOT set by guess_device_type() -#define CONTROLLER_3WARE_9000_CHAR 0x05 // set by guess_device_type() -#define CONTROLLER_3WARE_678K_CHAR 0x06 // set by guess_device_type() -#define CONTROLLER_MARVELL_SATA 0x07 // SATA drives behind Marvell controllers -#define CONTROLLER_SAT 0x08 // SATA device behind a SCSI ATA Translation (SAT) layer -#define CONTROLLER_HPT 0x09 // SATA drives behind HighPoint Raid controllers -#define CONTROLLER_CCISS 0x10 // CCISS controller -#define CONTROLLER_PARSEDEV 0x11 // "smartctl -r ataioctl,2 ..." output parser pseudo-device -#define CONTROLLER_USBCYPRESS 0x12 // ATA device behind Cypress USB bridge -#define CONTROLLER_ARECA 0x13 // Areca controller - -static __unused const char *filenameandversion="$Id: os_freebsd.cpp 2879 2009-08-29 17:19:00Z chrfranke $"; - -const char *os_XXXX_c_cvsid="$Id: os_freebsd.cpp 2879 2009-08-29 17:19:00Z chrfranke $" \ -ATACMDS_H_CVSID CCISS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_FREEBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; - -extern smartmonctrl * con; +// This way we define one variable for the GNU/kFreeBSD and FreeBSD +#define FREEBSDVER __FreeBSD_version +#else +#define FREEBSDVER __FreeBSD_kernel_version +#endif -// Private table of open devices: guaranteed zero on startup since -// part of static data. -struct freebsd_dev_channel *devicetable[FREEBSD_MAXDEV]; +#if (FREEBSDVER >= 800000) +#include +#include +#elif defined(__DragonFly__) +#include +#include +#else +#include +#include +#endif -// forward declaration -// static int parse_ata_chan_dev(const char * dev_name, struct freebsd_dev_channel *ch); +#define CONTROLLER_3WARE_9000_CHAR 0x01 +#define CONTROLLER_3WARE_678K_CHAR 0x02 +#ifndef PATHINQ_SETTINGS_SIZE +#define PATHINQ_SETTINGS_SIZE 128 +#endif -// Returns 1 if device not available/open/found else 0. Also shifts fd into valid range. -static int isnotopen(int *fd, struct freebsd_dev_channel** fdchan) { - // put valid "file descriptor" into range 0...FREEBSD_MAXDEV-1 - *fd -= FREEBSD_FDOFFSET; - - // check for validity of "file descriptor". - if (*fd<0 || *fd>=FREEBSD_MAXDEV || !((*fdchan)=devicetable[*fd])) { - errno = ENODEV; - return 1; - } - - return 0; -} +const char *os_XXXX_c_cvsid="$Id: os_freebsd.cpp 3902 2014-05-23 19:14:15Z samm2 $" \ +ATACMDS_H_CVSID CCISS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_FREEBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; #define NO_RETURN 0 #define BAD_SMART 1 @@ -105,11 +89,11 @@ void printwarning(int msgNo, const char* extra) { static int printed[] = {0,0,0,0}; static const char* message[]={ "The SMART RETURN STATUS return value (smartmontools -H option/Directive)\n can not be retrieved with this version of ATAng, please do not rely on this value\nYou should update to at least 5.2\n", - + "Error SMART Status command failed\nPlease get assistance from \n" PACKAGE_HOMEPAGE "\nRegister values returned from SMART Status command are:\n", - + "You must specify a DISK # for 3ware drives with -d 3ware, where begins with 1 for first disk drive\n", - + "ATA support is not provided for this kernel version. Please ugrade to a recent 5-CURRENT kernel (post 09/01/2003 or so)\n" }; @@ -124,56 +108,21 @@ void printwarning(int msgNo, const char* extra) { return; } - // Interface to ATA devices behind 3ware escalade RAID controller cards. See os_linux.c #define BUFFER_LEN_678K_CHAR ( sizeof(struct twe_usercommand) ) // 520 #define BUFFER_LEN_9000_CHAR ( sizeof(TW_OSLI_IOCTL_NO_DATA_BUF) + sizeof(TWE_Command) ) // 2048 #define TW_IOCTL_BUFFER_SIZE ( MAX(BUFFER_LEN_678K_CHAR, BUFFER_LEN_9000_CHAR) ) - - - - - #ifndef ATA_DEVICE #define ATA_DEVICE "/dev/ata" #endif - +#define ARGUSED(x) ((void)(x)) // global variable holding byte count of allocated memory long long bytes; - - -/* - * dev_legacy.cpp - * - * Home page of code is: http://smartmontools.sourceforge.net - * - * Copyright (C) 2008 Christian Franke - * - * 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, or (at your option) - * any later version. - * - * You should have received a copy of the GNU General Public License - * (for example COPYING); If not, see . - * - */ - - -const char * dev_freebsd_cpp_cvsid = "$Id: os_freebsd.cpp 2879 2009-08-29 17:19:00Z chrfranke $" - DEV_INTERFACE_H_CVSID; - -extern smartmonctrl * con; // con->reportscsiioctl - -///////////////////////////////////////////////////////////////////////////// - -#ifdef HAVE_ATA_IDENTIFY_IS_CACHED -int ata_identify_is_cached(int fd); -#endif +extern unsigned char failuretest_permissive; ///////////////////////////////////////////////////////////////////////////// @@ -186,9 +135,9 @@ class freebsd_smart_device : virtual public /*implements*/ smart_device { public: - explicit freebsd_smart_device(const char * mode) + explicit freebsd_smart_device() : smart_device(never_called), - m_fd(-1), m_mode(mode) { } + m_fd(-1) { } virtual ~freebsd_smart_device() throw(); @@ -203,11 +152,21 @@ protected: int get_fd() const { return m_fd; } + void set_fd(int fd) + { m_fd = fd; } + private: int m_fd; ///< filedesc, -1 if not open. - const char * m_mode; ///< Mode string for deviceopen(). }; +#ifdef __GLIBC__ +static inline void * reallocf(void *ptr, size_t size) { + void *rv = realloc(ptr, size); + if((rv == NULL) && (size != 0)) + free(ptr); + return rv; + } +#endif freebsd_smart_device::~freebsd_smart_device() throw() { @@ -221,7 +180,7 @@ unsigned char m_controller_port; // examples for smartctl static const char smartctl_examples[] = - "=================================================== SMARTCTL EXAMPLES =====\n\n" + "=================================================== SMARTCTL EXAMPLES =====\n\n" " smartctl -a /dev/ad0 (Prints all SMART information)\n\n" " smartctl --smart=on --offlineauto=on --saveauto=on /dev/ad0\n" " (Enables SMART on first disk)\n\n" @@ -231,8 +190,16 @@ static const char smartctl_examples[] = " (Prints Self-Test & Attribute errors)\n\n" " smartctl -a --device=3ware,2 /dev/twa0\n" " smartctl -a --device=3ware,2 /dev/twe0\n" + " smartctl -a --device=3ware,2 /dev/tws0\n" " (Prints all SMART information for ATA disk on\n" " third port of first 3ware RAID controller)\n" + " smartctl -a --device=cciss,0 /dev/ciss0\n" + " (Prints all SMART information for first disk \n" + " on Common Interface for SCSI-3 Support driver)\n" + " smartctl -a --device=areca,3/1 /dev/arcmsr0\n" + " (Prints all SMART information for 3rd disk in the 1st enclosure \n" + " on first ARECA RAID controller)\n" + ; bool freebsd_smart_device::is_open() const @@ -241,586 +208,269 @@ bool freebsd_smart_device::is_open() const } -static int hpt_hba(const char* name) { - int i=0; - const char *hpt_node[]={"hptmv", "hptmv6", "hptrr", "hptiop", "hptmviop", "hpt32xx", "rr2320", - "rr232x", "rr2310", "rr2310_00", "rr2300", "rr2340", "rr1740", NULL}; - while (hpt_node[i]) { - if (!strncmp(name, hpt_node[i], strlen(hpt_node[i]))) - return 1; - i++; +bool freebsd_smart_device::open() +{ + const char *dev = get_dev_name(); + if ((m_fd = ::open(dev,O_RDONLY))<0) { + set_err(errno); + return false; } - return 0; + return true; } -static int get_tw_channel_unit (const char* name, int* unit, int* dev) { - const char *p; - - /* device node sanity check */ - for (p = name + 3; *p; p++) - if (*p < '0' || *p > '9') - return -1; - if (strlen(name) > 4 && *(name + 3) == '0') - return -1; +bool freebsd_smart_device::close() +{ + int failed = 0; + // close device, if open + if (is_open()) + failed=::close(get_fd()); - if (dev != NULL) - *dev=atoi(name + 3); + set_fd(-1); - /* no need for unit number */ - if (unit != NULL) - *unit=0; - return 0; + if(failed) return false; + else return true; } -#ifndef IOCATAREQUEST -static int get_ata_channel_unit ( const char* name, int* unit, int* dev) { -#ifndef ATAREQUEST - *dev=0; - *unit=0; -return 0; -#else - // there is no direct correlation between name 'ad0, ad1, ...' and - // channel/unit number. So we need to iterate through the possible - // channels and check each unit to see if we match names - struct ata_cmd iocmd; - int fd,maxunit; - - bzero(&iocmd, sizeof(struct ata_cmd)); - - if ((fd = open(ATA_DEVICE, O_RDWR)) < 0) - return -errno; - - iocmd.cmd = ATAGMAXCHANNEL; - if (ioctl(fd, IOCATA, &iocmd) < 0) { - return -errno; - close(fd); - } - maxunit = iocmd.u.maxchan; - for (*unit = 0; *unit < maxunit; (*unit)++) { - iocmd.channel = *unit; - iocmd.device = -1; - iocmd.cmd = ATAGPARM; - if (ioctl(fd, IOCATA, &iocmd) < 0) { - close(fd); - return -errno; - } - if (iocmd.u.param.type[0] && !strcmp(name,iocmd.u.param.name[0])) { - *dev = 0; - break; - } - if (iocmd.u.param.type[1] && !strcmp(name,iocmd.u.param.name[1])) { - *dev = 1; - break; - } - } - close(fd); - if (*unit == maxunit) - return -1; - else - return 0; -#endif -} -#endif +///////////////////////////////////////////////////////////////////////////// +/// Implement standard ATA support -// Guess device type (ata or scsi) based on device name (FreeBSD -// specific) SCSI device name in FreeBSD can be sd, sr, scd, st, nst, -// osst, nosst and sg. -static const char * fbsd_dev_prefix = _PATH_DEV; -static const char * fbsd_dev_ata_disk_prefix = "ad"; -static const char * fbsd_dev_scsi_disk_plus = "da"; -static const char * fbsd_dev_scsi_pass = "pass"; -static const char * fbsd_dev_scsi_tape1 = "sa"; -static const char * fbsd_dev_scsi_tape2 = "nsa"; -static const char * fbsd_dev_scsi_tape3 = "esa"; -static const char * fbsd_dev_twe_ctrl = "twe"; -static const char * fbsd_dev_twa_ctrl = "twa"; -static const char * fbsd_dev_cciss = "ciss"; - -int parse_ata_chan_dev(const char * dev_name, struct freebsd_dev_channel *chan) { - int len; - int dev_prefix_len = strlen(fbsd_dev_prefix); - - // if dev_name null, or string length zero - if (!dev_name || !(len = strlen(dev_name))) - return CONTROLLER_UNKNOWN; - - // Remove the leading /dev/... if it's there - if (!strncmp(fbsd_dev_prefix, dev_name, dev_prefix_len)) { - if (len <= dev_prefix_len) - // if nothing else in the string, unrecognized - return CONTROLLER_UNKNOWN; - // else advance pointer to following characters - dev_name += dev_prefix_len; - } - // form /dev/ad* or ad* - if (!strncmp(fbsd_dev_ata_disk_prefix, dev_name, - strlen(fbsd_dev_ata_disk_prefix))) { -#ifndef IOCATAREQUEST - if (chan != NULL) { - if (get_ata_channel_unit(dev_name,&(chan->channel),&(chan->device))<0) { - return CONTROLLER_UNKNOWN; - } - } -#endif - return CONTROLLER_ATA; - } +class freebsd_ata_device +: public /*implements*/ ata_device, + public /*extends*/ freebsd_smart_device +{ +public: + freebsd_ata_device(smart_interface * intf, const char * dev_name, const char * req_type); + virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); - // form /dev/pass* or pass* - if (!strncmp(fbsd_dev_scsi_pass, dev_name, - strlen(fbsd_dev_scsi_pass))) - goto handlescsi; +protected: + virtual int do_cmd(struct ata_ioc_request* request, bool is_48bit_cmd); +}; - // form /dev/da* or da* - if (!strncmp(fbsd_dev_scsi_disk_plus, dev_name, - strlen(fbsd_dev_scsi_disk_plus))) - goto handlescsi; +freebsd_ata_device::freebsd_ata_device(smart_interface * intf, const char * dev_name, const char * req_type) +: smart_device(intf, dev_name, "ata", req_type), + freebsd_smart_device() +{ +} - // form /dev/sa* or sa* - if (!strncmp(fbsd_dev_scsi_tape1, dev_name, - strlen(fbsd_dev_scsi_tape1))) - goto handlescsi; +int freebsd_ata_device::do_cmd( struct ata_ioc_request* request, bool is_48bit_cmd) +{ + int fd = get_fd(), ret; + ARGUSED(is_48bit_cmd); // no support for 48 bit commands in the IOCATAREQUEST + ret = ioctl(fd, IOCATAREQUEST, request); + if (ret) set_err(errno); + return ret; +} - // form /dev/nsa* or nsa* - if (!strncmp(fbsd_dev_scsi_tape2, dev_name, - strlen(fbsd_dev_scsi_tape2))) - goto handlescsi; - // form /dev/esa* or esa* - if (!strncmp(fbsd_dev_scsi_tape3, dev_name, - strlen(fbsd_dev_scsi_tape3))) - goto handlescsi; - - if (!strncmp(fbsd_dev_twa_ctrl,dev_name, - strlen(fbsd_dev_twa_ctrl))) { - if (chan != NULL) { - if (get_tw_channel_unit(dev_name,&(chan->channel),&(chan->device))<0) { - return CONTROLLER_UNKNOWN; - } - } - else if (get_tw_channel_unit(dev_name,NULL,NULL)<0) { - return CONTROLLER_UNKNOWN; - } - return CONTROLLER_3WARE_9000_CHAR; - } - if (!strncmp(fbsd_dev_twe_ctrl,dev_name, - strlen(fbsd_dev_twe_ctrl))) { - if (chan != NULL) { - if (get_tw_channel_unit(dev_name,&(chan->channel),&(chan->device))<0) { - return CONTROLLER_UNKNOWN; - } - } - else if (get_tw_channel_unit(dev_name,NULL,NULL)<0) { - return CONTROLLER_UNKNOWN; +bool freebsd_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) +{ + bool ata_48bit = false; // no ata_48bit_support via IOCATAREQUEST + if(!strcmp("atacam",get_dev_type())) // enable for atacam interface + ata_48bit = true; + + if (!ata_cmd_is_ok(in, + true, // data_out_support + true, // multi_sector_support + ata_48bit) + ) { + set_err(ENOSYS, "48-bit ATA commands not implemented for legacy controllers"); + return false; } - return CONTROLLER_3WARE_678K_CHAR; - } - if (hpt_hba(dev_name)) { - return CONTROLLER_HPT; - } + struct ata_ioc_request request; + bzero(&request,sizeof(struct ata_ioc_request)); - // form /dev/ciss* - if (!strncmp(fbsd_dev_cciss, dev_name, - strlen(fbsd_dev_cciss))) - return CONTROLLER_CCISS; + request.timeout=SCSI_TIMEOUT_DEFAULT; + request.u.ata.command=in.in_regs.command; + request.u.ata.feature=in.in_regs.features; - // we failed to recognize any of the forms - return CONTROLLER_UNKNOWN; + request.u.ata.count = in.in_regs.sector_count_16; + request.u.ata.lba = in.in_regs.lba_48; - handlescsi: - if (chan != NULL) { - if (!(chan->devname = (char *)calloc(1,DEV_IDLEN+1))) - return CONTROLLER_UNKNOWN; - - if (cam_get_device(dev_name,chan->devname,DEV_IDLEN,&(chan->unitnum)) == -1) - return CONTROLLER_UNKNOWN; - } - return CONTROLLER_SCSI; - -} - - -bool freebsd_smart_device::open() -{ - - const char *dev = get_dev_name(); - struct freebsd_dev_channel *fdchan; - int parse_ok, i; - - // Search table for a free entry - for (i=0; idevice = ::open(dev,O_RDONLY))<0) { -#else - if ((fdchan->atacommand = ::open("/dev/ata",O_RDWR))<0) { -#endif - int myerror = errno; // preserve across free call - free(fdchan); - errno = myerror; + clear_err(); + errno = 0; + if (do_cmd(&request, in.in_regs.is_48bit_cmd())) return false; - } - } + if (request.error) + return set_err(EIO, "request failed, error code 0x%02x", request.error); - if (parse_ok == CONTROLLER_3WARE_678K_CHAR) { - char buf[512]; - sprintf(buf,"/dev/twe%d",fdchan->device); -#ifdef IOCATAREQUEST - if ((fdchan->device = ::open(buf,O_RDWR))<0) { -#else - if ((fdchan->atacommand = ::open(buf,O_RDWR))<0) { -#endif - int myerror = errno; // preserve across free call - free(fdchan); - errno = myerror; - return false; - } - } + out.out_regs.error = request.error; + out.out_regs.sector_count_16 = request.u.ata.count; + out.out_regs.lba_48 = request.u.ata.lba; - if (parse_ok == CONTROLLER_3WARE_9000_CHAR) { - char buf[512]; - sprintf(buf,"/dev/twa%d",fdchan->device); -#ifdef IOCATAREQUEST - if ((fdchan->device = ::open(buf,O_RDWR))<0) { -#else - if ((fdchan->atacommand = ::open(buf,O_RDWR))<0) { -#endif - int myerror = errno; // preserve across free call - free(fdchan); - errno = myerror; - return false; - } - } - if (parse_ok == CONTROLLER_HPT) { - if ((fdchan->device = ::open(dev,O_RDWR))<0) { - int myerror = errno; // preserve across free call - free(fdchan); - errno = myerror; - return false; - } - } + // Command specific processing + if (in.in_regs.command == ATA_SMART_CMD + && in.in_regs.features == ATA_SMART_STATUS + && in.out_needed.lba_high) + { + unsigned const char normal_lo=0x4f, normal_hi=0xc2; + unsigned const char failed_lo=0xf4, failed_hi=0x2c; - if (parse_ok == CONTROLLER_CCISS) { - if ((fdchan->device = ::open(dev,O_RDWR))<0) { - int myerror = errno; // preserve across free call - free(fdchan); - errno = myerror; - return false; + // Cyl low and Cyl high unchanged means "Good SMART status" + if (!(out.out_regs.lba_mid==normal_lo && out.out_regs.lba_high==normal_hi) + // These values mean "Bad SMART status" + && !(out.out_regs.lba_mid==failed_lo && out.out_regs.lba_high==failed_hi)) + + { + // We haven't gotten output that makes sense; print out some debugging info + char buf[512]; + snprintf(buf, sizeof(buf), + "CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n", + (int)request.u.ata.command, + (int)request.u.ata.feature, + (int)request.u.ata.count, + (int)((request.u.ata.lba) & 0xff), + (int)((request.u.ata.lba>>8) & 0xff), + (int)((request.u.ata.lba>>16) & 0xff), + (int)request.error); + printwarning(BAD_SMART,buf); + out.out_regs.lba_high = failed_hi; + out.out_regs.lba_mid = failed_lo; } } - if (parse_ok == CONTROLLER_SCSI) { - // this is really a NO-OP, as the parse takes care - // of filling in correct details - } - - // return pointer to "file descriptor" table entry, properly offset. - devicetable[i]=fdchan; - m_fd = i+FREEBSD_FDOFFSET; - // endofold - if (m_fd < 0) { - set_err((errno==ENOENT || errno==ENOTDIR) ? ENODEV : errno); - return false; - } return true; } -bool freebsd_smart_device::close() +#if FREEBSDVER > 800100 +class freebsd_atacam_device : public freebsd_ata_device { - int fd = m_fd; m_fd = -1; - struct freebsd_dev_channel *fdchan; - int failed = 0; - - // check for valid file descriptor - if (isnotopen(&fd, &fdchan)) - return false; +public: + freebsd_atacam_device(smart_interface * intf, const char * dev_name, const char * req_type) + : smart_device(intf, dev_name, "atacam", req_type), freebsd_ata_device(intf, dev_name, req_type) + {} - - // did we allocate a SCSI device name? - if (fdchan->devname) - free(fdchan->devname); + virtual bool open(); + virtual bool close(); - // close device, if open - if (fdchan->device) - failed=::close(fdchan->device); -#ifndef IOCATAREQUEST - if (fdchan->atacommand) - failed=::close(fdchan->atacommand); -#endif +protected: + int m_fd; + struct cam_device *m_camdev; + + virtual int do_cmd( struct ata_ioc_request* request , bool is_48bit_cmd); +}; + +bool freebsd_atacam_device::open(){ + const char *dev = get_dev_name(); - // if close succeeded, then remove from device list - // Eduard, should we also remove it from list if close() fails? I'm - // not sure. Here I only remove it from list if close() worked. - if (!failed) { - free(fdchan); - devicetable[fd]=NULL; + if ((m_camdev = cam_open_device(dev, O_RDWR)) == NULL) { + set_err(errno); + return false; } - - return failed; + set_fd(m_camdev->fd); + return true; } -///////////////////////////////////////////////////////////////////////////// -/// Implement standard ATA support with old functions +bool freebsd_atacam_device::close(){ + cam_close_device(m_camdev); + set_fd(-1); + return true; +} -class freebsd_ata_device -: public /*implements*/ ata_device_with_command_set, - public /*extends*/ freebsd_smart_device +int freebsd_atacam_device::do_cmd( struct ata_ioc_request* request, bool is_48bit_cmd) { -public: - freebsd_ata_device(smart_interface * intf, const char * dev_name, const char * req_type); + union ccb ccb; + int camflags; + + // FIXME: + // 48bit commands are broken in ATACAM before r242422/HEAD + // and may cause system hang + // Waiting for MFC to make sure that bug is fixed, + // later version check needs to be added + if(!strcmp("ata",m_camdev->sim_name) && is_48bit_cmd) { + set_err(ENOSYS, "48-bit ATA commands not implemented for legacy controllers"); + return -1; + } -#ifdef HAVE_ATA_IDENTIFY_IS_CACHED - virtual bool ata_identify_is_cached() const; -#endif + memset(&ccb, 0, sizeof(ccb)); -protected: - virtual int ata_command_interface(smart_command_set command, int select, char * data); -}; + if (request->count == 0) + camflags = CAM_DIR_NONE; + else if (request->flags & ATA_CMD_READ) + camflags = CAM_DIR_IN; + else + camflags = CAM_DIR_OUT; + + cam_fill_ataio(&ccb.ataio, + 0, + NULL, + camflags, + MSG_SIMPLE_Q_TAG, + (u_int8_t*)request->data, + request->count, + request->timeout * 1000); // timeout in seconds + + ccb.ataio.cmd.flags = CAM_ATAIO_NEEDRESULT | + (is_48bit_cmd ? CAM_ATAIO_48BIT : 0); + // ata_28bit_cmd + ccb.ataio.cmd.command = request->u.ata.command; + ccb.ataio.cmd.features = request->u.ata.feature; + ccb.ataio.cmd.lba_low = request->u.ata.lba; + ccb.ataio.cmd.lba_mid = request->u.ata.lba >> 8; + ccb.ataio.cmd.lba_high = request->u.ata.lba >> 16; + // ata_48bit cmd + ccb.ataio.cmd.lba_low_exp = request->u.ata.lba >> 24; + ccb.ataio.cmd.lba_mid_exp = request->u.ata.lba >> 32; + ccb.ataio.cmd.lba_high_exp = request->u.ata.lba >> 40; + ccb.ataio.cmd.device = 0x40 | ((request->u.ata.lba >> 24) & 0x0f); + ccb.ataio.cmd.sector_count = request->u.ata.count; + ccb.ataio.cmd.sector_count_exp = request->u.ata.count >> 8;; + + ccb.ccb_h.flags |= CAM_DEV_QFRZDIS; + + if (cam_send_ccb(m_camdev, &ccb) < 0) { + set_err(EIO, "cam_send_ccb failed"); + return -1; + } -freebsd_ata_device::freebsd_ata_device(smart_interface * intf, const char * dev_name, const char * req_type) -: smart_device(intf, dev_name, "ata", req_type), - freebsd_smart_device("ATA") -{ -} + if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + if(scsi_debugmode > 0) + cam_error_print(m_camdev, &ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); + set_err(EIO); + return -1; + } -int freebsd_ata_device::ata_command_interface(smart_command_set command, int select, char * data) -{ - int fd=get_fd(); - #if !defined(ATAREQUEST) && !defined(IOCATAREQUEST) - // sorry, but without ATAng, we can't do anything here - printwarning(BAD_KERNEL,NULL); - errno = ENOSYS; - return -1; - #else - struct freebsd_dev_channel* con; - int retval, copydata=0; - #ifdef IOCATAREQUEST - struct ata_ioc_request request; - #else - struct ata_cmd iocmd; - #endif - unsigned char buff[512]; - - // check that "file descriptor" is valid - if (isnotopen(&fd,&con)) - return -1; - - bzero(buff,512); - - #ifdef IOCATAREQUEST - bzero(&request,sizeof(struct ata_ioc_request)); - #else - bzero(&iocmd,sizeof(struct ata_cmd)); - #endif - bzero(buff,512); - - #ifndef IOCATAREQUEST - iocmd.cmd=ATAREQUEST; - iocmd.channel=con->channel; - iocmd.device=con->device; - #define request iocmd.u.request - #endif - - request.u.ata.command=ATA_SMART_CMD; - request.timeout=600; - switch (command){ - case READ_VALUES: - request.u.ata.feature=ATA_SMART_READ_VALUES; - request.u.ata.lba=0xc24f<<8; - request.flags=ATA_CMD_READ; - request.data=(char *)buff; - request.count=512; - copydata=1; - break; - case READ_THRESHOLDS: - request.u.ata.feature=ATA_SMART_READ_THRESHOLDS; - request.u.ata.count=1; - request.u.ata.lba=1|(0xc24f<<8); - request.flags=ATA_CMD_READ; - request.data=(char *)buff; - request.count=512; - copydata=1; - break; - case READ_LOG: - request.u.ata.feature=ATA_SMART_READ_LOG_SECTOR; - request.u.ata.lba=select|(0xc24f<<8); - request.u.ata.count=1; - request.flags=ATA_CMD_READ; - request.data=(char *)buff; - request.count=512; - copydata=1; - break; - case IDENTIFY: - request.u.ata.command=ATA_IDENTIFY_DEVICE; - request.flags=ATA_CMD_READ; - request.data=(char *)buff; - request.count=512; - copydata=1; - break; - case PIDENTIFY: - request.u.ata.command=ATA_IDENTIFY_PACKET_DEVICE; - request.flags=ATA_CMD_READ; - request.data=(char *)buff; - request.count=512; - copydata=1; - break; - case ENABLE: - request.u.ata.feature=ATA_SMART_ENABLE; - request.u.ata.lba=0xc24f<<8; - request.flags=ATA_CMD_CONTROL; - break; - case DISABLE: - request.u.ata.feature=ATA_SMART_DISABLE; - request.u.ata.lba=0xc24f<<8; - request.flags=ATA_CMD_CONTROL; - break; - case AUTO_OFFLINE: - // NOTE: According to ATAPI 4 and UP, this command is obsolete - request.u.ata.feature=ATA_SMART_AUTO_OFFLINE; - request.u.ata.lba=0xc24f<<8; - request.u.ata.count=select; - request.flags=ATA_CMD_CONTROL; - break; - case AUTOSAVE: - request.u.ata.feature=ATA_SMART_AUTOSAVE; - request.u.ata.lba=0xc24f<<8; - request.u.ata.count=select; - request.flags=ATA_CMD_CONTROL; - break; - case IMMEDIATE_OFFLINE: - request.u.ata.feature=ATA_SMART_IMMEDIATE_OFFLINE; - request.u.ata.lba = select|(0xc24f<<8); // put test in sector - request.flags=ATA_CMD_CONTROL; - break; - case STATUS_CHECK: // same command, no HDIO in FreeBSD - case STATUS: - // this command only says if SMART is working. It could be - // replaced with STATUS_CHECK below. - request.u.ata.feature=ATA_SMART_STATUS; - request.u.ata.lba=0xc24f<<8; - request.flags=ATA_CMD_CONTROL; - break; - case CHECK_POWER_MODE: - request.u.ata.command=ATA_CHECK_POWER_MODE; - request.u.ata.feature=0; - request.flags=ATA_CMD_CONTROL; - break; - case WRITE_LOG: - memcpy(buff, data, 512); - request.u.ata.feature=ATA_SMART_WRITE_LOG_SECTOR; - request.u.ata.lba=select|(0xc24f<<8); - request.u.ata.count=1; - request.flags=ATA_CMD_WRITE; - request.data=(char *)buff; - request.count=512; - break; - default: - pout("Unrecognized command %d in ata_command_interface()\n" - "Please contact " PACKAGE_BUGREPORT "\n", command); - errno=ENOSYS; - return -1; - } - - if (command==STATUS_CHECK){ - unsigned const char normal_lo=0x4f, normal_hi=0xc2; - unsigned const char failed_lo=0xf4, failed_hi=0x2c; - unsigned char low,high; - - #ifdef IOCATAREQUEST - if ((retval=ioctl(con->device, IOCATAREQUEST, &request)) || request.error) - #else - if ((retval=ioctl(con->atacommand, IOCATA, &iocmd)) || request.error) - #endif - return -1; - - #if __FreeBSD_version < 502000 - printwarning(NO_RETURN,NULL); - #endif - - high = (request.u.ata.lba >> 16) & 0xff; - low = (request.u.ata.lba >> 8) & 0xff; - - // Cyl low and Cyl high unchanged means "Good SMART status" - if (low==normal_lo && high==normal_hi) - return 0; - - // These values mean "Bad SMART status" - if (low==failed_lo && high==failed_hi) - return 1; - - // We haven't gotten output that makes sense; print out some debugging info - char buf[512]; - sprintf(buf,"CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n", - (int)request.u.ata.command, - (int)request.u.ata.feature, - (int)request.u.ata.count, - (int)((request.u.ata.lba) & 0xff), - (int)((request.u.ata.lba>>8) & 0xff), - (int)((request.u.ata.lba>>16) & 0xff), - (int)request.error); - printwarning(BAD_SMART,buf); - return 0; - } - - #ifdef IOCATAREQUEST - if ((retval=ioctl(con->device, IOCATAREQUEST, &request)) || request.error) - #else - if ((retval=ioctl(con->atacommand, IOCATA, &iocmd)) || request.error) - #endif - { - return -1; - } - // - if (command == CHECK_POWER_MODE) { - data[0] = request.u.ata.count & 0xff; - return 0; - } - if (copydata) - memcpy(data, buff, 512); - - return 0; - #endif -} + request->u.ata.lba = + ((u_int64_t)(ccb.ataio.res.lba_low)) | + ((u_int64_t)(ccb.ataio.res.lba_mid) << 8) | + ((u_int64_t)(ccb.ataio.res.lba_high) << 16) | + ((u_int64_t)(ccb.ataio.res.lba_low_exp) << 24) | + ((u_int64_t)(ccb.ataio.res.lba_mid_exp) << 32) | + ((u_int64_t)(ccb.ataio.res.lba_high_exp) << 40); -#ifdef HAVE_ATA_IDENTIFY_IS_CACHED -bool freebsd_ata_device::ata_identify_is_cached() const -{ - return !!::ata_identify_is_cached(get_fd()); + request->u.ata.count = ccb.ataio.res.sector_count | (ccb.ataio.res.sector_count_exp << 8); + request->error = ccb.ataio.res.error; + + return 0; } -#endif +#endif ///////////////////////////////////////////////////////////////////////////// -/// Implement AMCC/3ware RAID support with old functions +/// Implement AMCC/3ware RAID support class freebsd_escalade_device -: public /*implements*/ ata_device_with_command_set, +: public /*implements*/ ata_device, public /*extends*/ freebsd_smart_device { public: @@ -828,7 +478,8 @@ public: int escalade_type, int disknum); protected: - virtual int ata_command_interface(smart_command_set command, int select, char * data); + virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); + virtual bool open(); private: int m_escalade_type; ///< Type string for escalade_command_interface(). @@ -838,23 +489,37 @@ private: freebsd_escalade_device::freebsd_escalade_device(smart_interface * intf, const char * dev_name, int escalade_type, int disknum) : smart_device(intf, dev_name, "3ware", "3ware"), - freebsd_smart_device( - escalade_type==CONTROLLER_3WARE_9000_CHAR ? "ATA_3WARE_9000" : - escalade_type==CONTROLLER_3WARE_678K_CHAR ? "ATA_3WARE_678K" : - /* CONTROLLER_3WARE_678K */ "ATA" ), + freebsd_smart_device(), m_escalade_type(escalade_type), m_disknum(disknum) { set_info().info_name = strprintf("%s [3ware_disk_%02d]", dev_name, disknum); } -int freebsd_escalade_device::ata_command_interface(smart_command_set command, int select, char * data) +bool freebsd_escalade_device::open() { - // to hold true file descriptor - int fd = get_fd(); - struct freebsd_dev_channel* con; + const char *dev = get_dev_name(); + int fd; + + if ((fd = ::open(dev,O_RDWR))<0) { + set_err(errno); + return false; + } + set_fd(fd); + return true; +} + +bool freebsd_escalade_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) +{ + // to hold true file descriptor + int fd = get_fd(); + + if (!ata_cmd_is_ok(in, + true, // data_out_support + false, // TODO: multi_sector_support + true) // ata_48bit_support + ) + return false; - // return value and buffer for ioctl() - int ioctlreturn, readdata=0; struct twe_usercommand* cmd_twe = NULL; TW_OSLI_IOCTL_NO_DATA_BUF* cmd_twa = NULL; TWE_Command_ATA* ata = NULL; @@ -867,25 +532,21 @@ int freebsd_escalade_device::ata_command_interface(smart_command_set command, in return -1; } - // check that "file descriptor" is valid - if (isnotopen(&fd,&con)) - return -1; - memset(ioctl_buffer, 0, TW_IOCTL_BUFFER_SIZE); if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR) { cmd_twa = (TW_OSLI_IOCTL_NO_DATA_BUF*)ioctl_buffer; cmd_twa->pdata = ((TW_OSLI_IOCTL_WITH_PAYLOAD*)cmd_twa)->payload.data_buf; - cmd_twa->driver_pkt.buffer_length = 512; + cmd_twa->driver_pkt.buffer_length = in.size; + // using "old" packet format to speak with SATA devices ata = (TWE_Command_ATA*)&cmd_twa->cmd_pkt.command.cmd_pkt_7k; } else if (m_escalade_type==CONTROLLER_3WARE_678K_CHAR) { cmd_twe = (struct twe_usercommand*)ioctl_buffer; ata = &cmd_twe->tu_command.ata; } else { - pout("Unrecognized escalade_type %d in freebsd_3ware_command_interface(disk %d)\n" - "Please contact " PACKAGE_BUGREPORT "\n", m_escalade_type, m_disknum); - errno=ENOSYS; - return -1; + return set_err(ENOSYS, + "Unrecognized escalade_type %d in linux_3ware_command_interface(disk %d)\n" + "Please contact " PACKAGE_BUGREPORT "\n", (int)m_escalade_type, m_disknum); } ata->opcode = TWE_OP_ATA_PASSTHROUGH; @@ -893,19 +554,21 @@ int freebsd_escalade_device::ata_command_interface(smart_command_set command, in // Same for (almost) all commands - but some reset below ata->request_id = 0xFF; ata->unit = m_disknum; - ata->status = 0; + ata->status = 0; ata->flags = 0x1; - ata->drive_head = 0x0; - ata->sector_num = 0; + ata->size = 0x5; // TODO: multisector support + // Set registers + { + const ata_in_regs_48bit & r = in.in_regs; + ata->features = r.features_16; + ata->sector_count = r.sector_count_16; + ata->sector_num = r.lba_low_16; + ata->cylinder_lo = r.lba_mid_16; + ata->cylinder_hi = r.lba_high_16; + ata->drive_head = r.device; + ata->command = r.command; + } - // All SMART commands use this CL/CH signature. These are magic - // values from the ATA specifications. - ata->cylinder_lo = 0x4F; - ata->cylinder_hi = 0xC2; - - // SMART ATA COMMAND REGISTER value - ata->command = ATA_SMART_CMD; - // Is this a command that reads or returns 512 bytes? // passthru->param values are: // 0x0 - non data command without TFR write check, @@ -913,133 +576,62 @@ int freebsd_escalade_device::ata_command_interface(smart_command_set command, in // 0xD - data command that returns data to host from device // 0xF - data command that writes data from host to device // passthru->size values are 0x5 for non-data and 0x07 for data - if (command == READ_VALUES || - command == READ_THRESHOLDS || - command == READ_LOG || - command == IDENTIFY || - command == WRITE_LOG ) { - readdata=1; + bool readdata = false; + if (in.direction == ata_cmd_in::data_in) { if (m_escalade_type==CONTROLLER_3WARE_678K_CHAR) { - cmd_twe->tu_data = data; + cmd_twe->tu_data = in.buffer; cmd_twe->tu_size = 512; } - ata->sgl_offset = 0x5; - ata->size = 0x5; + + readdata=true; + ata->sgl_offset = 0x5; ata->param = 0xD; - ata->sector_count = 0x1; // For 64-bit to work correctly, up the size of the command packet // in dwords by 1 to account for the 64-bit single sgl 'address' // field. Note that this doesn't agree with the typedefs but it's // right (agree with kernel driver behavior/typedefs). - //if (sizeof(long)==8) + // if (sizeof(long)==8) // ata->size++; } - else { + else if (in.direction == ata_cmd_in::no_data) { // Non data command -- but doesn't use large sector - // count register values. - ata->sgl_offset = 0x0; - ata->size = 0x5; + // count register values. + ata->sgl_offset = 0x0; ata->param = 0x8; ata->sector_count = 0x0; } - - // Now set ATA registers depending upon command - switch (command){ - case CHECK_POWER_MODE: - ata->command = ATA_CHECK_POWER_MODE; - ata->features = 0; - ata->cylinder_lo = 0; - ata->cylinder_hi = 0; - break; - case READ_VALUES: - ata->features = ATA_SMART_READ_VALUES; - break; - case READ_THRESHOLDS: - ata->features = ATA_SMART_READ_THRESHOLDS; - break; - case READ_LOG: - ata->features = ATA_SMART_READ_LOG_SECTOR; - // log number to return - ata->sector_num = select; - break; - case WRITE_LOG: - readdata=0; - ata->features = ATA_SMART_WRITE_LOG_SECTOR; - ata->sector_count = 1; - ata->sector_num = select; - ata->param = 0xF; // PIO data write - break; - case IDENTIFY: - // ATA IDENTIFY DEVICE - ata->command = ATA_IDENTIFY_DEVICE; - ata->features = 0; - ata->cylinder_lo = 0; - ata->cylinder_hi = 0; - break; - case PIDENTIFY: - // 3WARE controller can NOT have packet device internally - pout("WARNING - NO DEVICE FOUND ON 3WARE CONTROLLER (disk %d)\n", m_disknum); - errno=ENODEV; - return -1; - case ENABLE: - ata->features = ATA_SMART_ENABLE; - break; - case DISABLE: - ata->features = ATA_SMART_DISABLE; - break; - case AUTO_OFFLINE: - ata->features = ATA_SMART_AUTO_OFFLINE; - // Enable or disable? - ata->sector_count = select; - break; - case AUTOSAVE: - ata->features = ATA_SMART_AUTOSAVE; - // Enable or disable? - ata->sector_count = select; - break; - case IMMEDIATE_OFFLINE: - ata->features = ATA_SMART_IMMEDIATE_OFFLINE; - // What test type to run? - ata->sector_num = select; - break; - case STATUS_CHECK: - ata->features = ATA_SMART_STATUS; - break; - case STATUS: - // This is JUST to see if SMART is enabled, by giving SMART status - // command. But it doesn't say if status was good, or failing. - // See below for the difference. - ata->features = ATA_SMART_STATUS; - break; - default: - pout("Unrecognized command %d in freebsd_3ware_command_interface(disk %d)\n" - "Please contact " PACKAGE_BUGREPORT "\n", command, m_disknum); - errno=ENOSYS; - return -1; + else if (in.direction == ata_cmd_in::data_out) { + ata->sgl_offset = 0x5; + ata->param = 0xF; // PIO data write + if (m_escalade_type==CONTROLLER_3WARE_678K_CHAR) { + cmd_twe->tu_data = in.buffer; + cmd_twe->tu_size = 512; + } + else if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR) { + memcpy(cmd_twa->pdata, in.buffer, in.size); + } + } + else + return set_err(EINVAL); + + // 3WARE controller can NOT have packet device internally + if (in.in_regs.command == ATA_IDENTIFY_PACKET_DEVICE) { + return set_err(ENODEV, "No drive on port %d", m_disknum); } // Now send the command down through an ioctl() + int ioctlreturn; if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR) { -#ifdef IOCATAREQUEST - ioctlreturn=ioctl(con->device,TW_OSL_IOCTL_FIRMWARE_PASS_THROUGH,cmd_twa); -#else - ioctlreturn=ioctl(con->atacommand,TW_OSL_IOCTL_FIRMWARE_PASS_THROUGH,cmd_twa); -#endif + ioctlreturn=ioctl(fd,TW_OSL_IOCTL_FIRMWARE_PASS_THROUGH,cmd_twa); } else { -#ifdef IOCATAREQUEST - ioctlreturn=ioctl(con->device,TWEIO_COMMAND,cmd_twe); -#else - ioctlreturn=ioctl(con->atacommand,TWEIO_COMMAND,cmd_twe); -#endif + ioctlreturn=ioctl(fd,TWEIO_COMMAND,cmd_twe); } // Deal with the different error cases if (ioctlreturn) { - if (!errno) - errno=EIO; - return -1; + return set_err(EIO); } - + // See if the ATA command failed. Now that we have returned from // the ioctl() call, if passthru is valid, then: // - ata->status contains the 3ware controller STATUS @@ -1051,52 +643,38 @@ int freebsd_escalade_device::ata_command_interface(smart_command_set command, in // While we *might* decode the ATA ERROR register, at the moment it // doesn't make much sense: we don't care in detail why the error // happened. - + if (ata->status || (ata->command & 0x21)) { - pout("Command failed, ata.status=(0x%2.2x), ata.command=(0x%2.2x), ata.flags=(0x%2.2x)\n",ata->status,ata->command,ata->flags); - errno=EIO; - return -1; + if (scsi_debugmode) + pout("Command failed, ata.status=(0x%2.2x), ata.command=(0x%2.2x), ata.flags=(0x%2.2x)\n",ata->status,ata->command,ata->flags); + return set_err(EIO); } - + // If this is a read data command, copy data to output buffer if (readdata) { if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR) - memcpy(data, cmd_twa->pdata, 512); - } - - // For STATUS_CHECK, we need to check register values - if (command==STATUS_CHECK) { - - // To find out if the SMART RETURN STATUS is good or failing, we - // need to examine the values of the Cylinder Low and Cylinder - // High Registers. - - unsigned short cyl_lo=ata->cylinder_lo; - unsigned short cyl_hi=ata->cylinder_hi; - - // If values in Cyl-LO and Cyl-HI are unchanged, SMART status is good. - if (cyl_lo==0x4F && cyl_hi==0xC2) - return 0; - - // If values in Cyl-LO and Cyl-HI are as follows, SMART status is FAIL - if (cyl_lo==0xF4 && cyl_hi==0x2C) - return 1; - - errno=EIO; - return -1; + memcpy(in.buffer, cmd_twa->pdata, in.size); + else if(m_escalade_type==CONTROLLER_3WARE_678K_CHAR) { + memcpy(in.buffer, cmd_twe->tu_data, in.size); // untested + } + } + // Return register values + if (ata) { + ata_out_regs_48bit & r = out.out_regs; + r.error = ata->features; + r.sector_count_16 = ata->sector_count; + r.lba_low_16 = ata->sector_num; + r.lba_mid_16 = ata->cylinder_lo; + r.lba_high_16 = ata->cylinder_hi; + r.device = ata->drive_head; + r.status = ata->command; } - - // copy sector count register (one byte!) to return data - if (command==CHECK_POWER_MODE) - *data=*(char *)&(ata->sector_count); - // look for nonexistent devices/ports - if (command==IDENTIFY && !nonempty((unsigned char *)data, 512)) { - errno=ENODEV; - return -1; + if (in.in_regs.command == ATA_IDENTIFY_DEVICE + && !nonempty((unsigned char *)in.buffer, in.size)) { + return set_err(ENODEV, "No drive on port %d", m_disknum); } - - return 0; + return true; } @@ -1113,6 +691,7 @@ public: protected: virtual int ata_command_interface(smart_command_set command, int select, char * data); + virtual bool open(); private: unsigned char m_hpt_data[3]; ///< controller/channel/port @@ -1122,26 +701,34 @@ private: freebsd_highpoint_device::freebsd_highpoint_device(smart_interface * intf, const char * dev_name, unsigned char controller, unsigned char channel, unsigned char port) : smart_device(intf, dev_name, "hpt", "hpt"), - freebsd_smart_device("ATA") + freebsd_smart_device() { m_hpt_data[0] = controller; m_hpt_data[1] = channel; m_hpt_data[2] = port; set_info().info_name = strprintf("%s [hpt_disk_%u/%u/%u]", dev_name, m_hpt_data[0], m_hpt_data[1], m_hpt_data[2]); } +bool freebsd_highpoint_device::open() +{ + const char *dev = get_dev_name(); + int fd; + + if ((fd = ::open(dev,O_RDWR))<0) { + set_err(errno); + return false; + } + set_fd(fd); + return true; +} + int freebsd_highpoint_device::ata_command_interface(smart_command_set command, int select, char * data) { - int fd=get_fd(); + int fd=get_fd(); int ids[2]; - struct freebsd_dev_channel* fbcon; HPT_IOCTL_PARAM param; HPT_CHANNEL_INFO_V2 info; unsigned char* buff[512 + 2 * sizeof(HPT_PASS_THROUGH_HEADER)]; PHPT_PASS_THROUGH_HEADER pide_pt_hdr, pide_pt_hdr_out; - // check that "file descriptor" is valid - if (isnotopen(&fd, &fbcon)) - return -1; - // get internal deviceid ids[0] = m_hpt_data[0] - 1; ids[1] = m_hpt_data[1] - 1; @@ -1159,7 +746,7 @@ int freebsd_highpoint_device::ata_command_interface(smart_command_set command, i param.ctrl_code = HPT_IOCTL_GET_CHANNEL_INFO; param.out_size = sizeof(HPT_CHANNEL_INFO); } - if (ioctl(fbcon->device, HPT_DO_IOCONTROL, ¶m)!=0 || + if (ioctl(fd, HPT_DO_IOCONTROL, ¶m)!=0 || info.devices[m_hpt_data[2]-1]==0) { return -1; } @@ -1244,12 +831,13 @@ int freebsd_highpoint_device::ata_command_interface(smart_command_set command, i pide_pt_hdr_out = (PHPT_PASS_THROUGH_HEADER)param.out; - if ((ioctl(fbcon->device, HPT_DO_IOCONTROL, ¶m)!=0) || + if ((ioctl(fd, HPT_DO_IOCONTROL, ¶m)!=0) || (pide_pt_hdr_out->command & 1)) { return -1; } - if (command==STATUS_CHECK){ + if (command==STATUS_CHECK) + { unsigned const char normal_lo=0x4f, normal_hi=0xc2; unsigned const char failed_lo=0xf4, failed_hi=0x2c; unsigned char low,high; @@ -1267,7 +855,8 @@ int freebsd_highpoint_device::ata_command_interface(smart_command_set command, i // We haven't gotten output that makes sense; print out some debugging info char buf[512]; - sprintf(buf,"CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n", + snprintf(buf, sizeof(buf), + "CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n", (int)pide_pt_hdr_out->command, (int)pide_pt_hdr_out->feature, (int)pide_pt_hdr_out->sectorcount, @@ -1280,179 +869,356 @@ int freebsd_highpoint_device::ata_command_interface(smart_command_set command, i else if (command==CHECK_POWER_MODE) data[0] = pide_pt_hdr_out->sectorcount & 0xff; else if (pide_pt_hdr->protocol==HPT_READ) - memcpy(data, (unsigned char *)buff + 2 * sizeof(HPT_PASS_THROUGH_HEADER), pide_pt_hdr->sectors * 512); + memcpy(data, (unsigned char *)buff + 2 * sizeof(HPT_PASS_THROUGH_HEADER), + pide_pt_hdr->sectors * 512); return 0; } ///////////////////////////////////////////////////////////////////////////// -/// Implement standard SCSI support with old functions +/// Standard SCSI support + +class freebsd_scsi_device +: public /*implements*/ scsi_device, + public /*extends*/ freebsd_smart_device +{ +public: + freebsd_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type); + + virtual smart_device * autodetect_open(); + + virtual bool scsi_pass_through(scsi_cmnd_io * iop); + + virtual bool open(); + + virtual bool close(); + +private: + struct cam_device *m_camdev; +}; + +bool freebsd_scsi_device::open(){ + const char *dev = get_dev_name(); + + if ((m_camdev = cam_open_device(dev, O_RDWR)) == NULL) { + set_err(errno); + return false; + } + set_fd(m_camdev->fd); + return true; +} + +bool freebsd_scsi_device::close(){ + cam_close_device(m_camdev); + set_fd(-1); + return true; +} + +freebsd_scsi_device::freebsd_scsi_device(smart_interface * intf, + const char * dev_name, const char * req_type) +: smart_device(intf, dev_name, "scsi", req_type), + freebsd_smart_device() +{ +} + + +bool freebsd_scsi_device::scsi_pass_through(scsi_cmnd_io * iop) +{ + union ccb *ccb; + + if (scsi_debugmode) { + unsigned int k; + const unsigned char * ucp = iop->cmnd; + const char * np; + + np = scsi_get_opcode_name(ucp[0]); + pout(" [%s: ", np ? np : ""); + for (k = 0; k < iop->cmnd_len; ++k) + pout("%02x ", ucp[k]); + if ((scsi_debugmode > 1) && + (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { + int trunc = (iop->dxfer_len > 256) ? 1 : 0; + + pout("]\n Outgoing data, len=%d%s:\n", (int)iop->dxfer_len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); + } + else + pout("]\n"); + } + + if(m_camdev==NULL) { + if (scsi_debugmode) + pout(" error: camdev=0!\n"); + return set_err(ENOTTY); + } + + if (!(ccb = cam_getccb(m_camdev))) { + if (scsi_debugmode) + pout(" error allocating ccb\n"); + return set_err(ENOMEM); + } + + // mfi SAT layer is known to be buggy + if(!strcmp("mfi",m_camdev->sim_name)) { + if (iop->cmnd[0] == SAT_ATA_PASSTHROUGH_12 || iop->cmnd[0] == SAT_ATA_PASSTHROUGH_16) { + // Controller does not return ATA output registers in SAT sense data + if (iop->cmnd[2] & (1 << 5)) // chk_cond + return set_err(ENOSYS, "ATA return descriptor not supported by controller firmware"); + } + // SMART WRITE LOG SECTOR causing media errors + if ((iop->cmnd[0] == SAT_ATA_PASSTHROUGH_16 + && iop->cmnd[14] == ATA_SMART_CMD && iop->cmnd[3]==0 && + iop->cmnd[4] == ATA_SMART_WRITE_LOG_SECTOR) || + (iop->cmnd[0] == SAT_ATA_PASSTHROUGH_12 + && iop->cmnd[9] == ATA_SMART_CMD && iop->cmnd[3] == ATA_SMART_WRITE_LOG_SECTOR)) + { + if(!failuretest_permissive) + return set_err(ENOSYS, "SMART WRITE LOG SECTOR may cause problems, try with -T permissive to force"); + } + } + // clear out structure, except for header that was filled in for us + bzero(&(&ccb->ccb_h)[1], + sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); + + cam_fill_csio(&ccb->csio, + /* retries */ 1, + /* cbfcnp */ NULL, + /* flags */ (iop->dxfer_dir == DXFER_NONE ? CAM_DIR_NONE :(iop->dxfer_dir == DXFER_FROM_DEVICE ? CAM_DIR_IN : CAM_DIR_OUT)), + /* tagaction */ MSG_SIMPLE_Q_TAG, + /* dataptr */ iop->dxferp, + /* datalen */ iop->dxfer_len, + /* senselen */ iop->max_sense_len, + /* cdblen */ iop->cmnd_len, + /* timout (converted to seconds) */ iop->timeout*1000); + memcpy(ccb->csio.cdb_io.cdb_bytes,iop->cmnd,iop->cmnd_len); + + if (cam_send_ccb(m_camdev,ccb) < 0) { + if (scsi_debugmode) { + pout(" error sending SCSI ccb\n"); + cam_error_print(m_camdev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr); + } + cam_freeccb(ccb); + return set_err(EIO); + } + + if (scsi_debugmode) { + pout(" CAM status=0x%x, SCSI status=0x%x, resid=0x%x\n", + ccb->ccb_h.status, ccb->csio.scsi_status, ccb->csio.resid); + if ((scsi_debugmode > 1) && (DXFER_FROM_DEVICE == iop->dxfer_dir)) { + int trunc, len; + + len = iop->dxfer_len - ccb->csio.resid; + trunc = (len > 256) ? 1 : 0; + if (len > 0) { + pout(" Incoming data, len=%d%s:\n", len, + (trunc ? " [only first 256 bytes shown]" : "")); + dStrHex(iop->dxferp, (trunc ? 256 : len), 1); + } + else + pout(" Incoming data trimmed to nothing by resid\n"); + } + } + + if (((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) && ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_SCSI_STATUS_ERROR)) { + if (scsi_debugmode) + cam_error_print(m_camdev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr); + cam_freeccb(ccb); + return set_err(EIO); + } + + iop->resid = ccb->csio.resid; + iop->scsi_status = ccb->csio.scsi_status; + if (iop->sensep && (ccb->ccb_h.status & CAM_AUTOSNS_VALID) != 0) { + if (scsi_debugmode) + pout(" sense_len=0x%x, sense_resid=0x%x\n", + ccb->csio.sense_len, ccb->csio.sense_resid); + iop->resp_sense_len = ccb->csio.sense_len - ccb->csio.sense_resid; + /* Some SCSI controller device drivers miscalculate the sense_resid + field so cap resp_sense_len on max_sense_len. */ + if (iop->resp_sense_len > iop->max_sense_len) + iop->resp_sense_len = iop->max_sense_len; + if (iop->resp_sense_len > 0) { + memcpy(iop->sensep, &(ccb->csio.sense_data), iop->resp_sense_len); + if (scsi_debugmode) { + if (scsi_debugmode > 1) { + pout(" >>> Sense buffer, len=%zu:\n", iop->resp_sense_len); + dStrHex(iop->sensep, iop->resp_sense_len, 1); + } + if ((iop->sensep[0] & 0x7f) > 0x71) + pout(" status=0x%x: [desc] sense_key=0x%x asc=0x%x ascq=0x%x\n", + iop->scsi_status, iop->sensep[1] & 0xf, + iop->sensep[2], iop->sensep[3]); + else + pout(" status=0x%x: sense_key=0x%x asc=0x%x ascq=0x%x\n", + iop->scsi_status, iop->sensep[2] & 0xf, + iop->sensep[12], iop->sensep[13]); + } + } + else if (scsi_debugmode) + pout(" status=0x%x\n", iop->scsi_status); + } + else if (scsi_debugmode) + pout(" status=0x%x\n", iop->scsi_status); + + cam_freeccb(ccb); + + // mfip replacing PDT of the device so response does not make a sense + // this sets PDT to 00h - direct-access block device + if((!strcmp("mfi", m_camdev->sim_name) || !strcmp("mpt", m_camdev->sim_name)) + && iop->cmnd[0] == INQUIRY) { + if (scsi_debugmode) { + pout(" device on %s controller, patching PDT\n", m_camdev->sim_name); + } + iop->dxferp[0] = iop->dxferp[0] & 0xe0; + } + + return true; +} + + +///////////////////////////////////////////////////////////////////////////// +/// Areca RAID support -class freebsd_scsi_device -: public /*implements*/ scsi_device, +/////////////////////////////////////////////////////////////////// +// SATA(ATA) device behind Areca RAID Controller +class freebsd_areca_ata_device +: public /*implements*/ areca_ata_device, public /*extends*/ freebsd_smart_device { public: - freebsd_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type); - + freebsd_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); virtual smart_device * autodetect_open(); - - virtual bool scsi_pass_through(scsi_cmnd_io * iop); + virtual bool arcmsr_lock(); + virtual bool arcmsr_unlock(); + virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop); }; -freebsd_scsi_device::freebsd_scsi_device(smart_interface * intf, - const char * dev_name, const char * req_type) -: smart_device(intf, dev_name, "scsi", req_type), - freebsd_smart_device("SCSI") +/////////////////////////////////////////////////////////////////// +// SAS(SCSI) device behind Areca RAID Controller +class freebsd_areca_scsi_device +: public /*implements*/ areca_scsi_device, + public /*extends*/ freebsd_smart_device { -} +public: + freebsd_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); + virtual smart_device * autodetect_open(); + virtual bool arcmsr_lock(); + virtual bool arcmsr_unlock(); + virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop); +}; -// Interface to SCSI devices. See os_linux.c -int do_normal_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) + +// Areca RAID Controller(SATA Disk) +freebsd_areca_ata_device::freebsd_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) +: smart_device(intf, dev_name, "areca", "areca"), + freebsd_smart_device() { - struct freebsd_dev_channel* con = NULL; - struct cam_device* cam_dev = NULL; - union ccb *ccb; - - - if (report > 0) { - unsigned int k; - const unsigned char * ucp = iop->cmnd; - const char * np; - - np = scsi_get_opcode_name(ucp[0]); - pout(" [%s: ", np ? np : ""); - for (k = 0; k < iop->cmnd_len; ++k) - pout("%02x ", ucp[k]); - if ((report > 1) && - (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { - int trunc = (iop->dxfer_len > 256) ? 1 : 0; - - pout("]\n Outgoing data, len=%d%s:\n", (int)iop->dxfer_len, - (trunc ? " [only first 256 bytes shown]" : "")); - dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); - } - else - pout("]"); - } + set_disknum(disknum); + set_encnum(encnum); + set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); +} - // check that "file descriptor" is valid - if (isnotopen(&fd,&con)) - return -ENOTTY; +smart_device * freebsd_areca_ata_device::autodetect_open() +{ + int is_ata = 1; - if (!(cam_dev = cam_open_spec_device(con->devname,con->unitnum,O_RDWR,NULL))) { - warnx("%s",cam_errbuf); - return -EIO; + // autodetect device type + is_ata = arcmsr_get_dev_type(); + if(is_ata < 0) + { + set_err(EIO); + return this; } - if (!(ccb = cam_getccb(cam_dev))) { - warnx("error allocating ccb"); - return -ENOMEM; + if(is_ata == 1) + { + // SATA device + return this; } - // clear out structure, except for header that was filled in for us - bzero(&(&ccb->ccb_h)[1], - sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); + // SAS device + smart_device_auto_ptr newdev(new freebsd_areca_scsi_device(smi(), get_dev_name(), get_disknum(), get_encnum())); + close(); + delete this; + newdev->open(); // TODO: Can possibly pass open fd - cam_fill_csio(&ccb->csio, - /*retrires*/ 1, - /*cbfcnp*/ NULL, - /* flags */ (iop->dxfer_dir == DXFER_NONE ? CAM_DIR_NONE :(iop->dxfer_dir == DXFER_FROM_DEVICE ? CAM_DIR_IN : CAM_DIR_OUT)), - /* tagaction */ MSG_SIMPLE_Q_TAG, - /* dataptr */ iop->dxferp, - /* datalen */ iop->dxfer_len, - /* senselen */ iop->max_sense_len, - /* cdblen */ iop->cmnd_len, - /* timout (converted to seconds) */ iop->timeout*1000); - memcpy(ccb->csio.cdb_io.cdb_bytes,iop->cmnd,iop->cmnd_len); + return newdev.release(); +} - if (cam_send_ccb(cam_dev,ccb) < 0) { - warn("error sending SCSI ccb"); - #if __FreeBSD_version > 500000 - cam_error_print(cam_dev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr); - #endif - cam_freeccb(ccb); - return -EIO; - } +int freebsd_areca_ata_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop) +{ + int ioctlreturn = 0; - if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { - #if __FreeBSD_version > 500000 - cam_error_print(cam_dev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr); - #endif - cam_freeccb(ccb); - return -EIO; + if(!is_open()) { + if(!open()){ + } } - if (iop->sensep) { - memcpy(iop->sensep,&(ccb->csio.sense_data),sizeof(struct scsi_sense_data)); - iop->resp_sense_len = sizeof(struct scsi_sense_data); + ioctlreturn = ioctl(get_fd(), ((sSRB_BUFFER *)(iop->dxferp))->srbioctl.ControlCode, iop->dxferp); + if (ioctlreturn) + { + // errors found + return -1; } - iop->scsi_status = ccb->csio.scsi_status; + return ioctlreturn; +} - cam_freeccb(ccb); - - if (cam_dev) - cam_close_device(cam_dev); +bool freebsd_areca_ata_device::arcmsr_lock() +{ + return true; +} - if (report > 0) { - int trunc; - pout(" status=0\n"); - trunc = (iop->dxfer_len > 256) ? 1 : 0; - - pout(" Incoming data, len=%d%s:\n", (int)iop->dxfer_len, - (trunc ? " [only first 256 bytes shown]" : "")); - dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); - } - return 0; +bool freebsd_areca_ata_device::arcmsr_unlock() +{ + return true; } -/* Check and call the right interface. Maybe when the do_generic_scsi_cmd_io interface is better - we can take off this crude way of calling the right interface */ -int do_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report) +// Areca RAID Controller(SAS Device) +freebsd_areca_scsi_device::freebsd_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) +: smart_device(intf, dev_name, "areca", "areca"), + freebsd_smart_device() { -struct freebsd_dev_channel *fdchan; - switch(m_controller_type) - { - case CONTROLLER_CCISS: -#ifdef HAVE_DEV_CISS_CISSIO_H - // check that "file descriptor" is valid - if (isnotopen(&dev_fd,&fdchan)) - return -ENOTTY; - return cciss_io_interface(fdchan->device, m_controller_port-1, iop, report); -#else - { - static int warned = 0; - if (!warned) { - pout("CCISS support is not available in this build of smartmontools,\n" - "/usr/src/sys/dev/ciss/cissio.h was not available at build time.\n\n"); - warned = 1; - } - } - return -ENOSYS; -#endif - // not reached - break; - default: - return do_normal_scsi_cmnd_io(dev_fd, iop, report); - // not reached - break; - } + set_disknum(disknum); + set_encnum(encnum); + set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); } -bool freebsd_scsi_device::scsi_pass_through(scsi_cmnd_io * iop) +smart_device * freebsd_areca_scsi_device::autodetect_open() { - unsigned char oldtype = m_controller_type, oldport = m_controller_port; - m_controller_type = CONTROLLER_SCSI; m_controller_port = 0; - int status = do_scsi_cmnd_io(get_fd(), iop, con->reportscsiioctl); - m_controller_type = oldtype; m_controller_port = oldport; - if (status < 0) { - set_err(-status); - return false; + return this; +} + +int freebsd_areca_scsi_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop) +{ + int ioctlreturn = 0; + + if(!is_open()) { + if(!open()){ + } + } + ioctlreturn = ioctl(get_fd(), ((sSRB_BUFFER *)(iop->dxferp))->srbioctl.ControlCode, iop->dxferp); + if (ioctlreturn) + { + // errors found + return -1; } + + return ioctlreturn; +} + +bool freebsd_areca_scsi_device::arcmsr_lock() +{ + return true; +} + + +bool freebsd_areca_scsi_device::arcmsr_unlock() +{ return true; } @@ -1468,16 +1234,28 @@ public: freebsd_cciss_device(smart_interface * intf, const char * name, unsigned char disknum); virtual bool scsi_pass_through(scsi_cmnd_io * iop); + virtual bool open(); private: unsigned char m_disknum; ///< Disk number. }; +bool freebsd_cciss_device::open() +{ + const char *dev = get_dev_name(); + int fd; + if ((fd = ::open(dev,O_RDWR))<0) { + set_err(errno); + return false; + } + set_fd(fd); + return true; +} freebsd_cciss_device::freebsd_cciss_device(smart_interface * intf, const char * dev_name, unsigned char disknum) : smart_device(intf, dev_name, "cciss", "cciss"), - freebsd_smart_device("SCSI"), + freebsd_smart_device(), m_disknum(disknum) { set_info().info_name = strprintf("%s [cciss_disk_%02d]", dev_name, disknum); @@ -1485,15 +1263,11 @@ freebsd_cciss_device::freebsd_cciss_device(smart_interface * intf, bool freebsd_cciss_device::scsi_pass_through(scsi_cmnd_io * iop) { - // See os_linux.cpp - unsigned char oldtype = m_controller_type, oldport = m_controller_port; - m_controller_type = CONTROLLER_CCISS; m_controller_port = m_disknum+1; - int status = do_scsi_cmnd_io(get_fd(), iop, con->reportscsiioctl); - m_controller_type = oldtype; m_controller_port = oldport; - if (status < 0) { - set_err(-status); - return false; - } + int status = cciss_io_interface(get_fd(), m_disknum, iop, scsi_debugmode); + if (status < 0) + return set_err(-status); + return true; + // not reached return true; } @@ -1531,33 +1305,31 @@ smart_device * freebsd_scsi_device::autodetect_open() int avail_len = req_buff[4] + 5; int len = (avail_len < req_len ? avail_len : req_len); if (len < 36) - return this; + return this; // Use INQUIRY to detect type - smart_device * newdev = 0; - try { - // 3ware ? - if (!memcmp(req_buff + 8, "3ware", 5) || !memcmp(req_buff + 8, "AMCC", 4)) { - close(); -#if defined(_WIN32) || defined(__CYGWIN__) - set_err(EINVAL, "AMCC/3ware controller, please try changing device to %s,N", get_dev_name()); -#else - set_err(EINVAL, "AMCC/3ware controller, please try adding '-d 3ware,N',\n" - "you may need to replace %s with /dev/twaN or /dev/tweN", get_dev_name()); -#endif - return this; - } - // SAT or USB ? - newdev = smi()->autodetect_sat_device(this, req_buff, len); - if (newdev) + // 3ware ? + if (!memcmp(req_buff + 8, "3ware", 5) || !memcmp(req_buff + 8, "AMCC", 4) || + !strcmp("tws",m_camdev->sim_name) || !strcmp("twa",m_camdev->sim_name)) { + close(); + set_err(EINVAL, "3ware/LSI controller, please try adding '-d 3ware,N',\n" + "you may need to replace %s with /dev/twaN, /dev/tweN or /dev/twsN", get_dev_name()); + return this; + } + + // SAT or USB, skip MFI controllers because of bugs + { + smart_device * newdev = smi()->autodetect_sat_device(this, req_buff, len); + if (newdev) { // NOTE: 'this' is now owned by '*newdev' + if(!strcmp("mfi",m_camdev->sim_name)) { + newdev->close(); + newdev->set_err(ENOSYS, "SATA device detected,\n" + "MegaRAID SAT layer is reportedly buggy, use '-d sat' to try anyhow"); + } return newdev; - } - catch (...) { - // Cleanup if exception occurs after newdev was allocated - delete newdev; - throw; + } } // Nothing special found @@ -1572,9 +1344,9 @@ class freebsd_smart_interface : public /*implements*/ smart_interface { public: - virtual const char * get_os_version_str(); + virtual std::string get_os_version_str(); - virtual const char * get_app_examples(const char * appname); + virtual std::string get_app_examples(const char * appname); virtual bool scan_smart_devices(smart_device_list & devlist, const char * type, const char * pattern = 0); @@ -1582,32 +1354,34 @@ public: protected: virtual ata_device * get_ata_device(const char * name, const char * type); +#if FREEBSDVER > 800100 + virtual ata_device * get_atacam_device(const char * name, const char * type); +#endif + virtual scsi_device * get_scsi_device(const char * name, const char * type); virtual smart_device * autodetect_smart_device(const char * name); virtual smart_device * get_custom_smart_device(const char * name, const char * type); - virtual const char * get_valid_custom_dev_types_str(); + virtual std::string get_valid_custom_dev_types_str(); }; ////////////////////////////////////////////////////////////////////// -char sysname[256]; -const char * freebsd_smart_interface::get_os_version_str() + +std::string freebsd_smart_interface::get_os_version_str() { - struct utsname osname; - uname(&osname); - snprintf(sysname, sizeof(sysname),"%s %s %s",osname.sysname, osname.release, - osname.machine); - return sysname; + struct utsname osname; + uname(&osname); + return strprintf("%s %s %s", osname.sysname, osname.release, osname.machine); } -const char * freebsd_smart_interface::get_app_examples(const char * appname) +std::string freebsd_smart_interface::get_app_examples(const char * appname) { if (!strcmp(appname, "smartctl")) return smartctl_examples; - return 0; + return ""; } ata_device * freebsd_smart_interface::get_ata_device(const char * name, const char * type) @@ -1615,152 +1389,45 @@ ata_device * freebsd_smart_interface::get_ata_device(const char * name, const ch return new freebsd_ata_device(this, name, type); } -scsi_device * freebsd_smart_interface::get_scsi_device(const char * name, const char * type) +#if FREEBSDVER > 800100 +ata_device * freebsd_smart_interface::get_atacam_device(const char * name, const char * type) { - return new freebsd_scsi_device(this, name, type); + return new freebsd_atacam_device(this, name, type); } +#endif -static int -cam_getumassno(char * devname) { - union ccb ccb; - int bufsize, fd; - unsigned int i; - int error = -1; - char devstring[256]; - - if ((fd = open(XPT_DEVICE, O_RDWR)) == -1) { - warn("couldn't open %s", XPT_DEVICE); - return(1); - } - bzero(&ccb, sizeof(union ccb)); - - ccb.ccb_h.path_id = CAM_XPT_PATH_ID; - ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; - ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; - - 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); - if (ccb.cdm.matches == NULL) { - warnx("can't malloc memory for matches"); - close(fd); - return(1); - } - ccb.cdm.num_matches = 0; - /* - * We fetch all nodes, since we display most of them in the default - * case, and all in the verbose case. - */ - ccb.cdm.num_patterns = 0; - ccb.cdm.pattern_buf_len = 0; - /* - * We do the ioctl multiple times if necessary, in case there are - * more than 100 nodes in the EDT. - */ - - do { - if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) { - warn("error sending CAMIOCOMMAND ioctl"); - error = -1; - break; - } - if ((ccb.ccb_h.status != CAM_REQ_CMP) - || ((ccb.cdm.status != CAM_DEV_MATCH_LAST) - && (ccb.cdm.status != CAM_DEV_MATCH_MORE))) { - warnx("got CAM error %#x, CDM error %d\n", - ccb.ccb_h.status, ccb.cdm.status); - error = -1; - break; - } - - struct bus_match_result *bus_result = 0; - for (i = 0; i < ccb.cdm.num_matches; i++) { - switch (ccb.cdm.matches[i].type) { - case DEV_MATCH_BUS: { - // struct bus_match_result *bus_result; - bus_result = - &ccb.cdm.matches[i].result.bus_result; - break; - } - case DEV_MATCH_DEVICE: { - /* we are not interested in device name */ - break; - } - case DEV_MATCH_PERIPH: { - struct periph_match_result *periph_result; - - periph_result = - &ccb.cdm.matches[i].result.periph_result; - - snprintf(devstring,sizeof(devstring),"%s%d",periph_result->periph_name,periph_result->unit_number); - if(strcmp(devstring,devname)==0){ /* found our device */ - if(strcmp(bus_result->dev_name,"umass-sim")) { - close(fd); - return -1; /* non usb device found, giving up */ - } - /* return bus number */ - return bus_result->unit_number; - } - break; - } - - default: - fprintf(stdout, "WARN: unknown match type\n"); - break; - } - } - - } while ((ccb.ccb_h.status == CAM_REQ_CMP) - && (ccb.cdm.status == CAM_DEV_MATCH_MORE)); - close(fd); - free(ccb.cdm.matches); - return(error); /* no device found */ +scsi_device * freebsd_smart_interface::get_scsi_device(const char * name, const char * type) +{ + return new freebsd_scsi_device(this, name, type); } - -// we are using CAM subsystem XPT enumerator to found all SCSI devices on system -// despite of it's names +// we are using CAM subsystem XPT enumerator to found all CAM (scsi/usb/ada/...) +// devices on system despite of it's names // // If any errors occur, leave errno set as it was returned by the // system call, and return <0. // +// arguments: +// names: resulting array +// show_all - export duplicate device name or not +// // Return values: // -1: error // >=0: number of discovered devices -int get_dev_names_scsi(char*** names) { - int n = 0; - char** mp = NULL; - unsigned int i; - union ccb ccb; - int bufsize, fd = -1; - int skip_device = 0, skip_bus = 0, changed = 0; - char *devname = NULL; - int serrno=-1; - - // in case of non-clean exit - *names=NULL; - ccb.cdm.matches = NULL; - +bool get_dev_names_cam(std::vector & names, bool show_all) +{ + int fd; if ((fd = open(XPT_DEVICE, O_RDWR)) == -1) { if (errno == ENOENT) /* There are no CAM device on this computer */ return 0; - serrno = errno; + int serrno = errno; pout("%s control device couldn't opened: %s\n", XPT_DEVICE, strerror(errno)); - n = -1; - goto end; + errno = serrno; + return false; } - // allocate space for up to MAX_NUM_DEV number of ATA devices - mp = (char **)calloc(MAX_NUM_DEV, sizeof(char*)); - if (mp == NULL) { - serrno=errno; - pout("Out of memory constructing scan device list (on line %d)\n", __LINE__); - n = -1; - goto end; - }; - + union ccb ccb; bzero(&ccb, sizeof(union ccb)); ccb.ccb_h.path_id = CAM_XPT_PATH_ID; @@ -1768,17 +1435,17 @@ int get_dev_names_scsi(char*** names) { ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; ccb.ccb_h.func_code = XPT_DEV_MATCH; - bufsize = sizeof(struct dev_match_result) * MAX_NUM_DEV; + int bufsize = sizeof(struct dev_match_result) * MAX_NUM_DEV; ccb.cdm.match_buf_len = bufsize; + // TODO: Use local buffer instead of malloc() if possible ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize); + bzero(ccb.cdm.matches,bufsize); // clear ccb.cdm.matches structure + if (ccb.cdm.matches == NULL) { - serrno = errno; - pout("can't malloc memory for matches on line %d\n", __LINE__); - n = -1; - goto end; + close(fd); + throw std::bad_alloc(); } ccb.cdm.num_matches = 0; - ccb.cdm.num_patterns = 0; ccb.cdm.pattern_buf_len = 0; @@ -1786,24 +1453,29 @@ int get_dev_names_scsi(char*** names) { * We do the ioctl multiple times if necessary, in case there are * more than MAX_NUM_DEV nodes in the EDT. */ + int skip_device = 0, skip_bus = 0, changed = 0; // TODO: bool + std::string devname; do { if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) { - serrno = errno; + int serrno = errno; pout("error sending CAMIOCOMMAND ioctl: %s\n", strerror(errno)); - n = -1; - break; + free(ccb.cdm.matches); + close(fd); + errno = serrno; + return false; } if ((ccb.ccb_h.status != CAM_REQ_CMP) - || ((ccb.cdm.status != CAM_DEV_MATCH_LAST) - && (ccb.cdm.status != CAM_DEV_MATCH_MORE))) { + || ((ccb.cdm.status != CAM_DEV_MATCH_LAST) + && (ccb.cdm.status != CAM_DEV_MATCH_MORE))) { pout("got CAM error %#x, CDM error %d\n", ccb.ccb_h.status, ccb.cdm.status); - serrno = ENXIO; - n = -1; - goto end; + free(ccb.cdm.matches); + close(fd); + errno = ENXIO; + return false; } - for (i = 0; i < ccb.cdm.num_matches && n < MAX_NUM_DEV; i++) { + for (unsigned i = 0; i < ccb.cdm.num_matches; i++) { struct bus_match_result *bus_result; struct device_match_result *dev_result; struct periph_match_result *periph_result; @@ -1811,9 +1483,8 @@ int get_dev_names_scsi(char*** names) { if (ccb.cdm.matches[i].type == DEV_MATCH_BUS) { bus_result = &ccb.cdm.matches[i].result.bus_result; - if (strcmp(bus_result->dev_name,"ata") == 0 /* ATAPICAM devices will be probed as ATA devices, skip'em there */ - || strcmp(bus_result->dev_name,"xpt") == 0) /* skip XPT bus at all */ - skip_bus = 1; + if (strcmp(bus_result->dev_name,"xpt") == 0) /* skip XPT bus at all */ + skip_bus = 1; else skip_bus = 0; changed = 1; @@ -1824,62 +1495,38 @@ int get_dev_names_scsi(char*** names) { skip_device = 1; else skip_device = 0; - -// /* Shall we skip non T_DIRECT devices ? */ -// if (dev_result->inq_data.device != T_DIRECT) -// skip_device = 1; + + // /* Shall we skip non T_DIRECT devices ? */ + // if (dev_result->inq_data.device != T_DIRECT) + // skip_device = 1; changed = 1; - } else if (ccb.cdm.matches[i].type == DEV_MATCH_PERIPH && skip_device == 0) { + } else if (ccb.cdm.matches[i].type == DEV_MATCH_PERIPH && + (skip_device == 0 || show_all)) { /* One device may be populated as many peripherals (pass0 & da0 for example). - * We are searching for latest name + * We are searching for best name */ periph_result = &ccb.cdm.matches[i].result.periph_result; - free(devname); - asprintf(&devname, "%s%s%d", _PATH_DEV, periph_result->periph_name, periph_result->unit_number); - if (devname == NULL) { - serrno=errno; - pout("Out of memory constructing scan SCSI device list (on line %d)\n", __LINE__); - n = -1; - goto end; - }; + /* Prefer non-"pass" names */ + if (devname.empty() || strncmp(periph_result->periph_name, "pass", 4) != 0) { + devname = strprintf("%s%s%d", _PATH_DEV, periph_result->periph_name, periph_result->unit_number); + } changed = 0; }; - - if (changed == 1 && devname != NULL) { - mp[n] = devname; - devname = NULL; - bytes+=1+strlen(mp[n]); - n++; + if ((changed == 1 || show_all) && !devname.empty()) { + names.push_back(devname); + devname.erase(); changed = 0; }; } - } while ((ccb.ccb_h.status == CAM_REQ_CMP) && (ccb.cdm.status == CAM_DEV_MATCH_MORE) && n < MAX_NUM_DEV); - - if (devname != NULL) { - mp[n] = devname; - devname = NULL; - bytes+=1+strlen(mp[n]); - n++; - }; + } while ((ccb.ccb_h.status == CAM_REQ_CMP) && (ccb.cdm.status == CAM_DEV_MATCH_MORE)); - mp = (char **)reallocf(mp,n*(sizeof (char*))); // shrink to correct size - bytes += (n)*(sizeof(char*)); // and set allocated byte count + if (!devname.empty()) + names.push_back(devname); -end: free(ccb.cdm.matches); - if (fd>-1) - close(fd); - if (n <= 0) { - free(mp); - mp = NULL; - } - - *names=mp; - - if (serrno>-1) - errno=serrno; - return(n); + close(fd); + return true; } // we are using ATA subsystem enumerator to found all ATA devices on system @@ -1906,7 +1553,7 @@ int get_dev_names_ata(char*** names) { n = -1; goto end; }; - + if (ioctl(fd, IOCATAGMAXCHANNEL, &maxchannel) < 0) { serrno = errno; pout("ioctl(IOCATAGMAXCHANNEL) on /dev/ata failed: %s\n", strerror(errno)); @@ -1925,7 +1572,7 @@ int get_dev_names_ata(char*** names) { for (devices.channel = 0; devices.channel < maxchannel && n < MAX_NUM_DEV; devices.channel++) { int j; - + if (ioctl(fd, IOCATADEVICES, &devices) < 0) { if (errno == ENXIO) continue; /* such channel not exist */ @@ -1947,8 +1594,14 @@ int get_dev_names_ata(char*** names) { }; }; mp = (char **)reallocf(mp,n*(sizeof (char*))); // shrink to correct size + if (mp == NULL && n > 0 ) { // reallocf never fail for size=0, but may return NULL + serrno=errno; + pout("Out of memory constructing scan device list (on line %d)\n", __LINE__); + n = -1; + goto end; + }; bytes += (n)*(sizeof(char*)); // and set allocated byte count - + end: if (fd>=0) close(fd); @@ -1984,11 +1637,10 @@ bool freebsd_smart_interface::scan_smart_devices(smart_device_list & devlist, } } - char * * scsinames = 0; int numscsi = 0; - if (!type || !strcmp(type, "scsi")) { - numscsi = get_dev_names_scsi(&scsinames); - if (numscsi < 0) { - set_err(ENOMEM); + std::vector scsinames; + if (!type || !strcmp(type, "scsi")) { // do not export duplicated names + if (!get_dev_names_cam(scsinames, false)) { + set_err(errno); return false; } } @@ -2000,148 +1652,265 @@ bool freebsd_smart_interface::scan_smart_devices(smart_device_list & devlist, for (i = 0; i < numata; i++) { ata_device * atadev = get_ata_device(atanames[i], type); if (atadev) - devlist.add(atadev); + devlist.push_back(atadev); + free(atanames[i]); } + if(numata) free(atanames); - for (i = 0; i < numscsi; i++) { - scsi_device * scsidev = get_scsi_device(scsinames[i], type); - if (scsidev) - devlist.add(scsidev); + for (i = 0; i < (int)scsinames.size(); i++) { + if(!*type) { // try USB autodetection if no type specified + smart_device * smartdev = autodetect_smart_device(scsinames[i].c_str()); + if(smartdev) + devlist.push_back(smartdev); + } + else { + scsi_device * scsidev = get_scsi_device(scsinames[i].c_str(), type); + if (scsidev) + devlist.push_back(scsidev); + } } return true; } +#if (FREEBSDVER < 800000) // without this build fail on FreeBSD 8 static char done[USB_MAX_DEVICES]; -// static unsigned short vendor_id = 0, product_id = 0, version = 0; static int usbdevinfo(int f, int a, int rec, int busno, unsigned short & vendor_id, - unsigned short & product_id, unsigned short & version) -{ - struct usb_device_info di; - int e, p, i; - char devname[256]; - - snprintf(devname, sizeof(devname),"umass%d",busno); - - di.udi_addr = a; - e = ioctl(f, USB_DEVICEINFO, &di); - if (e) { - if (errno != ENXIO) - printf("addr %d: I/O error\n", a); - return 0; - } - done[a] = 1; - - // list devices - for (i = 0; i < USB_MAX_DEVNAMES; i++) { - if (di.udi_devnames[i][0]) { - if(strcmp(di.udi_devnames[i],devname)==0) { - // device found! - vendor_id = di.udi_vendorNo; - product_id = di.udi_productNo; - version = di.udi_releaseNo; - return 1; - // FIXME - } - } - } - if (!rec) - return 0; - for (p = 0; p < di.udi_nports; p++) { - int s = di.udi_ports[p]; - if (s >= USB_MAX_DEVICES) { - continue; - } - if (s == 0) - printf("addr 0 should never happen!\n"); - else { - if(usbdevinfo(f, s, 1, busno, vendor_id, product_id, version)) return 1; - } - } - return 0; -} - + unsigned short & product_id, unsigned short & version) +{ + struct usb_device_info di; + int e, p, i; + char devname[256]; + snprintf(devname, sizeof(devname),"umass%d",busno); -static int usbdevlist(int busno,unsigned short & vendor_id, - unsigned short & product_id, unsigned short & version) -{ - int i, f, a, rc; - char buf[50]; - int ncont; - - for (ncont = 0, i = 0; i < 10; i++) { - snprintf(buf, sizeof(buf), "%s%d", USBDEV, i); - f = open(buf, O_RDONLY); - if (f >= 0) { - memset(done, 0, sizeof done); - for (a = 1; a < USB_MAX_DEVICES; a++) { - if (!done[a]) { - rc = usbdevinfo(f, a, 1, busno,vendor_id, product_id, version); - if(rc) return 1; - } - - } - close(f); - } else { - if (errno == ENOENT || errno == ENXIO) - continue; - warn("%s", buf); - } - ncont++; + di.udi_addr = a; + e = ioctl(f, USB_DEVICEINFO, &di); + if (e) { + if (errno != ENXIO) + printf("addr %d: I/O error\n", a); + return 0; + } + done[a] = 1; + + // list devices + for (i = 0; i < USB_MAX_DEVNAMES; i++) { + if (di.udi_devnames[i][0]) { + if(strcmp(di.udi_devnames[i],devname)==0) { + // device found! + vendor_id = di.udi_vendorNo; + product_id = di.udi_productNo; + version = di.udi_releaseNo; + return 1; + // FIXME + } } + } + if (!rec) return 0; + for (p = 0; p < di.udi_nports; p++) { + int s = di.udi_ports[p]; + if (s >= USB_MAX_DEVICES) { + continue; + } + if (s == 0) + printf("addr 0 should never happen!\n"); + else { + if(usbdevinfo(f, s, 1, busno, vendor_id, product_id, version)) return 1; + } + } + return 0; } +#endif -// Get USB bridge ID for "/dev/daX" -static bool get_usb_id(const char * path, unsigned short & vendor_id, - unsigned short & product_id, unsigned short & version) + +static int usbdevlist(int busno,unsigned short & vendor_id, + unsigned short & product_id, unsigned short & version) { - // Only "/dev/daX" supported - if (!(!strncmp(path, "/dev/da", 7) && !strchr(path + 7, '/'))) - return false; - int bus = cam_getumassno((char *)path+5); - - if (bus == -1) - return false; +#if (FREEBSDVER >= 800000) // libusb2 interface + struct libusb20_device *pdev = NULL; + struct libusb20_backend *pbe; + uint32_t matches = 0; + char buf[128]; // do not change! + char devname[128]; + uint8_t n; + struct LIBUSB20_DEVICE_DESC_DECODED *pdesc; + + pbe = libusb20_be_alloc_default(); + + while ((pdev = libusb20_be_device_foreach(pbe, pdev))) { + matches++; + + if (libusb20_dev_open(pdev, 0)) { + warnx("libusb20_dev_open: could not open device"); + return 0; + } - usbdevlist(bus,vendor_id, - product_id, version); + pdesc=libusb20_dev_get_device_desc(pdev); + + snprintf(devname, sizeof(devname),"umass%d:",busno); + for (n = 0; n != 255; n++) { + if (libusb20_dev_get_iface_desc(pdev, n, buf, sizeof(buf))) + break; + if (buf[0] == 0) + continue; + if(strncmp(buf,devname,strlen(devname))==0){ + // found! + vendor_id = pdesc->idVendor; + product_id = pdesc->idProduct; + version = pdesc->bcdDevice; + libusb20_dev_close(pdev); + libusb20_be_free(pbe); + return 1; + } + } - return true; -} + libusb20_dev_close(pdev); + } + + if (matches == 0) { + printf("No device match or lack of permissions.\n"); + } + + libusb20_be_free(pbe); + return false; +#else // freebsd < 8.0 USB stack, ioctl interface + + int i, f, a, rc; + char buf[50]; + int ncont; + + for (ncont = 0, i = 0; i < 10; i++) { + snprintf(buf, sizeof(buf), "%s%d", USBDEV, i); + f = open(buf, O_RDONLY); + if (f >= 0) { + memset(done, 0, sizeof done); + for (a = 1; a < USB_MAX_DEVICES; a++) { + if (!done[a]) { + rc = usbdevinfo(f, a, 1, busno,vendor_id, product_id, version); + if(rc) return 1; + } + + } + close(f); + } else { + if (errno == ENOENT || errno == ENXIO) + continue; + warn("%s", buf); + } + ncont++; + } + return 0; +#endif +} smart_device * freebsd_smart_interface::autodetect_smart_device(const char * name) { - int guess = parse_ata_chan_dev(name,NULL); unsigned short vendor_id = 0, product_id = 0, version = 0; - - switch (guess) { - case CONTROLLER_ATA : - return new freebsd_ata_device(this, name, ""); - case CONTROLLER_SCSI: - // Try to detect possible USB->(S)ATA bridge - if (get_usb_id(name, vendor_id, product_id, version)) { - const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id, version); - if (!usbtype) - return 0; - // Return SAT/USB device for this type - // (Note: freebsd_scsi_device::autodetect_open() will not be called in this case) - return get_sat_device(usbtype, new freebsd_scsi_device(this, name, "")); - } - // non usb device, handle as normal scsi - return new freebsd_scsi_device(this, name, ""); - case CONTROLLER_CCISS: - // device - cciss, but no ID known - set_err(EINVAL, "Option -d cciss,N requires N to be a non-negative integer"); + struct cam_device *cam_dev; + union ccb ccb; + int bus=-1; + int i,c; + int len; + const char * test_name = name; + + // if dev_name null, or string length zero + if (!name || !(len = strlen(name))) return 0; + + // Dereference symlinks + struct stat st; + std::string pathbuf; + if (!lstat(name, &st) && S_ISLNK(st.st_mode)) { + char * p = realpath(name, (char *)0); + if (p) { + pathbuf = p; + free(p); + test_name = pathbuf.c_str(); + } } - - - // TODO: Test autodetect device here + + // check ATA bus + char * * atanames = 0; int numata = 0; + numata = get_dev_names_ata(&atanames); + if (numata > 0) { + // check ATA/ATAPI devices + for (i = 0; i < numata; i++) { + if(!strcmp(atanames[i],test_name)) { + for (c = i; c < numata; c++) free(atanames[c]); + free(atanames); + return new freebsd_ata_device(this, test_name, ""); + } + else free(atanames[i]); + } + if(numata) free(atanames); + } + else { + if (numata < 0) + pout("Unable to get ATA device list\n"); + } + + // check CAM + std::vector scsinames; + if (!get_dev_names_cam(scsinames, true)) + pout("Unable to get CAM device list\n"); + else if (!scsinames.empty()) { + // check all devices on CAM bus + for (i = 0; i < (int)scsinames.size(); i++) { + if(strcmp(scsinames[i].c_str(), test_name)==0) + { // our disk device is CAM + if(strncmp(scsinames[i].c_str(), "/dev/pmp", strlen("/dev/pmp")) == 0) { + pout("Skipping port multiplier [%s]\n", scsinames[i].c_str()); + set_err(EINVAL); + return 0; + } + if ((cam_dev = cam_open_device(test_name, O_RDWR)) == NULL) { + // open failure + set_err(errno); + return 0; + } + // zero the payload + bzero(&(&ccb.ccb_h)[1], PATHINQ_SETTINGS_SIZE); + ccb.ccb_h.func_code = XPT_PATH_INQ; // send PATH_INQ to the device + if (ioctl(cam_dev->fd, CAMIOCOMMAND, &ccb) == -1) { + warn("Get Transfer Settings CCB failed\n" + "%s", strerror(errno)); + cam_close_device(cam_dev); + return 0; + } + // now check if we are working with USB device, see umass.c + if(strcmp(ccb.cpi.dev_name,"umass-sim") == 0) { // USB device found + usbdevlist(bus,vendor_id, product_id, version); + int bus=ccb.cpi.unit_number; // unit_number will match umass number + cam_close_device(cam_dev); + if(usbdevlist(bus,vendor_id, product_id, version)){ + const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id, version); + if (usbtype) + return get_sat_device(usbtype, new freebsd_scsi_device(this, test_name, "")); + } + return 0; + } +#if FREEBSDVER > 800100 + // check if we have ATA device connected to CAM (ada) + if(ccb.cpi.protocol == PROTO_ATA){ + cam_close_device(cam_dev); + return new freebsd_atacam_device(this, test_name, ""); + } +#endif + // close cam device, we don`t need it anymore + cam_close_device(cam_dev); + // handle as usual scsi + return new freebsd_scsi_device(this, test_name, ""); + } + } + } + // device is LSI raid supported by mfi driver + if(!strncmp("/dev/mfid", test_name, strlen("/dev/mfid"))) + set_err(EINVAL, "To monitor disks on LSI RAID load mfip.ko module and run 'smartctl -a /dev/passX' to show SMART information"); + // device type unknown return 0; } @@ -2149,7 +1918,11 @@ smart_device * freebsd_smart_interface::autodetect_smart_device(const char * nam smart_device * freebsd_smart_interface::get_custom_smart_device(const char * name, const char * type) { // 3Ware ? - int disknum = -1, n1 = -1, n2 = -1; + static const char * fbsd_dev_twe_ctrl = "/dev/twe"; + static const char * fbsd_dev_twa_ctrl = "/dev/twa"; + static const char * fbsd_dev_tws_ctrl = "/dev/tws"; + int disknum = -1, n1 = -1, n2 = -1, contr = -1; + if (sscanf(type, "3ware,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) { if (n2 != (int)strlen(type)) { set_err(EINVAL, "Option -d 3ware,N requires N to be a non-negative integer"); @@ -2159,11 +1932,23 @@ smart_device * freebsd_smart_interface::get_custom_smart_device(const char * nam set_err(EINVAL, "Option -d 3ware,N (N=%d) must have 0 <= N <= 127", disknum); return 0; } - int contr = parse_ata_chan_dev(name,NULL); - if (contr != CONTROLLER_3WARE_9000_CHAR && contr != CONTROLLER_3WARE_678K_CHAR) - contr = CONTROLLER_3WARE_678K; - return new freebsd_escalade_device(this, name, contr, disknum); - } + + // guess 3ware device type based on device name + if (str_starts_with(name, fbsd_dev_twa_ctrl) || + str_starts_with(name, fbsd_dev_tws_ctrl) ) { + contr=CONTROLLER_3WARE_9000_CHAR; + } + if (!strncmp(fbsd_dev_twe_ctrl, name, strlen(fbsd_dev_twe_ctrl))){ + contr=CONTROLLER_3WARE_678K_CHAR; + } + + if(contr == -1){ + set_err(EINVAL, "3ware controller type unknown, use %sX, %sX or %sX devices", + fbsd_dev_twe_ctrl, fbsd_dev_twa_ctrl, fbsd_dev_tws_ctrl); + return 0; + } + return new freebsd_escalade_device(this, name, contr, disknum); + } // Highpoint ? int controller = -1, channel = -1; disknum = 1; @@ -2178,7 +1963,7 @@ smart_device * freebsd_smart_interface::get_custom_smart_device(const char * nam set_err(EINVAL, "Option '-d hpt,L/M/N' invalid controller id L supplied"); return 0; } - if (!(1 <= channel && channel <= 8)) { + if (!(1 <= channel && channel <= 128)) { set_err(EINVAL, "Option '-d hpt,L/M/N' invalid channel number M supplied"); return 0; } @@ -2196,25 +1981,46 @@ smart_device * freebsd_smart_interface::get_custom_smart_device(const char * nam set_err(EINVAL, "Option -d cciss,N requires N to be a non-negative integer"); return 0; } - if (!(0 <= disknum && disknum <= 15)) { - set_err(EINVAL, "Option -d cciss,N (N=%d) must have 0 <= N <= 15", disknum); + if (!(0 <= disknum && disknum <= 127)) { + set_err(EINVAL, "Option -d cciss,N (N=%d) must have 0 <= N <= 127", disknum); + return 0; + } + return get_sat_device("sat,auto", new freebsd_cciss_device(this, name, disknum)); + } +#if FREEBSDVER > 800100 + // adaX devices ? + if(!strcmp(type,"atacam")) + return new freebsd_atacam_device(this, name, ""); +#endif + // Areca? + disknum = n1 = n2 = -1; + int encnum = 1; + if (sscanf(type, "areca,%n%d/%d%n", &n1, &disknum, &encnum, &n2) >= 1 || n1 == 6) { + if (!(1 <= disknum && disknum <= 128)) { + set_err(EINVAL, "Option -d areca,N/E (N=%d) must have 1 <= N <= 128", disknum); + return 0; + } + if (!(1 <= encnum && encnum <= 8)) { + set_err(EINVAL, "Option -d areca,N/E (E=%d) must have 1 <= E <= 8", encnum); return 0; } - return new freebsd_cciss_device(this, name, disknum); + return new freebsd_areca_ata_device(this, name, disknum, encnum); } return 0; } -const char * freebsd_smart_interface::get_valid_custom_dev_types_str() +std::string freebsd_smart_interface::get_valid_custom_dev_types_str() { - return "marvell, 3ware,N, hpt,L/M/N, cciss,N"; + return "3ware,N, hpt,L/M/N, cciss,N, areca,N/E" +#if FREEBSDVER > 800100 + ", atacam" +#endif + ; } - } // namespace - ///////////////////////////////////////////////////////////////////////////// /// Initialize platform interface and register with smi()