X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=os_freebsd.cpp;h=5038b1219ecc52e6aff98c95e677ec81cf308586;hb=f55b7a7b37eda529795f2a8207ef78a8168f7721;hp=0f92fc0f023ce0996f769ee15a3006694d4bc36d;hpb=eb07ddf29ca46e50c84392a66ce0e40a189b375c;p=mirror_smartmontools-debian.git diff --git a/os_freebsd.cpp b/os_freebsd.cpp index 0f92fc0..5038b12 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 @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -41,7 +42,6 @@ #include "scsicmds.h" #include "cciss.h" #include "utility.h" -#include "extern.h" #include "os_freebsd.h" #include "dev_interface.h" @@ -59,6 +59,9 @@ #if (FREEBSDVER >= 800000) #include #include +#elif defined(__DragonFly__) +#include +#include #else #include #include @@ -71,13 +74,9 @@ #define PATHINQ_SETTINGS_SIZE 128 #endif -static __unused const char *filenameandversion="$Id: os_freebsd.cpp 2955 2009-10-10 12:34:08Z samm2 $"; - -const char *os_XXXX_c_cvsid="$Id: os_freebsd.cpp 2955 2009-10-10 12:34:08Z samm2 $" \ +const char *os_XXXX_c_cvsid="$Id: os_freebsd.cpp 3525 2012-03-22 08:54:52Z samm2 $" \ 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; - #define NO_RETURN 0 #define BAD_SMART 1 #define NO_DISK_3WARE 2 @@ -118,14 +117,11 @@ void printwarning(int msgNo, const char* extra) { #define ATA_DEVICE "/dev/ata" #endif +#define ARGUSED(x) ((void)(x)) + // global variable holding byte count of allocated memory long long bytes; -const char * dev_freebsd_cpp_cvsid = "$Id: os_freebsd.cpp 2955 2009-10-10 12:34:08Z samm2 $" - DEV_INTERFACE_H_CVSID; - -extern smartmonctrl * con; // con->reportscsiioctl - ///////////////////////////////////////////////////////////////////////////// namespace os_freebsd { // No need to publish anything, name provided for Doxygen @@ -165,7 +161,7 @@ private: #ifdef __GLIBC__ static inline void * reallocf(void *ptr, size_t size) { void *rv = realloc(ptr, size); - if(rv == NULL) + if((rv == NULL) && (size != 0)) free(ptr); return rv; } @@ -198,6 +194,9 @@ static const char smartctl_examples[] = " 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,1 /dev/arcmsr0\n" + " (Prints all SMART information for first disk \n" + " on first ARECA RAID controller)\n" ; @@ -231,18 +230,18 @@ bool freebsd_smart_device::close() } ///////////////////////////////////////////////////////////////////////////// -/// Implement standard ATA support with old functions +/// Implement standard ATA support class freebsd_ata_device -: public /*implements*/ ata_device_with_command_set, +: 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); protected: - virtual int ata_command_interface(smart_command_set command, int select, char * data); - virtual int do_cmd(struct ata_ioc_request* request); + virtual int do_cmd(struct ata_ioc_request* request, bool is_48bit_cmd); }; freebsd_ata_device::freebsd_ata_device(smart_interface * intf, const char * dev_name, const char * req_type) @@ -251,12 +250,101 @@ freebsd_ata_device::freebsd_ata_device(smart_interface * intf, const char * dev_ { } -int freebsd_ata_device::do_cmd( struct ata_ioc_request* request) +int freebsd_ata_device::do_cmd( struct ata_ioc_request* request, bool is_48bit_cmd) { int fd = get_fd(); + ARGUSED(is_48bit_cmd); // no support for 48 bit commands in the IOCATAREQUEST return ioctl(fd, IOCATAREQUEST, request); } + + +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) + ) + return false; + + struct ata_ioc_request request; + bzero(&request,sizeof(struct ata_ioc_request)); + + request.timeout=SCSI_TIMEOUT_DEFAULT; + request.u.ata.command=in.in_regs.command; + request.u.ata.feature=in.in_regs.features; + + request.u.ata.count = in.in_regs.sector_count_16; + request.u.ata.lba = in.in_regs.lba_48; + + switch (in.direction) { + case ata_cmd_in::no_data: + request.flags=ATA_CMD_CONTROL; + break; + case ata_cmd_in::data_in: + request.flags=ATA_CMD_READ | ATA_CMD_CONTROL; + request.data=(char *)in.buffer; + request.count=in.size; + break; + case ata_cmd_in::data_out: + request.flags=ATA_CMD_WRITE | ATA_CMD_CONTROL; + request.data=(char *)in.buffer; + request.count=in.size; + break; + default: + return set_err(ENOSYS); + } + + clear_err(); + errno = 0; + if (do_cmd(&request, in.in_regs.is_48bit_cmd())) + return set_err(errno); + if (request.error) + return set_err(EIO, "request failed, error code 0x%02x", request.error); + + 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; + + + // 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; + + // 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]; + 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); + out.out_regs.lba_high = failed_hi; + out.out_regs.lba_mid = failed_lo; + } + } + + return true; +} + #if FREEBSDVER > 800100 class freebsd_atacam_device : public freebsd_ata_device { @@ -272,7 +360,7 @@ protected: int m_fd; struct cam_device *m_camdev; - virtual int do_cmd( struct ata_ioc_request* request); + virtual int do_cmd( struct ata_ioc_request* request , bool is_48bit_cmd); }; bool freebsd_atacam_device::open(){ @@ -292,7 +380,7 @@ bool freebsd_atacam_device::close(){ return true; } -int freebsd_atacam_device::do_cmd( struct ata_ioc_request* request) +int freebsd_atacam_device::do_cmd( struct ata_ioc_request* request, bool is_48bit_cmd) { union ccb ccb; int camflags; @@ -301,10 +389,12 @@ int freebsd_atacam_device::do_cmd( struct ata_ioc_request* request) if (request->count == 0) camflags = CAM_DIR_NONE; - else if (request->flags == ATA_CMD_READ) + else if (request->flags & ATA_CMD_READ) camflags = CAM_DIR_IN; else camflags = CAM_DIR_OUT; + if(is_48bit_cmd) + camflags |= CAM_ATAIO_48BIT; cam_fill_ataio(&ccb.ataio, 0, @@ -315,15 +405,20 @@ int freebsd_atacam_device::do_cmd( struct ata_ioc_request* request) request->count, request->timeout * 1000); // timeout in seconds + ccb.ataio.cmd.flags = CAM_ATAIO_NEEDRESULT; // ata_28bit_cmd - ccb.ataio.cmd.flags = 0; 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; @@ -332,177 +427,27 @@ int freebsd_atacam_device::do_cmd( struct ata_ioc_request* request) return -1; } - if ((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) - return 0; - - cam_error_print(m_camdev, &ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); - return -1; -} + if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + cam_error_print(m_camdev, &ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); + return -1; + } -#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); -int freebsd_ata_device::ata_command_interface(smart_command_set command, int select, char * data) -{ - int retval, copydata=0; - struct ata_ioc_request request; - unsigned char buff[512]; - - bzero(buff,512); - bzero(&request,sizeof(struct ata_ioc_request)); - bzero(buff,512); - - request.u.ata.command=ATA_SMART_CMD; - request.timeout=SCSI_TIMEOUT_DEFAULT; - 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; - - if ((retval=do_cmd(&request)) || request.error) - return -1; - -#if (FREEBSDVER < 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; - } - - if ((retval=do_cmd(&request)) || request.error) - { - return -1; - } - // - if (command == CHECK_POWER_MODE) { - data[0] = request.u.ata.count & 0xff; - return 0; - } - if (copydata) - memcpy(data, buff, 512); + 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 + ///////////////////////////////////////////////////////////////////////////// /// Implement AMCC/3ware RAID support with old functions @@ -780,7 +725,7 @@ int freebsd_escalade_device::ata_command_interface(smart_command_set command, in *data=*(char *)&(ata->sector_count); // look for nonexistent devices/ports - if (command==IDENTIFY && !nonempty((unsigned char *)data, 512)) { + if (command==IDENTIFY && !nonempty(data, 512)) { errno=ENODEV; return -1; } @@ -986,7 +931,7 @@ int freebsd_highpoint_device::ata_command_interface(smart_command_set command, i ///////////////////////////////////////////////////////////////////////////// -/// Implement standard SCSI support with old functions +/// Standard SCSI support class freebsd_scsi_device : public /*implements*/ scsi_device, @@ -1035,7 +980,7 @@ freebsd_scsi_device::freebsd_scsi_device(smart_interface * intf, bool freebsd_scsi_device::scsi_pass_through(scsi_cmnd_io * iop) { - int report=con->reportscsiioctl; + int report=scsi_debugmode; union ccb *ccb; if (report > 0) { @@ -1068,6 +1013,20 @@ bool freebsd_scsi_device::scsi_pass_through(scsi_cmnd_io * iop) warnx("error allocating ccb"); return -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)) + return set_err(ENOSYS, "SMART WRITE LOG SECTOR command is not supported by controller firmware"); + } // clear out structure, except for header that was filled in for us bzero(&(&ccb->ccb_h)[1], @@ -1087,24 +1046,20 @@ bool freebsd_scsi_device::scsi_pass_through(scsi_cmnd_io * iop) if (cam_send_ccb(m_camdev,ccb) < 0) { warn("error sending SCSI ccb"); -#if (FREEBSDVER > 500000) cam_error_print(m_camdev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr); -#endif cam_freeccb(ccb); return -EIO; } if (((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) && ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_SCSI_STATUS_ERROR)) { -#if (FREEBSDVER > 500000) cam_error_print(m_camdev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr); -#endif cam_freeccb(ccb); return -EIO; } if (iop->sensep) { - memcpy(iop->sensep,&(ccb->csio.sense_data),sizeof(struct scsi_sense_data)); - iop->resp_sense_len = sizeof(struct scsi_sense_data); + iop->resp_sense_len = ccb->csio.sense_len - ccb->csio.sense_resid; + memcpy(iop->sensep,&(ccb->csio.sense_data),iop->resp_sense_len); } iop->scsi_status = ccb->csio.scsi_status; @@ -1126,6 +1081,435 @@ bool freebsd_scsi_device::scsi_pass_through(scsi_cmnd_io * iop) } +///////////////////////////////////////////////////////////////////////////// +/// Areca RAID support + +class freebsd_areca_device +: public /*implements*/ ata_device, + public /*extends*/ freebsd_smart_device +{ +public: + freebsd_areca_device(smart_interface * intf, const char * dev_name, int disknum); + +protected: + virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); + +private: + int m_disknum; ///< Disk number. +}; + + +// PURPOSE +// This is an interface routine meant to isolate the OS dependent +// parts of the code, and to provide a debugging interface. Each +// different port and OS needs to provide it's own interface. This +// is the linux interface to the Areca "arcmsr" driver. It allows ATA +// commands to be passed through the SCSI driver. +// DETAILED DESCRIPTION OF ARGUMENTS +// fd: is the file descriptor provided by open() +// disknum is the disk number (0 to 15) in the RAID array +// command: defines the different operations. +// select: additional input data if needed (which log, which type of +// self-test). +// data: location to write output data, if needed (512 bytes). +// Note: not all commands use all arguments. +// RETURN VALUES +// -1 if the command failed +// 0 if the command succeeded, +// STATUS_CHECK routine: +// -1 if the command failed +// 0 if the command succeeded and disk SMART status is "OK" +// 1 if the command succeeded and disk SMART status is "FAILING" + + +/*FunctionCode*/ +#define FUNCTION_READ_RQBUFFER 0x0801 +#define FUNCTION_WRITE_WQBUFFER 0x0802 +#define FUNCTION_CLEAR_RQBUFFER 0x0803 +#define FUNCTION_CLEAR_WQBUFFER 0x0804 + +/* ARECA IO CONTROL CODE*/ +#define ARCMSR_IOCTL_READ_RQBUFFER _IOWR('F', FUNCTION_READ_RQBUFFER, sSRB_BUFFER) +#define ARCMSR_IOCTL_WRITE_WQBUFFER _IOWR('F', FUNCTION_WRITE_WQBUFFER, sSRB_BUFFER) +#define ARCMSR_IOCTL_CLEAR_RQBUFFER _IOWR('F', FUNCTION_CLEAR_RQBUFFER, sSRB_BUFFER) +#define ARCMSR_IOCTL_CLEAR_WQBUFFER _IOWR('F', FUNCTION_CLEAR_WQBUFFER, sSRB_BUFFER) +#define ARECA_SIG_STR "ARCMSR" + + + +// The SRB_IO_CONTROL & SRB_BUFFER structures are used to communicate(to/from) to areca driver +typedef struct _SRB_IO_CONTROL +{ + unsigned int HeaderLength; + unsigned char Signature[8]; + unsigned int Timeout; + unsigned int ControlCode; + unsigned int ReturnCode; + unsigned int Length; +} sSRB_IO_CONTROL; + +typedef struct _SRB_BUFFER +{ + sSRB_IO_CONTROL srbioctl; + unsigned char ioctldatabuffer[1032]; // the buffer to put the command data to/from firmware +} sSRB_BUFFER; + + +// For debugging areca code + +static void areca_dumpdata(unsigned char *block, int len) +{ + int ln = (len / 16) + 1; // total line# + unsigned char c; + int pos = 0; + + printf(" Address = %p, Length = (0x%x)%d\n", block, len, len); + printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F ASCII \n"); + printf("=====================================================================\n"); + + for ( int l = 0; l < ln && len; l++ ) + { + // printf the line# and the HEX data + // if a line data length < 16 then append the space to the tail of line to reach 16 chars + printf("%02X | ", l); + for ( pos = 0; pos < 16 && len; pos++, len-- ) + { + c = block[l*16+pos]; + printf("%02X ", c); + } + + if ( pos < 16 ) + { + for ( int loop = pos; loop < 16; loop++ ) + { + printf(" "); + } + } + + // print ASCII char + for ( int loop = 0; loop < pos; loop++ ) + { + c = block[l*16+loop]; + if ( c >= 0x20 && c <= 0x7F ) + { + printf("%c", c); + } + else + { + printf("."); + } + } + printf("\n"); + } + printf("=====================================================================\n"); +} + + +static int arcmsr_command_handler(int fd, unsigned long arcmsr_cmd, unsigned char *data, int data_len, void *ext_data /* reserved for further use */) +{ + ARGUSED(ext_data); + + int ioctlreturn = 0; + sSRB_BUFFER sBuf; + + unsigned char *areca_return_packet; + int total = 0; + int expected = -1; + unsigned char return_buff[2048]; + unsigned char *ptr = &return_buff[0]; + memset(return_buff, 0, sizeof(return_buff)); + + memset((unsigned char *)&sBuf, 0, sizeof(sBuf)); + + + sBuf.srbioctl.HeaderLength = sizeof(sSRB_IO_CONTROL); + memcpy(sBuf.srbioctl.Signature, ARECA_SIG_STR, strlen(ARECA_SIG_STR)); + sBuf.srbioctl.Timeout = 10000; + sBuf.srbioctl.ControlCode = ARCMSR_IOCTL_READ_RQBUFFER; + + switch ( arcmsr_cmd ) + { + // command for writing data to driver + case ARCMSR_IOCTL_WRITE_WQBUFFER: + if ( data && data_len ) + { + sBuf.srbioctl.Length = data_len; + memcpy((unsigned char *)sBuf.ioctldatabuffer, (unsigned char *)data, data_len); + } + // commands for clearing related buffer of driver + case ARCMSR_IOCTL_CLEAR_RQBUFFER: + case ARCMSR_IOCTL_CLEAR_WQBUFFER: + break; + // command for reading data from driver + case ARCMSR_IOCTL_READ_RQBUFFER: + break; + default: + // unknown arcmsr commands + return -1; + } + + + while ( 1 ) + { + ioctlreturn = ioctl(fd,arcmsr_cmd,&sBuf); + if ( ioctlreturn ) + { + // errors found + break; + } + + if ( arcmsr_cmd != ARCMSR_IOCTL_READ_RQBUFFER ) + { + // if succeeded, just returns the length of outgoing data + return data_len; + } + + if ( sBuf.srbioctl.Length ) + { + if(ata_debugmode) + areca_dumpdata(&sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length); + memcpy(ptr, &sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length); + ptr += sBuf.srbioctl.Length; + total += sBuf.srbioctl.Length; + // the returned bytes enough to compute payload length ? + if ( expected < 0 && total >= 5 ) + { + areca_return_packet = (unsigned char *)&return_buff[0]; + if ( areca_return_packet[0] == 0x5E && + areca_return_packet[1] == 0x01 && + areca_return_packet[2] == 0x61 ) + { + // valid header, let's compute the returned payload length, + // we expected the total length is + // payload + 3 bytes header + 2 bytes length + 1 byte checksum + expected = areca_return_packet[4] * 256 + areca_return_packet[3] + 6; + } + } + + if ( total >= 7 && total >= expected ) + { + //printf("total bytes received = %d, expected length = %d\n", total, expected); + + // ------ Okay! we received enough -------- + break; + } + } + } + + // Deal with the different error cases + if ( ioctlreturn ) + { + pout("ioctl write buffer failed code = %x\n", ioctlreturn); + return -2; + } + + + if ( data ) + { + memcpy(data, return_buff, total); + } + + return total; +} + + +freebsd_areca_device::freebsd_areca_device(smart_interface * intf, const char * dev_name, int disknum) +: smart_device(intf, dev_name, "areca", "areca"), + freebsd_smart_device("ATA"), + m_disknum(disknum) +{ + set_info().info_name = strprintf("%s [areca_%02d]", dev_name, disknum); +} + +// Areca RAID Controller +// int freebsd_areca_device::ata_command_interface(smart_command_set command, int select, char * data) +bool freebsd_areca_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) +{ +if (!ata_cmd_is_ok(in, + true, // data_out_support + false, // TODO: multi_sector_support + true) // ata_48bit_support + ) + return false; + + // ATA input registers + typedef struct _ATA_INPUT_REGISTERS + { + unsigned char features; + unsigned char sector_count; + unsigned char sector_number; + unsigned char cylinder_low; + unsigned char cylinder_high; + unsigned char device_head; + unsigned char command; + unsigned char reserved[8]; + unsigned char data[512]; // [in/out] buffer for outgoing/incoming data + } sATA_INPUT_REGISTERS; + + // ATA output registers + // Note: The output registers is re-sorted for areca internal use only + typedef struct _ATA_OUTPUT_REGISTERS + { + unsigned char error; + unsigned char status; + unsigned char sector_count; + unsigned char sector_number; + unsigned char cylinder_low; + unsigned char cylinder_high; + }sATA_OUTPUT_REGISTERS; + + // Areca packet format for outgoing: + // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 + // B[3~4] : 2 bytes command length + variant data length, little endian + // B[5] : 1 bytes areca defined command code, ATA passthrough command code is 0x1c + // B[6~last-1] : variant bytes payload data + // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) + // + // + // header 3 bytes length 2 bytes cmd 1 byte payload data x bytes cs 1 byte + // +--------------------------------------------------------------------------------+ + // + 0x5E 0x01 0x61 | 0x00 0x00 | 0x1c | .................... | 0x00 | + // +--------------------------------------------------------------------------------+ + // + + //Areca packet format for incoming: + // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61 + // B[3~4] : 2 bytes payload length, little endian + // B[5~last-1] : variant bytes returned payload data + // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1]) + // + // + // header 3 bytes length 2 bytes payload data x bytes cs 1 byte + // +-------------------------------------------------------------------+ + // + 0x5E 0x01 0x61 | 0x00 0x00 | .................... | 0x00 | + // +-------------------------------------------------------------------+ + unsigned char areca_packet[640]; + int areca_packet_len = sizeof(areca_packet); + unsigned char cs = 0; + + sATA_INPUT_REGISTERS *ata_cmd; + + // For debugging + memset(areca_packet, 0, areca_packet_len); + + // ----- BEGIN TO SETUP HEADERS ------- + areca_packet[0] = 0x5E; + areca_packet[1] = 0x01; + areca_packet[2] = 0x61; + areca_packet[3] = (unsigned char)((areca_packet_len - 6) & 0xff); + areca_packet[4] = (unsigned char)(((areca_packet_len - 6) >> 8) & 0xff); + areca_packet[5] = 0x1c; // areca defined code for ATA passthrough command + + // ----- BEGIN TO SETUP PAYLOAD DATA ----- + memcpy(&areca_packet[7], "SmrT", 4); // areca defined password + ata_cmd = (sATA_INPUT_REGISTERS *)&areca_packet[12]; + + // Set registers + { + const ata_in_regs_48bit & r = in.in_regs; + ata_cmd->features = r.features_16; + ata_cmd->sector_count = r.sector_count_16; + ata_cmd->sector_number = r.lba_low_16; + ata_cmd->cylinder_low = r.lba_mid_16; + ata_cmd->cylinder_high = r.lba_high_16; + ata_cmd->device_head = r.device; + ata_cmd->command = r.command; + } + bool readdata = false; + if (in.direction == ata_cmd_in::data_in) { + readdata = true; + // the command will read data + areca_packet[6] = 0x13; + } + else if ( in.direction == ata_cmd_in::no_data ) + { + // the commands will return no data + areca_packet[6] = 0x15; + } + else if (in.direction == ata_cmd_in::data_out) + { + // the commands will write data + memcpy(ata_cmd->data, in.buffer, in.size); + areca_packet[6] = 0x14; + } + else { + // COMMAND NOT SUPPORTED VIA ARECA IOCTL INTERFACE + return set_err(ENOTSUP, "DATA OUT not supported for this Areca controller type"); + } + + areca_packet[11] = m_disknum - 1; // drive number + + // ----- BEGIN TO SETUP CHECKSUM ----- + for ( int loop = 3; loop < areca_packet_len - 1; loop++ ) + { + cs += areca_packet[loop]; + } + areca_packet[areca_packet_len-1] = cs; + + // ----- BEGIN TO SEND TO ARECA DRIVER ------ + int expected = 0; + unsigned char return_buff[2048]; + memset(return_buff, 0, sizeof(return_buff)); + + expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_CLEAR_RQBUFFER, NULL, 0, NULL); + if (expected==-3) { + return set_err(EIO); + } + + expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_CLEAR_WQBUFFER, NULL, 0, NULL); + expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_WRITE_WQBUFFER, areca_packet, areca_packet_len, NULL); + if ( expected > 0 ) + { + expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_READ_RQBUFFER, return_buff, sizeof(return_buff), NULL); + } + if ( expected < 0 ) + { + return -1; + } + + // ----- VERIFY THE CHECKSUM ----- + cs = 0; + for ( int loop = 3; loop < expected - 1; loop++ ) + { + cs += return_buff[loop]; + } + + if ( return_buff[expected - 1] != cs ) + { + return set_err(EIO); + } + + sATA_OUTPUT_REGISTERS *ata_out = (sATA_OUTPUT_REGISTERS *)&return_buff[5] ; + if ( ata_out->status ) + { + 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); + } + } + + // returns with data + if (readdata) + { + memcpy(in.buffer, &return_buff[7], in.size); + } + + // Return register values + { + ata_out_regs_48bit & r = out.out_regs; + r.error = ata_out->error; + r.sector_count_16 = ata_out->sector_count; + r.lba_low_16 = ata_out->sector_number; + r.lba_mid_16 = ata_out->cylinder_low; + r.lba_high_16 = ata_out->cylinder_high; + r.status = ata_out->status; + } + return true; +} + + + ///////////////////////////////////////////////////////////////////////////// /// Implement CCISS RAID support with old functions @@ -1147,11 +1531,6 @@ bool freebsd_cciss_device::open() { const char *dev = get_dev_name(); int fd; -#ifndef HAVE_DEV_CISS_CISSIO_H - 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"); - return false; -#endif if ((fd = ::open(dev,O_RDWR))<0) { set_err(errno); return false; @@ -1171,12 +1550,10 @@ freebsd_cciss_device::freebsd_cciss_device(smart_interface * intf, bool freebsd_cciss_device::scsi_pass_through(scsi_cmnd_io * iop) { -#ifdef HAVE_DEV_CISS_CISSIO_H - int status = cciss_io_interface(get_fd(), m_disknum, iop, con->reportscsiioctl); + int status = cciss_io_interface(get_fd(), m_disknum, iop, scsi_debugmode); if (status < 0) return set_err(-status); return true; -#endif // not reached return true; } @@ -1218,26 +1595,27 @@ smart_device * freebsd_scsi_device::autodetect_open() 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(); - 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()); - 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)) { + close(); + 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()); + 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 @@ -1323,38 +1701,19 @@ scsi_device * freebsd_smart_interface::get_scsi_device(const char * name, const // -1: error // >=0: number of discovered devices -int get_dev_names_cam(char*** names, bool show_all) { - 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; @@ -1362,14 +1721,15 @@ int get_dev_names_cam(char*** names, bool show_all) { 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; @@ -1379,24 +1739,29 @@ int get_dev_names_cam(char*** names, bool show_all) { * 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))) { 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; @@ -1404,8 +1769,7 @@ int get_dev_names_cam(char*** names, bool show_all) { 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 */ + if (strcmp(bus_result->dev_name,"xpt") == 0) /* skip XPT bus at all */ skip_bus = 1; else skip_bus = 0; @@ -1428,51 +1792,24 @@ int get_dev_names_cam(char*** names, bool show_all) { * We are searching for latest 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; - }; + devname = strprintf("%s%s%d", _PATH_DEV, periph_result->periph_name, periph_result->unit_number); changed = 0; }; - if ((changed == 1 || show_all) && 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); + } while ((ccb.ccb_h.status == CAM_REQ_CMP) && (ccb.cdm.status == CAM_DEV_MATCH_MORE)); - if (devname != NULL) { - mp[n] = devname; - devname = NULL; - bytes+=1+strlen(mp[n]); - n++; - }; + if (!devname.empty()) + names.push_back(devname); - mp = (char **)reallocf(mp,n*(sizeof (char*))); // shrink to correct size - bytes += (n)*(sizeof(char*)); // and set allocated byte count - -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 @@ -1540,6 +1877,12 @@ 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: @@ -1577,11 +1920,10 @@ bool freebsd_smart_interface::scan_smart_devices(smart_device_list & devlist, } } - char * * scsinames = 0; int numscsi = 0; + std::vector scsinames; if (!type || !strcmp(type, "scsi")) { // do not export duplicated names - numscsi = get_dev_names_cam(&scsinames,0); - if (numscsi < 0) { - set_err(ENOMEM); + if (!get_dev_names_cam(scsinames, false)) { + set_err(errno); return false; } } @@ -1593,19 +1935,21 @@ 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++) { + 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]); + smart_device * smartdev = autodetect_smart_device(scsinames[i].c_str()); if(smartdev) - devlist.add(smartdev); + devlist.push_back(smartdev); } else { - scsi_device * scsidev = get_scsi_device(scsinames[i], type); + scsi_device * scsidev = get_scsi_device(scsinames[i].c_str(), type); if (scsidev) - devlist.add(scsidev); + devlist.push_back(scsidev); } } return true; @@ -1752,12 +2096,25 @@ smart_device * freebsd_smart_interface::autodetect_smart_device(const char * nam struct cam_device *cam_dev; union ccb ccb; int bus=-1; - int i; + int i,c; int len; + const char * test_name = name; // if dev_name null, or string length zero if (!name || !(len = strlen(name))) - return false; + 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(); + } + } // check ATA bus char * * atanames = 0; int numata = 0; @@ -1765,10 +2122,14 @@ smart_device * freebsd_smart_interface::autodetect_smart_device(const char * nam if (numata > 0) { // check ATA/ATAPI devices for (i = 0; i < numata; i++) { - if(!strcmp(atanames[i],name)) { - return new freebsd_ata_device(this, name, ""); + 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) @@ -1776,19 +2137,19 @@ smart_device * freebsd_smart_interface::autodetect_smart_device(const char * nam } // check CAM - char * * scsinames = 0; int numscsi = 0; - numscsi = get_dev_names_cam(&scsinames, 1); - if (numscsi > 0) { + 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 < numscsi; i++) { - if(strcmp(scsinames[i],name)==0) + for (i = 0; i < (int)scsinames.size(); i++) { + if(strcmp(scsinames[i].c_str(), test_name)==0) { // our disk device is CAM - if ((cam_dev = cam_open_device(name, O_RDWR)) == NULL) { + if ((cam_dev = cam_open_device(test_name, O_RDWR)) == NULL) { // open failure set_err(errno); - return false; + 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 @@ -1805,28 +2166,28 @@ smart_device * freebsd_smart_interface::autodetect_smart_device(const char * nam 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 false; - return get_sat_device(usbtype, new freebsd_scsi_device(this, name, "")); + 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, name, ""); + 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, name, ""); + return new freebsd_scsi_device(this, test_name, ""); } } - } // numscsi > 0 - else { - if(numscsi<0) pout("Unable to get CAM device list\n"); } + // 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; } @@ -1878,7 +2239,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 <= 16)) { set_err(EINVAL, "Option '-d hpt,L/M/N' invalid channel number M supplied"); return 0; } @@ -1896,8 +2257,8 @@ 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 new freebsd_cciss_device(this, name, disknum); @@ -1907,13 +2268,26 @@ smart_device * freebsd_smart_interface::get_custom_smart_device(const char * nam if(!strcmp(type,"atacam")) return new freebsd_atacam_device(this, name, ""); #endif + // Areca? + disknum = n1 = n2 = -1; + if (sscanf(type, "areca,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) { + if (n2 != (int)strlen(type)) { + set_err(EINVAL, "Option -d areca,N requires N to be a non-negative integer"); + return 0; + } + if (!(1 <= disknum && disknum <= 24)) { + set_err(EINVAL, "Option -d areca,N (N=%d) must have 1 <= N <= 24", disknum); + return 0; + } + return new freebsd_areca_device(this, name, disknum); + } return 0; } std::string freebsd_smart_interface::get_valid_custom_dev_types_str() { - return "3ware,N, hpt,L/M/N, cciss,N" + return "3ware,N, hpt,L/M/N, cciss,N, areca,N" #if FREEBSDVER > 800100 ", atacam" #endif