| 1 | /* |
|---|
| 2 | * os_freebsd.c |
|---|
| 3 | * |
|---|
| 4 | * Home page of code is: http://smartmontools.sourceforge.net |
|---|
| 5 | * |
|---|
| 6 | * Copyright (C) 2003-10 Eduard Martinescu <smartmontools-support@lists.sourceforge.net> |
|---|
| 7 | * |
|---|
| 8 | * This program is free software; you can redistribute it and/or modify |
|---|
| 9 | * it under the terms of the GNU General Public License as published by |
|---|
| 10 | * the Free Software Foundation; either version 2, or (at your option) |
|---|
| 11 | * any later version. |
|---|
| 12 | * |
|---|
| 13 | * You should have received a copy of the GNU General Public License |
|---|
| 14 | * (for example COPYING); if not, write to the Free |
|---|
| 15 | * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
|---|
| 16 | */ |
|---|
| 17 | |
|---|
| 18 | #include <stdio.h> |
|---|
| 19 | #include <sys/types.h> |
|---|
| 20 | #include <dirent.h> |
|---|
| 21 | #include <fcntl.h> |
|---|
| 22 | #include <err.h> |
|---|
| 23 | #include <errno.h> |
|---|
| 24 | #include <camlib.h> |
|---|
| 25 | #include <cam/scsi/scsi_message.h> |
|---|
| 26 | #include <cam/scsi/scsi_pass.h> |
|---|
| 27 | #if defined(__DragonFly__) |
|---|
| 28 | #include <sys/nata.h> |
|---|
| 29 | #else |
|---|
| 30 | #include <sys/ata.h> |
|---|
| 31 | #endif |
|---|
| 32 | #include <sys/stat.h> |
|---|
| 33 | #include <unistd.h> |
|---|
| 34 | #include <glob.h> |
|---|
| 35 | #include <stddef.h> |
|---|
| 36 | #include <paths.h> |
|---|
| 37 | #include <sys/utsname.h> |
|---|
| 38 | |
|---|
| 39 | #include "config.h" |
|---|
| 40 | #include "int64.h" |
|---|
| 41 | #include "atacmds.h" |
|---|
| 42 | #include "scsicmds.h" |
|---|
| 43 | #include "cciss.h" |
|---|
| 44 | #include "utility.h" |
|---|
| 45 | #include "os_freebsd.h" |
|---|
| 46 | |
|---|
| 47 | #include "dev_interface.h" |
|---|
| 48 | #include "dev_ata_cmd_set.h" |
|---|
| 49 | #include "dev_areca.h" |
|---|
| 50 | |
|---|
| 51 | #define USBDEV "/dev/usb" |
|---|
| 52 | #if defined(__FreeBSD_version) |
|---|
| 53 | |
|---|
| 54 | // This way we define one variable for the GNU/kFreeBSD and FreeBSD |
|---|
| 55 | #define FREEBSDVER __FreeBSD_version |
|---|
| 56 | #else |
|---|
| 57 | #define FREEBSDVER __FreeBSD_kernel_version |
|---|
| 58 | #endif |
|---|
| 59 | |
|---|
| 60 | #if (FREEBSDVER >= 800000) |
|---|
| 61 | #include <libusb20_desc.h> |
|---|
| 62 | #include <libusb20.h> |
|---|
| 63 | #elif defined(__DragonFly__) |
|---|
| 64 | #include <bus/usb/usb.h> |
|---|
| 65 | #include <bus/usb/usbhid.h> |
|---|
| 66 | #else |
|---|
| 67 | #include <dev/usb/usb.h> |
|---|
| 68 | #include <dev/usb/usbhid.h> |
|---|
| 69 | #endif |
|---|
| 70 | |
|---|
| 71 | #define CONTROLLER_3WARE_9000_CHAR 0x01 |
|---|
| 72 | #define CONTROLLER_3WARE_678K_CHAR 0x02 |
|---|
| 73 | |
|---|
| 74 | #ifndef PATHINQ_SETTINGS_SIZE |
|---|
| 75 | #define PATHINQ_SETTINGS_SIZE 128 |
|---|
| 76 | #endif |
|---|
| 77 | |
|---|
| 78 | const char *os_XXXX_c_cvsid="$Id$" \ |
|---|
| 79 | ATACMDS_H_CVSID CCISS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_FREEBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; |
|---|
| 80 | |
|---|
| 81 | #define NO_RETURN 0 |
|---|
| 82 | #define BAD_SMART 1 |
|---|
| 83 | #define NO_DISK_3WARE 2 |
|---|
| 84 | #define BAD_KERNEL 3 |
|---|
| 85 | #define MAX_MSG 3 |
|---|
| 86 | |
|---|
| 87 | // Utility function for printing warnings |
|---|
| 88 | void printwarning(int msgNo, const char* extra) { |
|---|
| 89 | static int printed[] = {0,0,0,0}; |
|---|
| 90 | static const char* message[]={ |
|---|
| 91 | "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", |
|---|
| 92 | |
|---|
| 93 | "Error SMART Status command failed\nPlease get assistance from \n" PACKAGE_HOMEPAGE "\nRegister values returned from SMART Status command are:\n", |
|---|
| 94 | |
|---|
| 95 | "You must specify a DISK # for 3ware drives with -d 3ware,<n> where <n> begins with 1 for first disk drive\n", |
|---|
| 96 | |
|---|
| 97 | "ATA support is not provided for this kernel version. Please ugrade to a recent 5-CURRENT kernel (post 09/01/2003 or so)\n" |
|---|
| 98 | }; |
|---|
| 99 | |
|---|
| 100 | if (msgNo >= 0 && msgNo <= MAX_MSG) { |
|---|
| 101 | if (!printed[msgNo]) { |
|---|
| 102 | printed[msgNo] = 1; |
|---|
| 103 | pout("%s", message[msgNo]); |
|---|
| 104 | if (extra) |
|---|
| 105 | pout("%s",extra); |
|---|
| 106 | } |
|---|
| 107 | } |
|---|
| 108 | return; |
|---|
| 109 | } |
|---|
| 110 | |
|---|
| 111 | // Interface to ATA devices behind 3ware escalade RAID controller cards. See os_linux.c |
|---|
| 112 | |
|---|
| 113 | #define BUFFER_LEN_678K_CHAR ( sizeof(struct twe_usercommand) ) // 520 |
|---|
| 114 | #define BUFFER_LEN_9000_CHAR ( sizeof(TW_OSLI_IOCTL_NO_DATA_BUF) + sizeof(TWE_Command) ) // 2048 |
|---|
| 115 | #define TW_IOCTL_BUFFER_SIZE ( MAX(BUFFER_LEN_678K_CHAR, BUFFER_LEN_9000_CHAR) ) |
|---|
| 116 | |
|---|
| 117 | #ifndef ATA_DEVICE |
|---|
| 118 | #define ATA_DEVICE "/dev/ata" |
|---|
| 119 | #endif |
|---|
| 120 | |
|---|
| 121 | #define ARGUSED(x) ((void)(x)) |
|---|
| 122 | |
|---|
| 123 | // global variable holding byte count of allocated memory |
|---|
| 124 | long long bytes; |
|---|
| 125 | |
|---|
| 126 | ///////////////////////////////////////////////////////////////////////////// |
|---|
| 127 | |
|---|
| 128 | namespace os_freebsd { // No need to publish anything, name provided for Doxygen |
|---|
| 129 | |
|---|
| 130 | ///////////////////////////////////////////////////////////////////////////// |
|---|
| 131 | /// Implement shared open/close routines with old functions. |
|---|
| 132 | |
|---|
| 133 | class freebsd_smart_device |
|---|
| 134 | : virtual public /*implements*/ smart_device |
|---|
| 135 | { |
|---|
| 136 | public: |
|---|
| 137 | explicit freebsd_smart_device(const char * mode) |
|---|
| 138 | : smart_device(never_called), |
|---|
| 139 | m_fd(-1), m_mode(mode) { } |
|---|
| 140 | |
|---|
| 141 | virtual ~freebsd_smart_device() throw(); |
|---|
| 142 | |
|---|
| 143 | virtual bool is_open() const; |
|---|
| 144 | |
|---|
| 145 | virtual bool open(); |
|---|
| 146 | |
|---|
| 147 | virtual bool close(); |
|---|
| 148 | |
|---|
| 149 | protected: |
|---|
| 150 | /// Return filedesc for derived classes. |
|---|
| 151 | int get_fd() const |
|---|
| 152 | { return m_fd; } |
|---|
| 153 | |
|---|
| 154 | void set_fd(int fd) |
|---|
| 155 | { m_fd = fd; } |
|---|
| 156 | |
|---|
| 157 | private: |
|---|
| 158 | int m_fd; ///< filedesc, -1 if not open. |
|---|
| 159 | const char * m_mode; ///< Mode string for deviceopen(). |
|---|
| 160 | }; |
|---|
| 161 | |
|---|
| 162 | #ifdef __GLIBC__ |
|---|
| 163 | static inline void * reallocf(void *ptr, size_t size) { |
|---|
| 164 | void *rv = realloc(ptr, size); |
|---|
| 165 | if((rv == NULL) && (size != 0)) |
|---|
| 166 | free(ptr); |
|---|
| 167 | return rv; |
|---|
| 168 | } |
|---|
| 169 | #endif |
|---|
| 170 | |
|---|
| 171 | freebsd_smart_device::~freebsd_smart_device() throw() |
|---|
| 172 | { |
|---|
| 173 | if (m_fd >= 0) |
|---|
| 174 | os_freebsd::freebsd_smart_device::close(); |
|---|
| 175 | } |
|---|
| 176 | |
|---|
| 177 | // migration from the old_style |
|---|
| 178 | unsigned char m_controller_type; |
|---|
| 179 | unsigned char m_controller_port; |
|---|
| 180 | |
|---|
| 181 | // examples for smartctl |
|---|
| 182 | static const char smartctl_examples[] = |
|---|
| 183 | "=================================================== SMARTCTL EXAMPLES =====\n\n" |
|---|
| 184 | " smartctl -a /dev/ad0 (Prints all SMART information)\n\n" |
|---|
| 185 | " smartctl --smart=on --offlineauto=on --saveauto=on /dev/ad0\n" |
|---|
| 186 | " (Enables SMART on first disk)\n\n" |
|---|
| 187 | " smartctl -t long /dev/ad0 (Executes extended disk self-test)\n\n" |
|---|
| 188 | " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/ad0\n" |
|---|
| 189 | " (Prints Self-Test & Attribute errors)\n" |
|---|
| 190 | " (Prints Self-Test & Attribute errors)\n\n" |
|---|
| 191 | " smartctl -a --device=3ware,2 /dev/twa0\n" |
|---|
| 192 | " smartctl -a --device=3ware,2 /dev/twe0\n" |
|---|
| 193 | " smartctl -a --device=3ware,2 /dev/tws0\n" |
|---|
| 194 | " (Prints all SMART information for ATA disk on\n" |
|---|
| 195 | " third port of first 3ware RAID controller)\n" |
|---|
| 196 | " smartctl -a --device=cciss,0 /dev/ciss0\n" |
|---|
| 197 | " (Prints all SMART information for first disk \n" |
|---|
| 198 | " on Common Interface for SCSI-3 Support driver)\n" |
|---|
| 199 | " smartctl -a --device=areca,3/1 /dev/arcmsr0\n" |
|---|
| 200 | " (Prints all SMART information for 3rd disk in the 1st enclosure \n" |
|---|
| 201 | " on first ARECA RAID controller)\n" |
|---|
| 202 | |
|---|
| 203 | ; |
|---|
| 204 | |
|---|
| 205 | bool freebsd_smart_device::is_open() const |
|---|
| 206 | { |
|---|
| 207 | return (m_fd >= 0); |
|---|
| 208 | } |
|---|
| 209 | |
|---|
| 210 | |
|---|
| 211 | bool freebsd_smart_device::open() |
|---|
| 212 | { |
|---|
| 213 | const char *dev = get_dev_name(); |
|---|
| 214 | if ((m_fd = ::open(dev,O_RDONLY))<0) { |
|---|
| 215 | set_err(errno); |
|---|
| 216 | return false; |
|---|
| 217 | } |
|---|
| 218 | return true; |
|---|
| 219 | } |
|---|
| 220 | |
|---|
| 221 | bool freebsd_smart_device::close() |
|---|
| 222 | { |
|---|
| 223 | int failed = 0; |
|---|
| 224 | // close device, if open |
|---|
| 225 | if (is_open()) |
|---|
| 226 | failed=::close(get_fd()); |
|---|
| 227 | |
|---|
| 228 | set_fd(-1); |
|---|
| 229 | |
|---|
| 230 | if(failed) return false; |
|---|
| 231 | else return true; |
|---|
| 232 | } |
|---|
| 233 | |
|---|
| 234 | ///////////////////////////////////////////////////////////////////////////// |
|---|
| 235 | /// Implement standard ATA support |
|---|
| 236 | |
|---|
| 237 | class freebsd_ata_device |
|---|
| 238 | : public /*implements*/ ata_device, |
|---|
| 239 | public /*extends*/ freebsd_smart_device |
|---|
| 240 | { |
|---|
| 241 | public: |
|---|
| 242 | freebsd_ata_device(smart_interface * intf, const char * dev_name, const char * req_type); |
|---|
| 243 | virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); |
|---|
| 244 | |
|---|
| 245 | protected: |
|---|
| 246 | virtual int do_cmd(struct ata_ioc_request* request, bool is_48bit_cmd); |
|---|
| 247 | }; |
|---|
| 248 | |
|---|
| 249 | freebsd_ata_device::freebsd_ata_device(smart_interface * intf, const char * dev_name, const char * req_type) |
|---|
| 250 | : smart_device(intf, dev_name, "ata", req_type), |
|---|
| 251 | freebsd_smart_device("ATA") |
|---|
| 252 | { |
|---|
| 253 | } |
|---|
| 254 | |
|---|
| 255 | int freebsd_ata_device::do_cmd( struct ata_ioc_request* request, bool is_48bit_cmd) |
|---|
| 256 | { |
|---|
| 257 | int fd = get_fd(); |
|---|
| 258 | ARGUSED(is_48bit_cmd); // no support for 48 bit commands in the IOCATAREQUEST |
|---|
| 259 | return ioctl(fd, IOCATAREQUEST, request); |
|---|
| 260 | } |
|---|
| 261 | |
|---|
| 262 | |
|---|
| 263 | |
|---|
| 264 | bool freebsd_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) |
|---|
| 265 | { |
|---|
| 266 | bool ata_48bit = false; // no ata_48bit_support via IOCATAREQUEST |
|---|
| 267 | if(!strcmp("atacam",get_dev_type())) // enable for atacam interface |
|---|
| 268 | ata_48bit = true; |
|---|
| 269 | |
|---|
| 270 | if (!ata_cmd_is_ok(in, |
|---|
| 271 | true, // data_out_support |
|---|
| 272 | true, // multi_sector_support |
|---|
| 273 | ata_48bit) |
|---|
| 274 | ) |
|---|
| 275 | return false; |
|---|
| 276 | |
|---|
| 277 | struct ata_ioc_request request; |
|---|
| 278 | bzero(&request,sizeof(struct ata_ioc_request)); |
|---|
| 279 | |
|---|
| 280 | request.timeout=SCSI_TIMEOUT_DEFAULT; |
|---|
| 281 | request.u.ata.command=in.in_regs.command; |
|---|
| 282 | request.u.ata.feature=in.in_regs.features; |
|---|
| 283 | |
|---|
| 284 | request.u.ata.count = in.in_regs.sector_count_16; |
|---|
| 285 | request.u.ata.lba = in.in_regs.lba_48; |
|---|
| 286 | |
|---|
| 287 | switch (in.direction) { |
|---|
| 288 | case ata_cmd_in::no_data: |
|---|
| 289 | request.flags=ATA_CMD_CONTROL; |
|---|
| 290 | break; |
|---|
| 291 | case ata_cmd_in::data_in: |
|---|
| 292 | request.flags=ATA_CMD_READ | ATA_CMD_CONTROL; |
|---|
| 293 | request.data=(char *)in.buffer; |
|---|
| 294 | request.count=in.size; |
|---|
| 295 | break; |
|---|
| 296 | case ata_cmd_in::data_out: |
|---|
| 297 | request.flags=ATA_CMD_WRITE | ATA_CMD_CONTROL; |
|---|
| 298 | request.data=(char *)in.buffer; |
|---|
| 299 | request.count=in.size; |
|---|
| 300 | break; |
|---|
| 301 | default: |
|---|
| 302 | return set_err(ENOSYS); |
|---|
| 303 | } |
|---|
| 304 | |
|---|
| 305 | clear_err(); |
|---|
| 306 | errno = 0; |
|---|
| 307 | if (do_cmd(&request, in.in_regs.is_48bit_cmd())) |
|---|
| 308 | return set_err(errno); |
|---|
| 309 | if (request.error) |
|---|
| 310 | return set_err(EIO, "request failed, error code 0x%02x", request.error); |
|---|
| 311 | |
|---|
| 312 | out.out_regs.error = request.error; |
|---|
| 313 | out.out_regs.sector_count_16 = request.u.ata.count; |
|---|
| 314 | out.out_regs.lba_48 = request.u.ata.lba; |
|---|
| 315 | |
|---|
| 316 | |
|---|
| 317 | // Command specific processing |
|---|
| 318 | if (in.in_regs.command == ATA_SMART_CMD |
|---|
| 319 | && in.in_regs.features == ATA_SMART_STATUS |
|---|
| 320 | && in.out_needed.lba_high) |
|---|
| 321 | { |
|---|
| 322 | unsigned const char normal_lo=0x4f, normal_hi=0xc2; |
|---|
| 323 | unsigned const char failed_lo=0xf4, failed_hi=0x2c; |
|---|
| 324 | |
|---|
| 325 | // Cyl low and Cyl high unchanged means "Good SMART status" |
|---|
| 326 | if (!(out.out_regs.lba_mid==normal_lo && out.out_regs.lba_high==normal_hi) |
|---|
| 327 | // These values mean "Bad SMART status" |
|---|
| 328 | && !(out.out_regs.lba_mid==failed_lo && out.out_regs.lba_high==failed_hi)) |
|---|
| 329 | |
|---|
| 330 | { |
|---|
| 331 | // We haven't gotten output that makes sense; print out some debugging info |
|---|
| 332 | char buf[512]; |
|---|
| 333 | sprintf(buf,"CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n", |
|---|
| 334 | (int)request.u.ata.command, |
|---|
| 335 | (int)request.u.ata.feature, |
|---|
| 336 | (int)request.u.ata.count, |
|---|
| 337 | (int)((request.u.ata.lba) & 0xff), |
|---|
| 338 | (int)((request.u.ata.lba>>8) & 0xff), |
|---|
| 339 | (int)((request.u.ata.lba>>16) & 0xff), |
|---|
| 340 | (int)request.error); |
|---|
| 341 | printwarning(BAD_SMART,buf); |
|---|
| 342 | out.out_regs.lba_high = failed_hi; |
|---|
| 343 | out.out_regs.lba_mid = failed_lo; |
|---|
| 344 | } |
|---|
| 345 | } |
|---|
| 346 | |
|---|
| 347 | return true; |
|---|
| 348 | } |
|---|
| 349 | |
|---|
| 350 | #if FREEBSDVER > 800100 |
|---|
| 351 | class freebsd_atacam_device : public freebsd_ata_device |
|---|
| 352 | { |
|---|
| 353 | public: |
|---|
| 354 | freebsd_atacam_device(smart_interface * intf, const char * dev_name, const char * req_type) |
|---|
| 355 | : smart_device(intf, dev_name, "atacam", req_type), freebsd_ata_device(intf, dev_name, req_type) |
|---|
| 356 | {} |
|---|
| 357 | |
|---|
| 358 | virtual bool open(); |
|---|
| 359 | virtual bool close(); |
|---|
| 360 | |
|---|
| 361 | protected: |
|---|
| 362 | int m_fd; |
|---|
| 363 | struct cam_device *m_camdev; |
|---|
| 364 | |
|---|
| 365 | virtual int do_cmd( struct ata_ioc_request* request , bool is_48bit_cmd); |
|---|
| 366 | }; |
|---|
| 367 | |
|---|
| 368 | bool freebsd_atacam_device::open(){ |
|---|
| 369 | const char *dev = get_dev_name(); |
|---|
| 370 | |
|---|
| 371 | if ((m_camdev = cam_open_device(dev, O_RDWR)) == NULL) { |
|---|
| 372 | set_err(errno); |
|---|
| 373 | return false; |
|---|
| 374 | } |
|---|
| 375 | set_fd(m_camdev->fd); |
|---|
| 376 | return true; |
|---|
| 377 | } |
|---|
| 378 | |
|---|
| 379 | bool freebsd_atacam_device::close(){ |
|---|
| 380 | cam_close_device(m_camdev); |
|---|
| 381 | set_fd(-1); |
|---|
| 382 | return true; |
|---|
| 383 | } |
|---|
| 384 | |
|---|
| 385 | int freebsd_atacam_device::do_cmd( struct ata_ioc_request* request, bool is_48bit_cmd) |
|---|
| 386 | { |
|---|
| 387 | union ccb ccb; |
|---|
| 388 | int camflags; |
|---|
| 389 | |
|---|
| 390 | memset(&ccb, 0, sizeof(ccb)); |
|---|
| 391 | |
|---|
| 392 | if (request->count == 0) |
|---|
| 393 | camflags = CAM_DIR_NONE; |
|---|
| 394 | else if (request->flags & ATA_CMD_READ) |
|---|
| 395 | camflags = CAM_DIR_IN; |
|---|
| 396 | else |
|---|
| 397 | camflags = CAM_DIR_OUT; |
|---|
| 398 | if(is_48bit_cmd) |
|---|
| 399 | camflags |= CAM_ATAIO_48BIT; |
|---|
| 400 | |
|---|
| 401 | cam_fill_ataio(&ccb.ataio, |
|---|
| 402 | 0, |
|---|
| 403 | NULL, |
|---|
| 404 | camflags, |
|---|
| 405 | MSG_SIMPLE_Q_TAG, |
|---|
| 406 | (u_int8_t*)request->data, |
|---|
| 407 | request->count, |
|---|
| 408 | request->timeout * 1000); // timeout in seconds |
|---|
| 409 | |
|---|
| 410 | ccb.ataio.cmd.flags = CAM_ATAIO_NEEDRESULT; |
|---|
| 411 | // ata_28bit_cmd |
|---|
| 412 | ccb.ataio.cmd.command = request->u.ata.command; |
|---|
| 413 | ccb.ataio.cmd.features = request->u.ata.feature; |
|---|
| 414 | ccb.ataio.cmd.lba_low = request->u.ata.lba; |
|---|
| 415 | ccb.ataio.cmd.lba_mid = request->u.ata.lba >> 8; |
|---|
| 416 | ccb.ataio.cmd.lba_high = request->u.ata.lba >> 16; |
|---|
| 417 | // ata_48bit cmd |
|---|
| 418 | ccb.ataio.cmd.lba_low_exp = request->u.ata.lba >> 24; |
|---|
| 419 | ccb.ataio.cmd.lba_mid_exp = request->u.ata.lba >> 32; |
|---|
| 420 | ccb.ataio.cmd.lba_high_exp = request->u.ata.lba >> 40; |
|---|
| 421 | ccb.ataio.cmd.device = 0x40 | ((request->u.ata.lba >> 24) & 0x0f); |
|---|
| 422 | ccb.ataio.cmd.sector_count = request->u.ata.count; |
|---|
| 423 | ccb.ataio.cmd.sector_count_exp = request->u.ata.count >> 8;; |
|---|
| 424 | |
|---|
| 425 | ccb.ccb_h.flags |= CAM_DEV_QFRZDIS; |
|---|
| 426 | |
|---|
| 427 | if (cam_send_ccb(m_camdev, &ccb) < 0) { |
|---|
| 428 | err(1, "cam_send_ccb"); |
|---|
| 429 | return -1; |
|---|
| 430 | } |
|---|
| 431 | |
|---|
| 432 | if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { |
|---|
| 433 | cam_error_print(m_camdev, &ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); |
|---|
| 434 | return -1; |
|---|
| 435 | } |
|---|
| 436 | |
|---|
| 437 | request->u.ata.lba = |
|---|
| 438 | ((u_int64_t)(ccb.ataio.res.lba_low)) | |
|---|
| 439 | ((u_int64_t)(ccb.ataio.res.lba_mid) << 8) | |
|---|
| 440 | ((u_int64_t)(ccb.ataio.res.lba_high) << 16) | |
|---|
| 441 | ((u_int64_t)(ccb.ataio.res.lba_low_exp) << 24) | |
|---|
| 442 | ((u_int64_t)(ccb.ataio.res.lba_mid_exp) << 32) | |
|---|
| 443 | ((u_int64_t)(ccb.ataio.res.lba_high_exp) << 40); |
|---|
| 444 | |
|---|
| 445 | request->u.ata.count = ccb.ataio.res.sector_count | (ccb.ataio.res.sector_count_exp << 8); |
|---|
| 446 | request->error = ccb.ataio.res.error; |
|---|
| 447 | |
|---|
| 448 | return 0; |
|---|
| 449 | } |
|---|
| 450 | |
|---|
| 451 | #endif |
|---|
| 452 | |
|---|
| 453 | ///////////////////////////////////////////////////////////////////////////// |
|---|
| 454 | /// Implement AMCC/3ware RAID support |
|---|
| 455 | |
|---|
| 456 | class freebsd_escalade_device |
|---|
| 457 | : public /*implements*/ ata_device, |
|---|
| 458 | public /*extends*/ freebsd_smart_device |
|---|
| 459 | { |
|---|
| 460 | public: |
|---|
| 461 | freebsd_escalade_device(smart_interface * intf, const char * dev_name, |
|---|
| 462 | int escalade_type, int disknum); |
|---|
| 463 | |
|---|
| 464 | protected: |
|---|
| 465 | virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); |
|---|
| 466 | virtual bool open(); |
|---|
| 467 | |
|---|
| 468 | private: |
|---|
| 469 | int m_escalade_type; ///< Type string for escalade_command_interface(). |
|---|
| 470 | int m_disknum; ///< Disk number. |
|---|
| 471 | }; |
|---|
| 472 | |
|---|
| 473 | freebsd_escalade_device::freebsd_escalade_device(smart_interface * intf, const char * dev_name, |
|---|
| 474 | int escalade_type, int disknum) |
|---|
| 475 | : smart_device(intf, dev_name, "3ware", "3ware"), |
|---|
| 476 | freebsd_smart_device( |
|---|
| 477 | escalade_type==CONTROLLER_3WARE_9000_CHAR ? "ATA_3WARE_9000" : |
|---|
| 478 | escalade_type==CONTROLLER_3WARE_678K_CHAR ? "ATA_3WARE_678K" : |
|---|
| 479 | /* CONTROLLER_3WARE_678K */ "ATA" ), |
|---|
| 480 | m_escalade_type(escalade_type), m_disknum(disknum) |
|---|
| 481 | { |
|---|
| 482 | set_info().info_name = strprintf("%s [3ware_disk_%02d]", dev_name, disknum); |
|---|
| 483 | } |
|---|
| 484 | |
|---|
| 485 | bool freebsd_escalade_device::open() |
|---|
| 486 | { |
|---|
| 487 | const char *dev = get_dev_name(); |
|---|
| 488 | int fd; |
|---|
| 489 | |
|---|
| 490 | if ((fd = ::open(dev,O_RDWR))<0) { |
|---|
| 491 | set_err(errno); |
|---|
| 492 | return false; |
|---|
| 493 | } |
|---|
| 494 | set_fd(fd); |
|---|
| 495 | return true; |
|---|
| 496 | } |
|---|
| 497 | |
|---|
| 498 | bool freebsd_escalade_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) |
|---|
| 499 | { |
|---|
| 500 | // to hold true file descriptor |
|---|
| 501 | int fd = get_fd(); |
|---|
| 502 | |
|---|
| 503 | if (!ata_cmd_is_ok(in, |
|---|
| 504 | true, // data_out_support |
|---|
| 505 | false, // TODO: multi_sector_support |
|---|
| 506 | true) // ata_48bit_support |
|---|
| 507 | ) |
|---|
| 508 | return false; |
|---|
| 509 | |
|---|
| 510 | struct twe_usercommand* cmd_twe = NULL; |
|---|
| 511 | TW_OSLI_IOCTL_NO_DATA_BUF* cmd_twa = NULL; |
|---|
| 512 | TWE_Command_ATA* ata = NULL; |
|---|
| 513 | |
|---|
| 514 | // Used by both the SCSI and char interfaces |
|---|
| 515 | char ioctl_buffer[TW_IOCTL_BUFFER_SIZE]; |
|---|
| 516 | |
|---|
| 517 | if (m_disknum < 0) { |
|---|
| 518 | printwarning(NO_DISK_3WARE,NULL); |
|---|
| 519 | return -1; |
|---|
| 520 | } |
|---|
| 521 | |
|---|
| 522 | memset(ioctl_buffer, 0, TW_IOCTL_BUFFER_SIZE); |
|---|
| 523 | |
|---|
| 524 | if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR) { |
|---|
| 525 | cmd_twa = (TW_OSLI_IOCTL_NO_DATA_BUF*)ioctl_buffer; |
|---|
| 526 | cmd_twa->pdata = ((TW_OSLI_IOCTL_WITH_PAYLOAD*)cmd_twa)->payload.data_buf; |
|---|
| 527 | cmd_twa->driver_pkt.buffer_length = 512; |
|---|
| 528 | ata = (TWE_Command_ATA*)&cmd_twa->cmd_pkt.command.cmd_pkt_7k; |
|---|
| 529 | } else if (m_escalade_type==CONTROLLER_3WARE_678K_CHAR) { |
|---|
| 530 | cmd_twe = (struct twe_usercommand*)ioctl_buffer; |
|---|
| 531 | ata = &cmd_twe->tu_command.ata; |
|---|
| 532 | } else { |
|---|
| 533 | return set_err(ENOSYS, |
|---|
| 534 | "Unrecognized escalade_type %d in linux_3ware_command_interface(disk %d)\n" |
|---|
| 535 | "Please contact " PACKAGE_BUGREPORT "\n", (int)m_escalade_type, m_disknum); |
|---|
| 536 | } |
|---|
| 537 | |
|---|
| 538 | ata->opcode = TWE_OP_ATA_PASSTHROUGH; |
|---|
| 539 | |
|---|
| 540 | // Same for (almost) all commands - but some reset below |
|---|
| 541 | ata->request_id = 0xFF; |
|---|
| 542 | ata->unit = m_disknum; |
|---|
| 543 | ata->status = 0; |
|---|
| 544 | ata->flags = 0x1; |
|---|
| 545 | // Set registers |
|---|
| 546 | { |
|---|
| 547 | const ata_in_regs_48bit & r = in.in_regs; |
|---|
| 548 | ata->features = r.features_16; |
|---|
| 549 | ata->sector_count = r.sector_count_16; |
|---|
| 550 | ata->sector_num = r.lba_low_16; |
|---|
| 551 | ata->cylinder_lo = r.lba_mid_16; |
|---|
| 552 | ata->cylinder_hi = r.lba_high_16; |
|---|
| 553 | ata->drive_head = r.device; |
|---|
| 554 | ata->command = r.command; |
|---|
| 555 | } |
|---|
| 556 | |
|---|
| 557 | // Is this a command that reads or returns 512 bytes? |
|---|
| 558 | // passthru->param values are: |
|---|
| 559 | // 0x0 - non data command without TFR write check, |
|---|
| 560 | // 0x8 - non data command with TFR write check, |
|---|
| 561 | // 0xD - data command that returns data to host from device |
|---|
| 562 | // 0xF - data command that writes data from host to device |
|---|
| 563 | // passthru->size values are 0x5 for non-data and 0x07 for data |
|---|
| 564 | bool readdata = false; |
|---|
| 565 | if (in.direction == ata_cmd_in::data_in) { |
|---|
| 566 | readdata=true; |
|---|
| 567 | ata->sgl_offset = 0x5; |
|---|
| 568 | ata->size = 0x5; // TODO: other values for multisector? |
|---|
| 569 | ata->param = 0xD; |
|---|
| 570 | // For 64-bit to work correctly, up the size of the command packet |
|---|
| 571 | // in dwords by 1 to account for the 64-bit single sgl 'address' |
|---|
| 572 | // field. Note that this doesn't agree with the typedefs but it's |
|---|
| 573 | // right (agree with kernel driver behavior/typedefs). |
|---|
| 574 | // if (sizeof(long)==8) |
|---|
| 575 | // ata->size++; |
|---|
| 576 | } |
|---|
| 577 | else if (in.direction == ata_cmd_in::no_data) { |
|---|
| 578 | // Non data command -- but doesn't use large sector |
|---|
| 579 | // count register values. |
|---|
| 580 | ata->sgl_offset = 0x0; |
|---|
| 581 | ata->size = 0x5; |
|---|
| 582 | ata->param = 0x8; |
|---|
| 583 | ata->sector_count = 0x0; |
|---|
| 584 | } |
|---|
| 585 | |
|---|
| 586 | else if (in.direction == ata_cmd_in::data_out) { |
|---|
| 587 | // Non data command -- but doesn't use large sector |
|---|
| 588 | // count register values. |
|---|
| 589 | ata->sgl_offset = 0x5; |
|---|
| 590 | ata->size = 0x5; // TODO: not valid for multisector |
|---|
| 591 | ata->param = 0xF; // PIO data write |
|---|
| 592 | if (m_escalade_type==CONTROLLER_3WARE_678K_CHAR) { |
|---|
| 593 | cmd_twe->tu_data = in.buffer; |
|---|
| 594 | cmd_twe->tu_size = in.size; |
|---|
| 595 | } |
|---|
| 596 | else if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR) { |
|---|
| 597 | cmd_twa->pdata = in.buffer; |
|---|
| 598 | cmd_twa->driver_pkt.buffer_length = in.size; |
|---|
| 599 | } |
|---|
| 600 | } |
|---|
| 601 | |
|---|
| 602 | // 3WARE controller can NOT have packet device internally |
|---|
| 603 | if (in.in_regs.command == ATA_IDENTIFY_PACKET_DEVICE) { |
|---|
| 604 | return set_err(ENODEV, "No drive on port %d", m_disknum); |
|---|
| 605 | } |
|---|
| 606 | |
|---|
| 607 | // Now send the command down through an ioctl() |
|---|
| 608 | int ioctlreturn; |
|---|
| 609 | if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR) { |
|---|
| 610 | ioctlreturn=ioctl(fd,TW_OSL_IOCTL_FIRMWARE_PASS_THROUGH,cmd_twa); |
|---|
| 611 | } else { |
|---|
| 612 | ioctlreturn=ioctl(fd,TWEIO_COMMAND,cmd_twe); |
|---|
| 613 | } |
|---|
| 614 | |
|---|
| 615 | // Deal with the different error cases |
|---|
| 616 | if (ioctlreturn) { |
|---|
| 617 | return set_err(EIO); |
|---|
| 618 | } |
|---|
| 619 | |
|---|
| 620 | // See if the ATA command failed. Now that we have returned from |
|---|
| 621 | // the ioctl() call, if passthru is valid, then: |
|---|
| 622 | // - ata->status contains the 3ware controller STATUS |
|---|
| 623 | // - ata->command contains the ATA STATUS register |
|---|
| 624 | // - ata->features contains the ATA ERROR register |
|---|
| 625 | // |
|---|
| 626 | // Check bits 0 (error bit) and 5 (device fault) of the ATA STATUS |
|---|
| 627 | // If bit 0 (error bit) is set, then ATA ERROR register is valid. |
|---|
| 628 | // While we *might* decode the ATA ERROR register, at the moment it |
|---|
| 629 | // doesn't make much sense: we don't care in detail why the error |
|---|
| 630 | // happened. |
|---|
| 631 | |
|---|
| 632 | if (ata->status || (ata->command & 0x21)) { |
|---|
| 633 | if (scsi_debugmode) |
|---|
| 634 | 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); |
|---|
| 635 | return set_err(EIO); |
|---|
| 636 | } |
|---|
| 637 | |
|---|
| 638 | // If this is a read data command, copy data to output buffer |
|---|
| 639 | if (readdata) { |
|---|
| 640 | if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR) |
|---|
| 641 | memcpy(in.buffer, cmd_twa->pdata, in.size); |
|---|
| 642 | else if(m_escalade_type==CONTROLLER_3WARE_678K_CHAR) { |
|---|
| 643 | memcpy(in.buffer, cmd_twe->tu_data, in.size); // untested |
|---|
| 644 | } |
|---|
| 645 | } |
|---|
| 646 | // Return register values |
|---|
| 647 | if (ata) { |
|---|
| 648 | ata_out_regs_48bit & r = out.out_regs; |
|---|
| 649 | r.error = ata->features; |
|---|
| 650 | r.sector_count_16 = ata->sector_count; |
|---|
| 651 | r.lba_low_16 = ata->sector_num; |
|---|
| 652 | r.lba_mid_16 = ata->cylinder_lo; |
|---|
| 653 | r.lba_high_16 = ata->cylinder_hi; |
|---|
| 654 | r.device = ata->drive_head; |
|---|
| 655 | r.status = ata->command; |
|---|
| 656 | } |
|---|
| 657 | // look for nonexistent devices/ports |
|---|
| 658 | if (in.in_regs.command == ATA_IDENTIFY_DEVICE |
|---|
| 659 | && !nonempty((unsigned char *)in.buffer, in.size)) { |
|---|
| 660 | return set_err(ENODEV, "No drive on port %d", m_disknum); |
|---|
| 661 | } |
|---|
| 662 | return true; |
|---|
| 663 | } |
|---|
| 664 | |
|---|
| 665 | |
|---|
| 666 | ///////////////////////////////////////////////////////////////////////////// |
|---|
| 667 | /// Implement Highpoint RAID support with old functions |
|---|
| 668 | |
|---|
| 669 | class freebsd_highpoint_device |
|---|
| 670 | : public /*implements*/ ata_device_with_command_set, |
|---|
| 671 | public /*extends*/ freebsd_smart_device |
|---|
| 672 | { |
|---|
| 673 | public: |
|---|
| 674 | freebsd_highpoint_device(smart_interface * intf, const char * dev_name, |
|---|
| 675 | unsigned char controller, unsigned char channel, unsigned char port); |
|---|
| 676 | |
|---|
| 677 | protected: |
|---|
| 678 | virtual int ata_command_interface(smart_command_set command, int select, char * data); |
|---|
| 679 | virtual bool open(); |
|---|
| 680 | |
|---|
| 681 | private: |
|---|
| 682 | unsigned char m_hpt_data[3]; ///< controller/channel/port |
|---|
| 683 | }; |
|---|
| 684 | |
|---|
| 685 | |
|---|
| 686 | freebsd_highpoint_device::freebsd_highpoint_device(smart_interface * intf, const char * dev_name, |
|---|
| 687 | unsigned char controller, unsigned char channel, unsigned char port) |
|---|
| 688 | : smart_device(intf, dev_name, "hpt", "hpt"), |
|---|
| 689 | freebsd_smart_device("ATA") |
|---|
| 690 | { |
|---|
| 691 | m_hpt_data[0] = controller; m_hpt_data[1] = channel; m_hpt_data[2] = port; |
|---|
| 692 | 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]); |
|---|
| 693 | } |
|---|
| 694 | |
|---|
| 695 | bool freebsd_highpoint_device::open() |
|---|
| 696 | { |
|---|
| 697 | const char *dev = get_dev_name(); |
|---|
| 698 | int fd; |
|---|
| 699 | |
|---|
| 700 | if ((fd = ::open(dev,O_RDWR))<0) { |
|---|
| 701 | set_err(errno); |
|---|
| 702 | return false; |
|---|
| 703 | } |
|---|
| 704 | set_fd(fd); |
|---|
| 705 | return true; |
|---|
| 706 | } |
|---|
| 707 | |
|---|
| 708 | int freebsd_highpoint_device::ata_command_interface(smart_command_set command, int select, char * data) |
|---|
| 709 | { |
|---|
| 710 | int fd=get_fd(); |
|---|
| 711 | int ids[2]; |
|---|
| 712 | HPT_IOCTL_PARAM param; |
|---|
| 713 | HPT_CHANNEL_INFO_V2 info; |
|---|
| 714 | unsigned char* buff[512 + 2 * sizeof(HPT_PASS_THROUGH_HEADER)]; |
|---|
| 715 | PHPT_PASS_THROUGH_HEADER pide_pt_hdr, pide_pt_hdr_out; |
|---|
| 716 | |
|---|
| 717 | // get internal deviceid |
|---|
| 718 | ids[0] = m_hpt_data[0] - 1; |
|---|
| 719 | ids[1] = m_hpt_data[1] - 1; |
|---|
| 720 | |
|---|
| 721 | memset(¶m, 0, sizeof(HPT_IOCTL_PARAM)); |
|---|
| 722 | |
|---|
| 723 | param.magic = HPT_IOCTL_MAGIC; |
|---|
| 724 | param.ctrl_code = HPT_IOCTL_GET_CHANNEL_INFO_V2; |
|---|
| 725 | param.in = (unsigned char *)ids; |
|---|
| 726 | param.in_size = sizeof(unsigned int) * 2; |
|---|
| 727 | param.out = (unsigned char *)&info; |
|---|
| 728 | param.out_size = sizeof(HPT_CHANNEL_INFO_V2); |
|---|
| 729 | |
|---|
| 730 | if (m_hpt_data[2]==1) { |
|---|
| 731 | param.ctrl_code = HPT_IOCTL_GET_CHANNEL_INFO; |
|---|
| 732 | param.out_size = sizeof(HPT_CHANNEL_INFO); |
|---|
| 733 | } |
|---|
| 734 | if (ioctl(fd, HPT_DO_IOCONTROL, ¶m)!=0 || |
|---|
| 735 | info.devices[m_hpt_data[2]-1]==0) { |
|---|
| 736 | return -1; |
|---|
| 737 | } |
|---|
| 738 | |
|---|
| 739 | // perform smart action |
|---|
| 740 | memset(buff, 0, 512 + 2 * sizeof(HPT_PASS_THROUGH_HEADER)); |
|---|
| 741 | pide_pt_hdr = (PHPT_PASS_THROUGH_HEADER)buff; |
|---|
| 742 | |
|---|
| 743 | pide_pt_hdr->lbamid = 0x4f; |
|---|
| 744 | pide_pt_hdr->lbahigh = 0xc2; |
|---|
| 745 | pide_pt_hdr->command = ATA_SMART_CMD; |
|---|
| 746 | pide_pt_hdr->id = info.devices[m_hpt_data[2] - 1]; |
|---|
| 747 | |
|---|
| 748 | switch (command){ |
|---|
| 749 | case READ_VALUES: |
|---|
| 750 | pide_pt_hdr->feature=ATA_SMART_READ_VALUES; |
|---|
| 751 | pide_pt_hdr->protocol=HPT_READ; |
|---|
| 752 | break; |
|---|
| 753 | case READ_THRESHOLDS: |
|---|
| 754 | pide_pt_hdr->feature=ATA_SMART_READ_THRESHOLDS; |
|---|
| 755 | pide_pt_hdr->protocol=HPT_READ; |
|---|
| 756 | break; |
|---|
| 757 | case READ_LOG: |
|---|
| 758 | pide_pt_hdr->feature=ATA_SMART_READ_LOG_SECTOR; |
|---|
| 759 | pide_pt_hdr->lbalow=select; |
|---|
| 760 | pide_pt_hdr->protocol=HPT_READ; |
|---|
| 761 | break; |
|---|
| 762 | case IDENTIFY: |
|---|
| 763 | pide_pt_hdr->command=ATA_IDENTIFY_DEVICE; |
|---|
| 764 | pide_pt_hdr->protocol=HPT_READ; |
|---|
| 765 | break; |
|---|
| 766 | case ENABLE: |
|---|
| 767 | pide_pt_hdr->feature=ATA_SMART_ENABLE; |
|---|
| 768 | break; |
|---|
| 769 | case DISABLE: |
|---|
| 770 | pide_pt_hdr->feature=ATA_SMART_DISABLE; |
|---|
| 771 | break; |
|---|
| 772 | case AUTO_OFFLINE: |
|---|
| 773 | pide_pt_hdr->feature=ATA_SMART_AUTO_OFFLINE; |
|---|
| 774 | pide_pt_hdr->sectorcount=select; |
|---|
| 775 | break; |
|---|
| 776 | case AUTOSAVE: |
|---|
| 777 | pide_pt_hdr->feature=ATA_SMART_AUTOSAVE; |
|---|
| 778 | pide_pt_hdr->sectorcount=select; |
|---|
| 779 | break; |
|---|
| 780 | case IMMEDIATE_OFFLINE: |
|---|
| 781 | pide_pt_hdr->feature=ATA_SMART_IMMEDIATE_OFFLINE; |
|---|
| 782 | pide_pt_hdr->lbalow=select; |
|---|
| 783 | break; |
|---|
| 784 | case STATUS_CHECK: |
|---|
| 785 | case STATUS: |
|---|
| 786 | pide_pt_hdr->feature=ATA_SMART_STATUS; |
|---|
| 787 | break; |
|---|
| 788 | case CHECK_POWER_MODE: |
|---|
| 789 | pide_pt_hdr->command=ATA_CHECK_POWER_MODE; |
|---|
| 790 | break; |
|---|
| 791 | case WRITE_LOG: |
|---|
| 792 | memcpy(buff+sizeof(HPT_PASS_THROUGH_HEADER), data, 512); |
|---|
| 793 | pide_pt_hdr->feature=ATA_SMART_WRITE_LOG_SECTOR; |
|---|
| 794 | pide_pt_hdr->lbalow=select; |
|---|
| 795 | pide_pt_hdr->protocol=HPT_WRITE; |
|---|
| 796 | break; |
|---|
| 797 | default: |
|---|
| 798 | pout("Unrecognized command %d in highpoint_command_interface()\n" |
|---|
| 799 | "Please contact " PACKAGE_BUGREPORT "\n", command); |
|---|
| 800 | errno=ENOSYS; |
|---|
| 801 | return -1; |
|---|
| 802 | } |
|---|
| 803 | if (pide_pt_hdr->protocol!=0) { |
|---|
| 804 | pide_pt_hdr->sectors = 1; |
|---|
| 805 | pide_pt_hdr->sectorcount = 1; |
|---|
| 806 | } |
|---|
| 807 | |
|---|
| 808 | memset(¶m, 0, sizeof(HPT_IOCTL_PARAM)); |
|---|
| 809 | |
|---|
| 810 | param.magic = HPT_IOCTL_MAGIC; |
|---|
| 811 | param.ctrl_code = HPT_IOCTL_IDE_PASS_THROUGH; |
|---|
| 812 | param.in = (unsigned char *)buff; |
|---|
| 813 | param.in_size = sizeof(HPT_PASS_THROUGH_HEADER) + (pide_pt_hdr->protocol==HPT_READ ? 0 : pide_pt_hdr->sectors * 512); |
|---|
| 814 | param.out = (unsigned char *)buff+param.in_size; |
|---|
| 815 | param.out_size = sizeof(HPT_PASS_THROUGH_HEADER) + (pide_pt_hdr->protocol==HPT_READ ? pide_pt_hdr->sectors * 512 : 0); |
|---|
| 816 | |
|---|
| 817 | pide_pt_hdr_out = (PHPT_PASS_THROUGH_HEADER)param.out; |
|---|
| 818 | |
|---|
| 819 | if ((ioctl(fd, HPT_DO_IOCONTROL, ¶m)!=0) || |
|---|
| 820 | (pide_pt_hdr_out->command & 1)) { |
|---|
| 821 | return -1; |
|---|
| 822 | } |
|---|
| 823 | |
|---|
| 824 | if (command==STATUS_CHECK) |
|---|
| 825 | { |
|---|
| 826 | unsigned const char normal_lo=0x4f, normal_hi=0xc2; |
|---|
| 827 | unsigned const char failed_lo=0xf4, failed_hi=0x2c; |
|---|
| 828 | unsigned char low,high; |
|---|
| 829 | |
|---|
| 830 | high = pide_pt_hdr_out->lbahigh; |
|---|
| 831 | low = pide_pt_hdr_out->lbamid; |
|---|
| 832 | |
|---|
| 833 | // Cyl low and Cyl high unchanged means "Good SMART status" |
|---|
| 834 | if (low==normal_lo && high==normal_hi) |
|---|
| 835 | return 0; |
|---|
| 836 | |
|---|
| 837 | // These values mean "Bad SMART status" |
|---|
| 838 | if (low==failed_lo && high==failed_hi) |
|---|
| 839 | return 1; |
|---|
| 840 | |
|---|
| 841 | // We haven't gotten output that makes sense; print out some debugging info |
|---|
| 842 | char buf[512]; |
|---|
| 843 | sprintf(buf,"CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n", |
|---|
| 844 | (int)pide_pt_hdr_out->command, |
|---|
| 845 | (int)pide_pt_hdr_out->feature, |
|---|
| 846 | (int)pide_pt_hdr_out->sectorcount, |
|---|
| 847 | (int)pide_pt_hdr_out->lbalow, |
|---|
| 848 | (int)pide_pt_hdr_out->lbamid, |
|---|
| 849 | (int)pide_pt_hdr_out->lbahigh, |
|---|
| 850 | (int)pide_pt_hdr_out->sectors); |
|---|
| 851 | printwarning(BAD_SMART,buf); |
|---|
| 852 | } |
|---|
| 853 | else if (command==CHECK_POWER_MODE) |
|---|
| 854 | data[0] = pide_pt_hdr_out->sectorcount & 0xff; |
|---|
| 855 | else if (pide_pt_hdr->protocol==HPT_READ) |
|---|
| 856 | memcpy(data, (unsigned char *)buff + 2 * sizeof(HPT_PASS_THROUGH_HEADER), |
|---|
| 857 | pide_pt_hdr->sectors * 512); |
|---|
| 858 | return 0; |
|---|
| 859 | } |
|---|
| 860 | |
|---|
| 861 | |
|---|
| 862 | ///////////////////////////////////////////////////////////////////////////// |
|---|
| 863 | /// Standard SCSI support |
|---|
| 864 | |
|---|
| 865 | class freebsd_scsi_device |
|---|
| 866 | : public /*implements*/ scsi_device, |
|---|
| 867 | public /*extends*/ freebsd_smart_device |
|---|
| 868 | { |
|---|
| 869 | public: |
|---|
| 870 | freebsd_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type); |
|---|
| 871 | |
|---|
| 872 | virtual smart_device * autodetect_open(); |
|---|
| 873 | |
|---|
| 874 | virtual bool scsi_pass_through(scsi_cmnd_io * iop); |
|---|
| 875 | |
|---|
| 876 | virtual bool open(); |
|---|
| 877 | |
|---|
| 878 | virtual bool close(); |
|---|
| 879 | |
|---|
| 880 | private: |
|---|
| 881 | int m_fd; |
|---|
| 882 | struct cam_device *m_camdev; |
|---|
| 883 | }; |
|---|
| 884 | |
|---|
| 885 | bool freebsd_scsi_device::open(){ |
|---|
| 886 | const char *dev = get_dev_name(); |
|---|
| 887 | |
|---|
| 888 | if ((m_camdev = cam_open_device(dev, O_RDWR)) == NULL) { |
|---|
| 889 | set_err(errno); |
|---|
| 890 | return false; |
|---|
| 891 | } |
|---|
| 892 | set_fd(m_camdev->fd); |
|---|
| 893 | return true; |
|---|
| 894 | } |
|---|
| 895 | |
|---|
| 896 | bool freebsd_scsi_device::close(){ |
|---|
| 897 | cam_close_device(m_camdev); |
|---|
| 898 | set_fd(-1); |
|---|
| 899 | return true; |
|---|
| 900 | } |
|---|
| 901 | |
|---|
| 902 | freebsd_scsi_device::freebsd_scsi_device(smart_interface * intf, |
|---|
| 903 | const char * dev_name, const char * req_type) |
|---|
| 904 | : smart_device(intf, dev_name, "scsi", req_type), |
|---|
| 905 | freebsd_smart_device("SCSI") |
|---|
| 906 | { |
|---|
| 907 | } |
|---|
| 908 | |
|---|
| 909 | |
|---|
| 910 | bool freebsd_scsi_device::scsi_pass_through(scsi_cmnd_io * iop) |
|---|
| 911 | { |
|---|
| 912 | int report=scsi_debugmode; |
|---|
| 913 | union ccb *ccb; |
|---|
| 914 | |
|---|
| 915 | if (report > 0) { |
|---|
| 916 | unsigned int k; |
|---|
| 917 | const unsigned char * ucp = iop->cmnd; |
|---|
| 918 | const char * np; |
|---|
| 919 | |
|---|
| 920 | np = scsi_get_opcode_name(ucp[0]); |
|---|
| 921 | pout(" [%s: ", np ? np : "<unknown opcode>"); |
|---|
| 922 | for (k = 0; k < iop->cmnd_len; ++k) |
|---|
| 923 | pout("%02x ", ucp[k]); |
|---|
| 924 | if ((report > 1) && |
|---|
| 925 | (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { |
|---|
| 926 | int trunc = (iop->dxfer_len > 256) ? 1 : 0; |
|---|
| 927 | |
|---|
| 928 | pout("]\n Outgoing data, len=%d%s:\n", (int)iop->dxfer_len, |
|---|
| 929 | (trunc ? " [only first 256 bytes shown]" : "")); |
|---|
| 930 | dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); |
|---|
| 931 | } |
|---|
| 932 | else |
|---|
| 933 | pout("]"); |
|---|
| 934 | } |
|---|
| 935 | |
|---|
| 936 | if(m_camdev==NULL) { |
|---|
| 937 | warnx("error: camdev=0!"); |
|---|
| 938 | return -ENOTTY; |
|---|
| 939 | } |
|---|
| 940 | |
|---|
| 941 | if (!(ccb = cam_getccb(m_camdev))) { |
|---|
| 942 | warnx("error allocating ccb"); |
|---|
| 943 | return -ENOMEM; |
|---|
| 944 | } |
|---|
| 945 | // mfi SAT layer is known to be buggy |
|---|
| 946 | if(!strcmp("mfi",m_camdev->sim_name)) { |
|---|
| 947 | if (iop->cmnd[0] == SAT_ATA_PASSTHROUGH_12 || iop->cmnd[0] == SAT_ATA_PASSTHROUGH_16) { |
|---|
| 948 | // Controller does not return ATA output registers in SAT sense data |
|---|
| 949 | if (iop->cmnd[2] & (1 << 5)) // chk_cond |
|---|
| 950 | return set_err(ENOSYS, "ATA return descriptor not supported by controller firmware"); |
|---|
| 951 | } |
|---|
| 952 | // SMART WRITE LOG SECTOR causing media errors |
|---|
| 953 | if ((iop->cmnd[0] == SAT_ATA_PASSTHROUGH_16 && iop->cmnd[14] == ATA_SMART_CMD |
|---|
| 954 | && iop->cmnd[3]==0 && iop->cmnd[4] == ATA_SMART_WRITE_LOG_SECTOR) || |
|---|
| 955 | (iop->cmnd[0] == SAT_ATA_PASSTHROUGH_12 && iop->cmnd[9] == ATA_SMART_CMD && |
|---|
| 956 | iop->cmnd[3] == ATA_SMART_WRITE_LOG_SECTOR)) |
|---|
| 957 | return set_err(ENOSYS, "SMART WRITE LOG SECTOR command is not supported by controller firmware"); |
|---|
| 958 | } |
|---|
| 959 | |
|---|
| 960 | // clear out structure, except for header that was filled in for us |
|---|
| 961 | bzero(&(&ccb->ccb_h)[1], |
|---|
| 962 | sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); |
|---|
| 963 | |
|---|
| 964 | cam_fill_csio(&ccb->csio, |
|---|
| 965 | /*retrires*/ 1, |
|---|
| 966 | /*cbfcnp*/ NULL, |
|---|
| 967 | /* flags */ (iop->dxfer_dir == DXFER_NONE ? CAM_DIR_NONE :(iop->dxfer_dir == DXFER_FROM_DEVICE ? CAM_DIR_IN : CAM_DIR_OUT)), |
|---|
| 968 | /* tagaction */ MSG_SIMPLE_Q_TAG, |
|---|
| 969 | /* dataptr */ iop->dxferp, |
|---|
| 970 | /* datalen */ iop->dxfer_len, |
|---|
| 971 | /* senselen */ iop->max_sense_len, |
|---|
| 972 | /* cdblen */ iop->cmnd_len, |
|---|
| 973 | /* timout (converted to seconds) */ iop->timeout*1000); |
|---|
| 974 | memcpy(ccb->csio.cdb_io.cdb_bytes,iop->cmnd,iop->cmnd_len); |
|---|
| 975 | |
|---|
| 976 | if (cam_send_ccb(m_camdev,ccb) < 0) { |
|---|
| 977 | warn("error sending SCSI ccb"); |
|---|
| 978 | cam_error_print(m_camdev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr); |
|---|
| 979 | cam_freeccb(ccb); |
|---|
| 980 | return -EIO; |
|---|
| 981 | } |
|---|
| 982 | |
|---|
| 983 | if (((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) && ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_SCSI_STATUS_ERROR)) { |
|---|
| 984 | cam_error_print(m_camdev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr); |
|---|
| 985 | cam_freeccb(ccb); |
|---|
| 986 | return -EIO; |
|---|
| 987 | } |
|---|
| 988 | |
|---|
| 989 | if (iop->sensep) { |
|---|
| 990 | iop->resp_sense_len = ccb->csio.sense_len - ccb->csio.sense_resid; |
|---|
| 991 | memcpy(iop->sensep,&(ccb->csio.sense_data),iop->resp_sense_len); |
|---|
| 992 | } |
|---|
| 993 | |
|---|
| 994 | iop->scsi_status = ccb->csio.scsi_status; |
|---|
| 995 | |
|---|
| 996 | cam_freeccb(ccb); |
|---|
| 997 | |
|---|
| 998 | if (report > 0) { |
|---|
| 999 | int trunc; |
|---|
| 1000 | |
|---|
| 1001 | pout(" status=0\n"); |
|---|
| 1002 | trunc = (iop->dxfer_len > 256) ? 1 : 0; |
|---|
| 1003 | |
|---|
| 1004 | pout(" Incoming data, len=%d%s:\n", (int)iop->dxfer_len, |
|---|
| 1005 | (trunc ? " [only first 256 bytes shown]" : "")); |
|---|
| 1006 | dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); |
|---|
| 1007 | } |
|---|
| 1008 | |
|---|
| 1009 | return true; |
|---|
| 1010 | } |
|---|
| 1011 | |
|---|
| 1012 | |
|---|
| 1013 | ///////////////////////////////////////////////////////////////////////////// |
|---|
| 1014 | /// Areca RAID support |
|---|
| 1015 | |
|---|
| 1016 | /////////////////////////////////////////////////////////////////// |
|---|
| 1017 | // SATA(ATA) device behind Areca RAID Controller |
|---|
| 1018 | class freebsd_areca_ata_device |
|---|
| 1019 | : public /*implements*/ areca_ata_device, |
|---|
| 1020 | public /*extends*/ freebsd_smart_device |
|---|
| 1021 | { |
|---|
| 1022 | public: |
|---|
| 1023 | freebsd_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); |
|---|
| 1024 | virtual smart_device * autodetect_open(); |
|---|
| 1025 | virtual bool arcmsr_lock(); |
|---|
| 1026 | virtual bool arcmsr_unlock(); |
|---|
| 1027 | virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop); |
|---|
| 1028 | }; |
|---|
| 1029 | |
|---|
| 1030 | /////////////////////////////////////////////////////////////////// |
|---|
| 1031 | // SAS(SCSI) device behind Areca RAID Controller |
|---|
| 1032 | class freebsd_areca_scsi_device |
|---|
| 1033 | : public /*implements*/ areca_scsi_device, |
|---|
| 1034 | public /*extends*/ freebsd_smart_device |
|---|
| 1035 | { |
|---|
| 1036 | public: |
|---|
| 1037 | freebsd_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1); |
|---|
| 1038 | virtual smart_device * autodetect_open(); |
|---|
| 1039 | virtual bool arcmsr_lock(); |
|---|
| 1040 | virtual bool arcmsr_unlock(); |
|---|
| 1041 | virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop); |
|---|
| 1042 | }; |
|---|
| 1043 | |
|---|
| 1044 | |
|---|
| 1045 | // Areca RAID Controller(SATA Disk) |
|---|
| 1046 | freebsd_areca_ata_device::freebsd_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) |
|---|
| 1047 | : smart_device(intf, dev_name, "areca", "areca"), |
|---|
| 1048 | freebsd_smart_device("ATA") |
|---|
| 1049 | { |
|---|
| 1050 | set_disknum(disknum); |
|---|
| 1051 | set_encnum(encnum); |
|---|
| 1052 | set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); |
|---|
| 1053 | } |
|---|
| 1054 | |
|---|
| 1055 | |
|---|
| 1056 | smart_device * freebsd_areca_ata_device::autodetect_open() |
|---|
| 1057 | { |
|---|
| 1058 | int is_ata = 1; |
|---|
| 1059 | |
|---|
| 1060 | // autodetect device type |
|---|
| 1061 | is_ata = arcmsr_get_dev_type(); |
|---|
| 1062 | if(is_ata < 0) |
|---|
| 1063 | { |
|---|
| 1064 | set_err(EIO); |
|---|
| 1065 | return this; |
|---|
| 1066 | } |
|---|
| 1067 | |
|---|
| 1068 | if(is_ata == 1) |
|---|
| 1069 | { |
|---|
| 1070 | // SATA device |
|---|
| 1071 | return this; |
|---|
| 1072 | } |
|---|
| 1073 | |
|---|
| 1074 | // SAS device |
|---|
| 1075 | smart_device_auto_ptr newdev(new freebsd_areca_scsi_device(smi(), get_dev_name(), get_disknum(), get_encnum())); |
|---|
| 1076 | close(); |
|---|
| 1077 | delete this; |
|---|
| 1078 | newdev->open(); // TODO: Can possibly pass open fd |
|---|
| 1079 | |
|---|
| 1080 | return newdev.release(); |
|---|
| 1081 | } |
|---|
| 1082 | |
|---|
| 1083 | int freebsd_areca_ata_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop) |
|---|
| 1084 | { |
|---|
| 1085 | int ioctlreturn = 0; |
|---|
| 1086 | |
|---|
| 1087 | if(!is_open()) { |
|---|
| 1088 | if(!open()){ |
|---|
| 1089 | } |
|---|
| 1090 | } |
|---|
| 1091 | |
|---|
| 1092 | ioctlreturn = ioctl(get_fd(), ((sSRB_BUFFER *)(iop->dxferp))->srbioctl.ControlCode, iop->dxferp); |
|---|
| 1093 | if (ioctlreturn) |
|---|
| 1094 | { |
|---|
| 1095 | // errors found |
|---|
| 1096 | return -1; |
|---|
| 1097 | } |
|---|
| 1098 | |
|---|
| 1099 | return ioctlreturn; |
|---|
| 1100 | } |
|---|
| 1101 | |
|---|
| 1102 | bool freebsd_areca_ata_device::arcmsr_lock() |
|---|
| 1103 | { |
|---|
| 1104 | return true; |
|---|
| 1105 | } |
|---|
| 1106 | |
|---|
| 1107 | |
|---|
| 1108 | bool freebsd_areca_ata_device::arcmsr_unlock() |
|---|
| 1109 | { |
|---|
| 1110 | return true; |
|---|
| 1111 | } |
|---|
| 1112 | |
|---|
| 1113 | |
|---|
| 1114 | // Areca RAID Controller(SAS Device) |
|---|
| 1115 | freebsd_areca_scsi_device::freebsd_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum) |
|---|
| 1116 | : smart_device(intf, dev_name, "areca", "areca"), |
|---|
| 1117 | freebsd_smart_device("SCSI") |
|---|
| 1118 | { |
|---|
| 1119 | set_disknum(disknum); |
|---|
| 1120 | set_encnum(encnum); |
|---|
| 1121 | set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum); |
|---|
| 1122 | } |
|---|
| 1123 | |
|---|
| 1124 | smart_device * freebsd_areca_scsi_device::autodetect_open() |
|---|
| 1125 | { |
|---|
| 1126 | return this; |
|---|
| 1127 | } |
|---|
| 1128 | |
|---|
| 1129 | int freebsd_areca_scsi_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop) |
|---|
| 1130 | { |
|---|
| 1131 | int ioctlreturn = 0; |
|---|
| 1132 | |
|---|
| 1133 | if(!is_open()) { |
|---|
| 1134 | if(!open()){ |
|---|
| 1135 | } |
|---|
| 1136 | } |
|---|
| 1137 | ioctlreturn = ioctl(get_fd(), ((sSRB_BUFFER *)(iop->dxferp))->srbioctl.ControlCode, iop->dxferp); |
|---|
| 1138 | if (ioctlreturn) |
|---|
| 1139 | { |
|---|
| 1140 | // errors found |
|---|
| 1141 | return -1; |
|---|
| 1142 | } |
|---|
| 1143 | |
|---|
| 1144 | return ioctlreturn; |
|---|
| 1145 | } |
|---|
| 1146 | |
|---|
| 1147 | bool freebsd_areca_scsi_device::arcmsr_lock() |
|---|
| 1148 | { |
|---|
| 1149 | return true; |
|---|
| 1150 | } |
|---|
| 1151 | |
|---|
| 1152 | |
|---|
| 1153 | bool freebsd_areca_scsi_device::arcmsr_unlock() |
|---|
| 1154 | { |
|---|
| 1155 | return true; |
|---|
| 1156 | } |
|---|
| 1157 | |
|---|
| 1158 | |
|---|
| 1159 | ///////////////////////////////////////////////////////////////////////////// |
|---|
| 1160 | /// Implement CCISS RAID support with old functions |
|---|
| 1161 | |
|---|
| 1162 | class freebsd_cciss_device |
|---|
| 1163 | : public /*implements*/ scsi_device, |
|---|
| 1164 | public /*extends*/ freebsd_smart_device |
|---|
| 1165 | { |
|---|
| 1166 | public: |
|---|
| 1167 | freebsd_cciss_device(smart_interface * intf, const char * name, unsigned char disknum); |
|---|
| 1168 | |
|---|
| 1169 | virtual bool scsi_pass_through(scsi_cmnd_io * iop); |
|---|
| 1170 | virtual bool open(); |
|---|
| 1171 | |
|---|
| 1172 | private: |
|---|
| 1173 | unsigned char m_disknum; ///< Disk number. |
|---|
| 1174 | }; |
|---|
| 1175 | |
|---|
| 1176 | bool freebsd_cciss_device::open() |
|---|
| 1177 | { |
|---|
| 1178 | const char *dev = get_dev_name(); |
|---|
| 1179 | int fd; |
|---|
| 1180 | if ((fd = ::open(dev,O_RDWR))<0) { |
|---|
| 1181 | set_err(errno); |
|---|
| 1182 | return false; |
|---|
| 1183 | } |
|---|
| 1184 | set_fd(fd); |
|---|
| 1185 | return true; |
|---|
| 1186 | } |
|---|
| 1187 | |
|---|
| 1188 | freebsd_cciss_device::freebsd_cciss_device(smart_interface * intf, |
|---|
| 1189 | const char * dev_name, unsigned char disknum) |
|---|
| 1190 | : smart_device(intf, dev_name, "cciss", "cciss"), |
|---|
| 1191 | freebsd_smart_device("SCSI"), |
|---|
| 1192 | m_disknum(disknum) |
|---|
| 1193 | { |
|---|
| 1194 | set_info().info_name = strprintf("%s [cciss_disk_%02d]", dev_name, disknum); |
|---|
| 1195 | } |
|---|
| 1196 | |
|---|
| 1197 | bool freebsd_cciss_device::scsi_pass_through(scsi_cmnd_io * iop) |
|---|
| 1198 | { |
|---|
| 1199 | int status = cciss_io_interface(get_fd(), m_disknum, iop, scsi_debugmode); |
|---|
| 1200 | if (status < 0) |
|---|
| 1201 | return set_err(-status); |
|---|
| 1202 | return true; |
|---|
| 1203 | // not reached |
|---|
| 1204 | return true; |
|---|
| 1205 | } |
|---|
| 1206 | |
|---|
| 1207 | |
|---|
| 1208 | ///////////////////////////////////////////////////////////////////////////// |
|---|
| 1209 | /// SCSI open with autodetection support |
|---|
| 1210 | |
|---|
| 1211 | smart_device * freebsd_scsi_device::autodetect_open() |
|---|
| 1212 | { |
|---|
| 1213 | // Open device |
|---|
| 1214 | if (!open()) |
|---|
| 1215 | return this; |
|---|
| 1216 | |
|---|
| 1217 | // No Autodetection if device type was specified by user |
|---|
| 1218 | if (*get_req_type()) |
|---|
| 1219 | return this; |
|---|
| 1220 | |
|---|
| 1221 | // The code below is based on smartd.cpp:SCSIFilterKnown() |
|---|
| 1222 | |
|---|
| 1223 | // Get INQUIRY |
|---|
| 1224 | unsigned char req_buff[64] = {0, }; |
|---|
| 1225 | int req_len = 36; |
|---|
| 1226 | if (scsiStdInquiry(this, req_buff, req_len)) { |
|---|
| 1227 | // Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices |
|---|
| 1228 | // watch this spot ... other devices could lock up here |
|---|
| 1229 | req_len = 64; |
|---|
| 1230 | if (scsiStdInquiry(this, req_buff, req_len)) { |
|---|
| 1231 | // device doesn't like INQUIRY commands |
|---|
| 1232 | close(); |
|---|
| 1233 | set_err(EIO, "INQUIRY failed"); |
|---|
| 1234 | return this; |
|---|
| 1235 | } |
|---|
| 1236 | } |
|---|
| 1237 | |
|---|
| 1238 | int avail_len = req_buff[4] + 5; |
|---|
| 1239 | int len = (avail_len < req_len ? avail_len : req_len); |
|---|
| 1240 | if (len < 36) |
|---|
| 1241 | return this; |
|---|
| 1242 | |
|---|
| 1243 | // Use INQUIRY to detect type |
|---|
| 1244 | |
|---|
| 1245 | // 3ware ? |
|---|
| 1246 | if (!memcmp(req_buff + 8, "3ware", 5) || !memcmp(req_buff + 8, "AMCC", 4)) { |
|---|
| 1247 | close(); |
|---|
| 1248 | set_err(EINVAL, "AMCC/3ware controller, please try adding '-d 3ware,N',\n" |
|---|
| 1249 | "you may need to replace %s with /dev/twaN, /dev/tweN or /dev/twsN", get_dev_name()); |
|---|
| 1250 | return this; |
|---|
| 1251 | } |
|---|
| 1252 | |
|---|
| 1253 | // SAT or USB, skip MFI controllers because of bugs |
|---|
| 1254 | { |
|---|
| 1255 | smart_device * newdev = smi()->autodetect_sat_device(this, req_buff, len); |
|---|
| 1256 | if (newdev) { |
|---|
| 1257 | // NOTE: 'this' is now owned by '*newdev' |
|---|
| 1258 | if(!strcmp("mfi",m_camdev->sim_name)) { |
|---|
| 1259 | newdev->close(); |
|---|
| 1260 | newdev->set_err(ENOSYS, "SATA device detected,\n" |
|---|
| 1261 | "MegaRAID SAT layer is reportedly buggy, use '-d sat' to try anyhow"); |
|---|
| 1262 | } |
|---|
| 1263 | return newdev; |
|---|
| 1264 | } |
|---|
| 1265 | } |
|---|
| 1266 | |
|---|
| 1267 | // Nothing special found |
|---|
| 1268 | return this; |
|---|
| 1269 | } |
|---|
| 1270 | |
|---|
| 1271 | |
|---|
| 1272 | ///////////////////////////////////////////////////////////////////////////// |
|---|
| 1273 | /// Implement platform interface with old functions. |
|---|
| 1274 | |
|---|
| 1275 | class freebsd_smart_interface |
|---|
| 1276 | : public /*implements*/ smart_interface |
|---|
| 1277 | { |
|---|
| 1278 | public: |
|---|
| 1279 | virtual std::string get_os_version_str(); |
|---|
| 1280 | |
|---|
| 1281 | virtual std::string get_app_examples(const char * appname); |
|---|
| 1282 | |
|---|
| 1283 | virtual bool scan_smart_devices(smart_device_list & devlist, const char * type, |
|---|
| 1284 | const char * pattern = 0); |
|---|
| 1285 | |
|---|
| 1286 | protected: |
|---|
| 1287 | virtual ata_device * get_ata_device(const char * name, const char * type); |
|---|
| 1288 | |
|---|
| 1289 | #if FREEBSDVER > 800100 |
|---|
| 1290 | virtual ata_device * get_atacam_device(const char * name, const char * type); |
|---|
| 1291 | #endif |
|---|
| 1292 | |
|---|
| 1293 | virtual scsi_device * get_scsi_device(const char * name, const char * type); |
|---|
| 1294 | |
|---|
| 1295 | virtual smart_device * autodetect_smart_device(const char * name); |
|---|
| 1296 | |
|---|
| 1297 | virtual smart_device * get_custom_smart_device(const char * name, const char * type); |
|---|
| 1298 | |
|---|
| 1299 | virtual std::string get_valid_custom_dev_types_str(); |
|---|
| 1300 | }; |
|---|
| 1301 | |
|---|
| 1302 | |
|---|
| 1303 | ////////////////////////////////////////////////////////////////////// |
|---|
| 1304 | |
|---|
| 1305 | std::string freebsd_smart_interface::get_os_version_str() |
|---|
| 1306 | { |
|---|
| 1307 | struct utsname osname; |
|---|
| 1308 | uname(&osname); |
|---|
| 1309 | return strprintf("%s %s %s", osname.sysname, osname.release, osname.machine); |
|---|
| 1310 | } |
|---|
| 1311 | |
|---|
| 1312 | std::string freebsd_smart_interface::get_app_examples(const char * appname) |
|---|
| 1313 | { |
|---|
| 1314 | if (!strcmp(appname, "smartctl")) |
|---|
| 1315 | return smartctl_examples; |
|---|
| 1316 | return ""; |
|---|
| 1317 | } |
|---|
| 1318 | |
|---|
| 1319 | ata_device * freebsd_smart_interface::get_ata_device(const char * name, const char * type) |
|---|
| 1320 | { |
|---|
| 1321 | return new freebsd_ata_device(this, name, type); |
|---|
| 1322 | } |
|---|
| 1323 | |
|---|
| 1324 | #if FREEBSDVER > 800100 |
|---|
| 1325 | ata_device * freebsd_smart_interface::get_atacam_device(const char * name, const char * type) |
|---|
| 1326 | { |
|---|
| 1327 | return new freebsd_atacam_device(this, name, type); |
|---|
| 1328 | } |
|---|
| 1329 | #endif |
|---|
| 1330 | |
|---|
| 1331 | scsi_device * freebsd_smart_interface::get_scsi_device(const char * name, const char * type) |
|---|
| 1332 | { |
|---|
| 1333 | return new freebsd_scsi_device(this, name, type); |
|---|
| 1334 | } |
|---|
| 1335 | |
|---|
| 1336 | // we are using CAM subsystem XPT enumerator to found all CAM (scsi/usb/ada/...) |
|---|
| 1337 | // devices on system despite of it's names |
|---|
| 1338 | // |
|---|
| 1339 | // If any errors occur, leave errno set as it was returned by the |
|---|
| 1340 | // system call, and return <0. |
|---|
| 1341 | // |
|---|
| 1342 | // arguments: |
|---|
| 1343 | // names: resulting array |
|---|
| 1344 | // show_all - export duplicate device name or not |
|---|
| 1345 | // |
|---|
| 1346 | // Return values: |
|---|
| 1347 | // -1: error |
|---|
| 1348 | // >=0: number of discovered devices |
|---|
| 1349 | |
|---|
| 1350 | bool get_dev_names_cam(std::vector<std::string> & names, bool show_all) |
|---|
| 1351 | { |
|---|
| 1352 | int fd; |
|---|
| 1353 | if ((fd = open(XPT_DEVICE, O_RDWR)) == -1) { |
|---|
| 1354 | if (errno == ENOENT) /* There are no CAM device on this computer */ |
|---|
| 1355 | return 0; |
|---|
| 1356 | int serrno = errno; |
|---|
| 1357 | pout("%s control device couldn't opened: %s\n", XPT_DEVICE, strerror(errno)); |
|---|
| 1358 | errno = serrno; |
|---|
| 1359 | return false; |
|---|
| 1360 | } |
|---|
| 1361 | |
|---|
| 1362 | union ccb ccb; |
|---|
| 1363 | bzero(&ccb, sizeof(union ccb)); |
|---|
| 1364 | |
|---|
| 1365 | ccb.ccb_h.path_id = CAM_XPT_PATH_ID; |
|---|
| 1366 | ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; |
|---|
| 1367 | ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; |
|---|
| 1368 | |
|---|
| 1369 | ccb.ccb_h.func_code = XPT_DEV_MATCH; |
|---|
| 1370 | int bufsize = sizeof(struct dev_match_result) * MAX_NUM_DEV; |
|---|
| 1371 | ccb.cdm.match_buf_len = bufsize; |
|---|
| 1372 | // TODO: Use local buffer instead of malloc() if possible |
|---|
| 1373 | ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize); |
|---|
| 1374 | bzero(ccb.cdm.matches,bufsize); // clear ccb.cdm.matches structure |
|---|
| 1375 | |
|---|
| 1376 | if (ccb.cdm.matches == NULL) { |
|---|
| 1377 | close(fd); |
|---|
| 1378 | throw std::bad_alloc(); |
|---|
| 1379 | } |
|---|
| 1380 | ccb.cdm.num_matches = 0; |
|---|
| 1381 | ccb.cdm.num_patterns = 0; |
|---|
| 1382 | ccb.cdm.pattern_buf_len = 0; |
|---|
| 1383 | |
|---|
| 1384 | /* |
|---|
| 1385 | * We do the ioctl multiple times if necessary, in case there are |
|---|
| 1386 | * more than MAX_NUM_DEV nodes in the EDT. |
|---|
| 1387 | */ |
|---|
| 1388 | int skip_device = 0, skip_bus = 0, changed = 0; // TODO: bool |
|---|
| 1389 | std::string devname; |
|---|
| 1390 | do { |
|---|
| 1391 | if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) { |
|---|
| 1392 | int serrno = errno; |
|---|
| 1393 | pout("error sending CAMIOCOMMAND ioctl: %s\n", strerror(errno)); |
|---|
| 1394 | free(ccb.cdm.matches); |
|---|
| 1395 | close(fd); |
|---|
| 1396 | errno = serrno; |
|---|
| 1397 | return false; |
|---|
| 1398 | } |
|---|
| 1399 | |
|---|
| 1400 | if ((ccb.ccb_h.status != CAM_REQ_CMP) |
|---|
| 1401 | || ((ccb.cdm.status != CAM_DEV_MATCH_LAST) |
|---|
| 1402 | && (ccb.cdm.status != CAM_DEV_MATCH_MORE))) { |
|---|
| 1403 | pout("got CAM error %#x, CDM error %d\n", ccb.ccb_h.status, ccb.cdm.status); |
|---|
| 1404 | free(ccb.cdm.matches); |
|---|
| 1405 | close(fd); |
|---|
| 1406 | errno = ENXIO; |
|---|
| 1407 | return false; |
|---|
| 1408 | } |
|---|
| 1409 | |
|---|
| 1410 | for (unsigned i = 0; i < ccb.cdm.num_matches; i++) { |
|---|
| 1411 | struct bus_match_result *bus_result; |
|---|
| 1412 | struct device_match_result *dev_result; |
|---|
| 1413 | struct periph_match_result *periph_result; |
|---|
| 1414 | |
|---|
| 1415 | if (ccb.cdm.matches[i].type == DEV_MATCH_BUS) { |
|---|
| 1416 | bus_result = &ccb.cdm.matches[i].result.bus_result; |
|---|
| 1417 | |
|---|
| 1418 | if (strcmp(bus_result->dev_name,"xpt") == 0) /* skip XPT bus at all */ |
|---|
| 1419 | skip_bus = 1; |
|---|
| 1420 | else |
|---|
| 1421 | skip_bus = 0; |
|---|
| 1422 | changed = 1; |
|---|
| 1423 | } else if (ccb.cdm.matches[i].type == DEV_MATCH_DEVICE) { |
|---|
| 1424 | dev_result = &ccb.cdm.matches[i].result.device_result; |
|---|
| 1425 | |
|---|
| 1426 | if (dev_result->flags & DEV_RESULT_UNCONFIGURED || skip_bus == 1) |
|---|
| 1427 | skip_device = 1; |
|---|
| 1428 | else |
|---|
| 1429 | skip_device = 0; |
|---|
| 1430 | |
|---|
| 1431 | // /* Shall we skip non T_DIRECT devices ? */ |
|---|
| 1432 | // if (dev_result->inq_data.device != T_DIRECT) |
|---|
| 1433 | // skip_device = 1; |
|---|
| 1434 | changed = 1; |
|---|
| 1435 | } else if (ccb.cdm.matches[i].type == DEV_MATCH_PERIPH && |
|---|
| 1436 | (skip_device == 0 || show_all)) { |
|---|
| 1437 | /* One device may be populated as many peripherals (pass0 & da0 for example). |
|---|
| 1438 | * We are searching for latest name |
|---|
| 1439 | */ |
|---|
| 1440 | periph_result = &ccb.cdm.matches[i].result.periph_result; |
|---|
| 1441 | devname = strprintf("%s%s%d", _PATH_DEV, periph_result->periph_name, periph_result->unit_number); |
|---|
| 1442 | changed = 0; |
|---|
| 1443 | }; |
|---|
| 1444 | if ((changed == 1 || show_all) && !devname.empty()) { |
|---|
| 1445 | names.push_back(devname); |
|---|
| 1446 | devname.erase(); |
|---|
| 1447 | changed = 0; |
|---|
| 1448 | }; |
|---|
| 1449 | } |
|---|
| 1450 | |
|---|
| 1451 | } while ((ccb.ccb_h.status == CAM_REQ_CMP) && (ccb.cdm.status == CAM_DEV_MATCH_MORE)); |
|---|
| 1452 | |
|---|
| 1453 | if (!devname.empty()) |
|---|
| 1454 | names.push_back(devname); |
|---|
| 1455 | |
|---|
| 1456 | free(ccb.cdm.matches); |
|---|
| 1457 | close(fd); |
|---|
| 1458 | return true; |
|---|
| 1459 | } |
|---|
| 1460 | |
|---|
| 1461 | // we are using ATA subsystem enumerator to found all ATA devices on system |
|---|
| 1462 | // despite of it's names |
|---|
| 1463 | // |
|---|
| 1464 | // If any errors occur, leave errno set as it was returned by the |
|---|
| 1465 | // system call, and return <0. |
|---|
| 1466 | |
|---|
| 1467 | // Return values: |
|---|
| 1468 | // -1: error |
|---|
| 1469 | // >=0: number of discovered devices |
|---|
| 1470 | int get_dev_names_ata(char*** names) { |
|---|
| 1471 | struct ata_ioc_devices devices; |
|---|
| 1472 | int fd=-1,maxchannel,serrno=-1,n=0; |
|---|
| 1473 | char **mp = NULL; |
|---|
| 1474 | |
|---|
| 1475 | *names=NULL; |
|---|
| 1476 | |
|---|
| 1477 | if ((fd = open(ATA_DEVICE, O_RDWR)) < 0) { |
|---|
| 1478 | if (errno == ENOENT) /* There are no ATA device on this computer */ |
|---|
| 1479 | return 0; |
|---|
| 1480 | serrno = errno; |
|---|
| 1481 | pout("%s control device can't be opened: %s\n", ATA_DEVICE, strerror(errno)); |
|---|
| 1482 | n = -1; |
|---|
| 1483 | goto end; |
|---|
| 1484 | }; |
|---|
| 1485 | |
|---|
| 1486 | if (ioctl(fd, IOCATAGMAXCHANNEL, &maxchannel) < 0) { |
|---|
| 1487 | serrno = errno; |
|---|
| 1488 | pout("ioctl(IOCATAGMAXCHANNEL) on /dev/ata failed: %s\n", strerror(errno)); |
|---|
| 1489 | n = -1; |
|---|
| 1490 | goto end; |
|---|
| 1491 | }; |
|---|
| 1492 | |
|---|
| 1493 | // allocate space for up to MAX_NUM_DEV number of ATA devices |
|---|
| 1494 | mp = (char **)calloc(MAX_NUM_DEV, sizeof(char*)); |
|---|
| 1495 | if (mp == NULL) { |
|---|
| 1496 | serrno=errno; |
|---|
| 1497 | pout("Out of memory constructing scan device list (on line %d)\n", __LINE__); |
|---|
| 1498 | n = -1; |
|---|
| 1499 | goto end; |
|---|
| 1500 | }; |
|---|
| 1501 | |
|---|
| 1502 | for (devices.channel = 0; devices.channel < maxchannel && n < MAX_NUM_DEV; devices.channel++) { |
|---|
| 1503 | int j; |
|---|
| 1504 | |
|---|
| 1505 | if (ioctl(fd, IOCATADEVICES, &devices) < 0) { |
|---|
| 1506 | if (errno == ENXIO) |
|---|
| 1507 | continue; /* such channel not exist */ |
|---|
| 1508 | pout("ioctl(IOCATADEVICES) on %s channel %d failed: %s\n", ATA_DEVICE, devices.channel, strerror(errno)); |
|---|
| 1509 | n = -1; |
|---|
| 1510 | goto end; |
|---|
| 1511 | }; |
|---|
| 1512 | for (j=0;j<=1 && n<MAX_NUM_DEV;j++) { |
|---|
| 1513 | if (devices.name[j][0] != '\0') { |
|---|
| 1514 | asprintf(mp+n, "%s%s", _PATH_DEV, devices.name[j]); |
|---|
| 1515 | if (mp[n] == NULL) { |
|---|
| 1516 | pout("Out of memory constructing scan ATA device list (on line %d)\n", __LINE__); |
|---|
| 1517 | n = -1; |
|---|
| 1518 | goto end; |
|---|
| 1519 | }; |
|---|
| 1520 | bytes+=1+strlen(mp[n]); |
|---|
| 1521 | n++; |
|---|
| 1522 | }; |
|---|
| 1523 | }; |
|---|
| 1524 | }; |
|---|
| 1525 | mp = (char **)reallocf(mp,n*(sizeof (char*))); // shrink to correct size |
|---|
| 1526 | if (mp == NULL && n > 0 ) { // reallocf never fail for size=0, but may return NULL |
|---|
| 1527 | serrno=errno; |
|---|
| 1528 | pout("Out of memory constructing scan device list (on line %d)\n", __LINE__); |
|---|
| 1529 | n = -1; |
|---|
| 1530 | goto end; |
|---|
| 1531 | }; |
|---|
| 1532 | bytes += (n)*(sizeof(char*)); // and set allocated byte count |
|---|
| 1533 | |
|---|
| 1534 | end: |
|---|
| 1535 | if (fd>=0) |
|---|
| 1536 | close(fd); |
|---|
| 1537 | if (n <= 0) { |
|---|
| 1538 | free(mp); |
|---|
| 1539 | mp = NULL; |
|---|
| 1540 | } |
|---|
| 1541 | |
|---|
| 1542 | *names=mp; |
|---|
| 1543 | |
|---|
| 1544 | if (serrno>-1) |
|---|
| 1545 | errno=serrno; |
|---|
| 1546 | return n; |
|---|
| 1547 | } |
|---|
| 1548 | |
|---|
| 1549 | |
|---|
| 1550 | |
|---|
| 1551 | bool freebsd_smart_interface::scan_smart_devices(smart_device_list & devlist, |
|---|
| 1552 | const char * type, const char * pattern /*= 0*/) |
|---|
| 1553 | { |
|---|
| 1554 | if (pattern) { |
|---|
| 1555 | set_err(EINVAL, "DEVICESCAN with pattern not implemented yet"); |
|---|
| 1556 | return false; |
|---|
| 1557 | } |
|---|
| 1558 | |
|---|
| 1559 | // Make namelists |
|---|
| 1560 | char * * atanames = 0; int numata = 0; |
|---|
| 1561 | if (!type || !strcmp(type, "ata")) { |
|---|
| 1562 | numata = get_dev_names_ata(&atanames); |
|---|
| 1563 | if (numata < 0) { |
|---|
| 1564 | set_err(ENOMEM); |
|---|
| 1565 | return false; |
|---|
| 1566 | } |
|---|
| 1567 | } |
|---|
| 1568 | |
|---|
| 1569 | std::vector<std::string> scsinames; |
|---|
| 1570 | if (!type || !strcmp(type, "scsi")) { // do not export duplicated names |
|---|
| 1571 | if (!get_dev_names_cam(scsinames, false)) { |
|---|
| 1572 | set_err(errno); |
|---|
| 1573 | return false; |
|---|
| 1574 | } |
|---|
| 1575 | } |
|---|
| 1576 | |
|---|
| 1577 | // Add to devlist |
|---|
| 1578 | int i; |
|---|
| 1579 | if (type==NULL) |
|---|
| 1580 | type=""; |
|---|
| 1581 | for (i = 0; i < numata; i++) { |
|---|
| 1582 | ata_device * atadev = get_ata_device(atanames[i], type); |
|---|
| 1583 | if (atadev) |
|---|
| 1584 | devlist.push_back(atadev); |
|---|
| 1585 | free(atanames[i]); |
|---|
| 1586 | } |
|---|
| 1587 | if(numata) free(atanames); |
|---|
| 1588 | |
|---|
| 1589 | for (i = 0; i < (int)scsinames.size(); i++) { |
|---|
| 1590 | if(!*type) { // try USB autodetection if no type specified |
|---|
| 1591 | smart_device * smartdev = autodetect_smart_device(scsinames[i].c_str()); |
|---|
| 1592 | if(smartdev) |
|---|
| 1593 | devlist.push_back(smartdev); |
|---|
| 1594 | } |
|---|
| 1595 | else { |
|---|
| 1596 | scsi_device * scsidev = get_scsi_device(scsinames[i].c_str(), type); |
|---|
| 1597 | if (scsidev) |
|---|
| 1598 | devlist.push_back(scsidev); |
|---|
| 1599 | } |
|---|
| 1600 | } |
|---|
| 1601 | return true; |
|---|
| 1602 | } |
|---|
| 1603 | |
|---|
| 1604 | |
|---|
| 1605 | #if (FREEBSDVER < 800000) // without this build fail on FreeBSD 8 |
|---|
| 1606 | static char done[USB_MAX_DEVICES]; |
|---|
| 1607 | |
|---|
| 1608 | static int usbdevinfo(int f, int a, int rec, int busno, unsigned short & vendor_id, |
|---|
| 1609 | unsigned short & product_id, unsigned short & version) |
|---|
| 1610 | { |
|---|
| 1611 | |
|---|
| 1612 | struct usb_device_info di; |
|---|
| 1613 | int e, p, i; |
|---|
| 1614 | char devname[256]; |
|---|
| 1615 | |
|---|
| 1616 | snprintf(devname, sizeof(devname),"umass%d",busno); |
|---|
| 1617 | |
|---|
| 1618 | di.udi_addr = a; |
|---|
| 1619 | e = ioctl(f, USB_DEVICEINFO, &di); |
|---|
| 1620 | if (e) { |
|---|
| 1621 | if (errno != ENXIO) |
|---|
| 1622 | printf("addr %d: I/O error\n", a); |
|---|
| 1623 | return 0; |
|---|
| 1624 | } |
|---|
| 1625 | done[a] = 1; |
|---|
| 1626 | |
|---|
| 1627 | // list devices |
|---|
| 1628 | for (i = 0; i < USB_MAX_DEVNAMES; i++) { |
|---|
| 1629 | if (di.udi_devnames[i][0]) { |
|---|
| 1630 | if(strcmp(di.udi_devnames[i],devname)==0) { |
|---|
| 1631 | // device found! |
|---|
| 1632 | vendor_id = di.udi_vendorNo; |
|---|
| 1633 | product_id = di.udi_productNo; |
|---|
| 1634 | version = di.udi_releaseNo; |
|---|
| 1635 | return 1; |
|---|
| 1636 | // FIXME |
|---|
| 1637 | } |
|---|
| 1638 | } |
|---|
| 1639 | } |
|---|
| 1640 | if (!rec) |
|---|
| 1641 | return 0; |
|---|
| 1642 | for (p = 0; p < di.udi_nports; p++) { |
|---|
| 1643 | int s = di.udi_ports[p]; |
|---|
| 1644 | if (s >= USB_MAX_DEVICES) { |
|---|
| 1645 | continue; |
|---|
| 1646 | } |
|---|
| 1647 | if (s == 0) |
|---|
| 1648 | printf("addr 0 should never happen!\n"); |
|---|
| 1649 | else { |
|---|
| 1650 | if(usbdevinfo(f, s, 1, busno, vendor_id, product_id, version)) return 1; |
|---|
| 1651 | } |
|---|
| 1652 | } |
|---|
| 1653 | return 0; |
|---|
| 1654 | } |
|---|
| 1655 | #endif |
|---|
| 1656 | |
|---|
| 1657 | |
|---|
| 1658 | static int usbdevlist(int busno,unsigned short & vendor_id, |
|---|
| 1659 | unsigned short & product_id, unsigned short & version) |
|---|
| 1660 | { |
|---|
| 1661 | #if (FREEBSDVER >= 800000) // libusb2 interface |
|---|
| 1662 | struct libusb20_device *pdev = NULL; |
|---|
| 1663 | struct libusb20_backend *pbe; |
|---|
| 1664 | uint32_t matches = 0; |
|---|
| 1665 | char buf[128]; // do not change! |
|---|
| 1666 | char devname[128]; |
|---|
| 1667 | uint8_t n; |
|---|
| 1668 | struct LIBUSB20_DEVICE_DESC_DECODED *pdesc; |
|---|
| 1669 | |
|---|
| 1670 | pbe = libusb20_be_alloc_default(); |
|---|
| 1671 | |
|---|
| 1672 | while ((pdev = libusb20_be_device_foreach(pbe, pdev))) { |
|---|
| 1673 | matches++; |
|---|
| 1674 | |
|---|
| 1675 | if (libusb20_dev_open(pdev, 0)) { |
|---|
| 1676 | warnx("libusb20_dev_open: could not open device"); |
|---|
| 1677 | return 0; |
|---|
| 1678 | } |
|---|
| 1679 | |
|---|
| 1680 | pdesc=libusb20_dev_get_device_desc(pdev); |
|---|
| 1681 | |
|---|
| 1682 | snprintf(devname, sizeof(devname),"umass%d:",busno); |
|---|
| 1683 | for (n = 0; n != 255; n++) { |
|---|
| 1684 | if (libusb20_dev_get_iface_desc(pdev, n, buf, sizeof(buf))) |
|---|
| 1685 | break; |
|---|
| 1686 | if (buf[0] == 0) |
|---|
| 1687 | continue; |
|---|
| 1688 | if(strncmp(buf,devname,strlen(devname))==0){ |
|---|
| 1689 | // found! |
|---|
| 1690 | vendor_id = pdesc->idVendor; |
|---|
| 1691 | product_id = pdesc->idProduct; |
|---|
| 1692 | version = pdesc->bcdDevice; |
|---|
| 1693 | libusb20_dev_close(pdev); |
|---|
| 1694 | libusb20_be_free(pbe); |
|---|
| 1695 | return 1; |
|---|
| 1696 | } |
|---|
| 1697 | } |
|---|
| 1698 | |
|---|
| 1699 | libusb20_dev_close(pdev); |
|---|
| 1700 | } |
|---|
| 1701 | |
|---|
| 1702 | if (matches == 0) { |
|---|
| 1703 | printf("No device match or lack of permissions.\n"); |
|---|
| 1704 | } |
|---|
| 1705 | |
|---|
| 1706 | libusb20_be_free(pbe); |
|---|
| 1707 | |
|---|
| 1708 | return false; |
|---|
| 1709 | #else // freebsd < 8.0 USB stack, ioctl interface |
|---|
| 1710 | |
|---|
| 1711 | int i, f, a, rc; |
|---|
| 1712 | char buf[50]; |
|---|
| 1713 | int ncont; |
|---|
| 1714 | |
|---|
| 1715 | for (ncont = 0, i = 0; i < 10; i++) { |
|---|
| 1716 | snprintf(buf, sizeof(buf), "%s%d", USBDEV, i); |
|---|
| 1717 | f = open(buf, O_RDONLY); |
|---|
| 1718 | if (f >= 0) { |
|---|
| 1719 | memset(done, 0, sizeof done); |
|---|
| 1720 | for (a = 1; a < USB_MAX_DEVICES; a++) { |
|---|
| 1721 | if (!done[a]) { |
|---|
| 1722 | rc = usbdevinfo(f, a, 1, busno,vendor_id, product_id, version); |
|---|
| 1723 | if(rc) return 1; |
|---|
| 1724 | } |
|---|
| 1725 | |
|---|
| 1726 | } |
|---|
| 1727 | close(f); |
|---|
| 1728 | } else { |
|---|
| 1729 | if (errno == ENOENT || errno == ENXIO) |
|---|
| 1730 | continue; |
|---|
| 1731 | warn("%s", buf); |
|---|
| 1732 | } |
|---|
| 1733 | ncont++; |
|---|
| 1734 | } |
|---|
| 1735 | return 0; |
|---|
| 1736 | #endif |
|---|
| 1737 | } |
|---|
| 1738 | |
|---|
| 1739 | smart_device * freebsd_smart_interface::autodetect_smart_device(const char * name) |
|---|
| 1740 | { |
|---|
| 1741 | unsigned short vendor_id = 0, product_id = 0, version = 0; |
|---|
| 1742 | struct cam_device *cam_dev; |
|---|
| 1743 | union ccb ccb; |
|---|
| 1744 | int bus=-1; |
|---|
| 1745 | int i,c; |
|---|
| 1746 | int len; |
|---|
| 1747 | const char * test_name = name; |
|---|
| 1748 | |
|---|
| 1749 | // if dev_name null, or string length zero |
|---|
| 1750 | if (!name || !(len = strlen(name))) |
|---|
| 1751 | return 0; |
|---|
| 1752 | |
|---|
| 1753 | // Dereference symlinks |
|---|
| 1754 | struct stat st; |
|---|
| 1755 | std::string pathbuf; |
|---|
| 1756 | if (!lstat(name, &st) && S_ISLNK(st.st_mode)) { |
|---|
| 1757 | char * p = realpath(name, (char *)0); |
|---|
| 1758 | if (p) { |
|---|
| 1759 | pathbuf = p; |
|---|
| 1760 | free(p); |
|---|
| 1761 | test_name = pathbuf.c_str(); |
|---|
| 1762 | } |
|---|
| 1763 | } |
|---|
| 1764 | |
|---|
| 1765 | // check ATA bus |
|---|
| 1766 | char * * atanames = 0; int numata = 0; |
|---|
| 1767 | numata = get_dev_names_ata(&atanames); |
|---|
| 1768 | if (numata > 0) { |
|---|
| 1769 | // check ATA/ATAPI devices |
|---|
| 1770 | for (i = 0; i < numata; i++) { |
|---|
| 1771 | if(!strcmp(atanames[i],test_name)) { |
|---|
| 1772 | for (c = i; c < numata; c++) free(atanames[c]); |
|---|
| 1773 | free(atanames); |
|---|
| 1774 | return new freebsd_ata_device(this, test_name, ""); |
|---|
| 1775 | } |
|---|
| 1776 | else free(atanames[i]); |
|---|
| 1777 | } |
|---|
| 1778 | if(numata) free(atanames); |
|---|
| 1779 | } |
|---|
| 1780 | else { |
|---|
| 1781 | if (numata < 0) |
|---|
| 1782 | pout("Unable to get ATA device list\n"); |
|---|
| 1783 | } |
|---|
| 1784 | |
|---|
| 1785 | // check CAM |
|---|
| 1786 | std::vector<std::string> scsinames; |
|---|
| 1787 | if (!get_dev_names_cam(scsinames, true)) |
|---|
| 1788 | pout("Unable to get CAM device list\n"); |
|---|
| 1789 | else if (!scsinames.empty()) { |
|---|
| 1790 | // check all devices on CAM bus |
|---|
| 1791 | for (i = 0; i < (int)scsinames.size(); i++) { |
|---|
| 1792 | if(strcmp(scsinames[i].c_str(), test_name)==0) |
|---|
| 1793 | { // our disk device is CAM |
|---|
| 1794 | if ((cam_dev = cam_open_device(test_name, O_RDWR)) == NULL) { |
|---|
| 1795 | // open failure |
|---|
| 1796 | set_err(errno); |
|---|
| 1797 | return 0; |
|---|
| 1798 | } |
|---|
| 1799 | // zero the payload |
|---|
| 1800 | bzero(&(&ccb.ccb_h)[1], PATHINQ_SETTINGS_SIZE); |
|---|
| 1801 | ccb.ccb_h.func_code = XPT_PATH_INQ; // send PATH_INQ to the device |
|---|
| 1802 | if (ioctl(cam_dev->fd, CAMIOCOMMAND, &ccb) == -1) { |
|---|
| 1803 | warn("Get Transfer Settings CCB failed\n" |
|---|
| 1804 | "%s", strerror(errno)); |
|---|
| 1805 | cam_close_device(cam_dev); |
|---|
| 1806 | return 0; |
|---|
| 1807 | } |
|---|
| 1808 | // now check if we are working with USB device, see umass.c |
|---|
| 1809 | if(strcmp(ccb.cpi.dev_name,"umass-sim") == 0) { // USB device found |
|---|
| 1810 | usbdevlist(bus,vendor_id, product_id, version); |
|---|
| 1811 | int bus=ccb.cpi.unit_number; // unit_number will match umass number |
|---|
| 1812 | cam_close_device(cam_dev); |
|---|
| 1813 | if(usbdevlist(bus,vendor_id, product_id, version)){ |
|---|
| 1814 | const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id, version); |
|---|
| 1815 | if (usbtype) |
|---|
| 1816 | return get_sat_device(usbtype, new freebsd_scsi_device(this, test_name, "")); |
|---|
| 1817 | } |
|---|
| 1818 | return 0; |
|---|
| 1819 | } |
|---|
| 1820 | #if FREEBSDVER > 800100 |
|---|
| 1821 | // check if we have ATA device connected to CAM (ada) |
|---|
| 1822 | if(ccb.cpi.protocol == PROTO_ATA){ |
|---|
| 1823 | cam_close_device(cam_dev); |
|---|
| 1824 | return new freebsd_atacam_device(this, test_name, ""); |
|---|
| 1825 | } |
|---|
| 1826 | #endif |
|---|
| 1827 | // close cam device, we don`t need it anymore |
|---|
| 1828 | cam_close_device(cam_dev); |
|---|
| 1829 | // handle as usual scsi |
|---|
| 1830 | return new freebsd_scsi_device(this, test_name, ""); |
|---|
| 1831 | } |
|---|
| 1832 | } |
|---|
| 1833 | } |
|---|
| 1834 | // device is LSI raid supported by mfi driver |
|---|
| 1835 | if(!strncmp("/dev/mfid", test_name, strlen("/dev/mfid"))) |
|---|
| 1836 | set_err(EINVAL, "To monitor disks on LSI RAID load mfip.ko module and run 'smartctl -a /dev/passX' to show SMART information"); |
|---|
| 1837 | // device type unknown |
|---|
| 1838 | return 0; |
|---|
| 1839 | } |
|---|
| 1840 | |
|---|
| 1841 | |
|---|
| 1842 | smart_device * freebsd_smart_interface::get_custom_smart_device(const char * name, const char * type) |
|---|
| 1843 | { |
|---|
| 1844 | // 3Ware ? |
|---|
| 1845 | static const char * fbsd_dev_twe_ctrl = "/dev/twe"; |
|---|
| 1846 | static const char * fbsd_dev_twa_ctrl = "/dev/twa"; |
|---|
| 1847 | static const char * fbsd_dev_tws_ctrl = "/dev/tws"; |
|---|
| 1848 | int disknum = -1, n1 = -1, n2 = -1, contr = -1; |
|---|
| 1849 | |
|---|
| 1850 | if (sscanf(type, "3ware,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) { |
|---|
| 1851 | if (n2 != (int)strlen(type)) { |
|---|
| 1852 | set_err(EINVAL, "Option -d 3ware,N requires N to be a non-negative integer"); |
|---|
| 1853 | return 0; |
|---|
| 1854 | } |
|---|
| 1855 | if (!(0 <= disknum && disknum <= 127)) { |
|---|
| 1856 | set_err(EINVAL, "Option -d 3ware,N (N=%d) must have 0 <= N <= 127", disknum); |
|---|
| 1857 | return 0; |
|---|
| 1858 | } |
|---|
| 1859 | |
|---|
| 1860 | // guess 3ware device type based on device name |
|---|
| 1861 | if (str_starts_with(name, fbsd_dev_twa_ctrl) || |
|---|
| 1862 | str_starts_with(name, fbsd_dev_tws_ctrl) ) { |
|---|
| 1863 | contr=CONTROLLER_3WARE_9000_CHAR; |
|---|
| 1864 | } |
|---|
| 1865 | if (!strncmp(fbsd_dev_twe_ctrl, name, strlen(fbsd_dev_twe_ctrl))){ |
|---|
| 1866 | contr=CONTROLLER_3WARE_678K_CHAR; |
|---|
| 1867 | } |
|---|
| 1868 | |
|---|
| 1869 | if(contr == -1){ |
|---|
| 1870 | set_err(EINVAL, "3ware controller type unknown, use %sX, %sX or %sX devices", |
|---|
| 1871 | fbsd_dev_twe_ctrl, fbsd_dev_twa_ctrl, fbsd_dev_tws_ctrl); |
|---|
| 1872 | return 0; |
|---|
| 1873 | } |
|---|
| 1874 | return new freebsd_escalade_device(this, name, contr, disknum); |
|---|
| 1875 | } |
|---|
| 1876 | |
|---|
| 1877 | // Highpoint ? |
|---|
| 1878 | int controller = -1, channel = -1; disknum = 1; |
|---|
| 1879 | n1 = n2 = -1; int n3 = -1; |
|---|
| 1880 | if (sscanf(type, "hpt,%n%d/%d%n/%d%n", &n1, &controller, &channel, &n2, &disknum, &n3) >= 2 || n1 == 4) { |
|---|
| 1881 | int len = strlen(type); |
|---|
| 1882 | if (!(n2 == len || n3 == len)) { |
|---|
| 1883 | set_err(EINVAL, "Option '-d hpt,L/M/N' supports 2-3 items"); |
|---|
| 1884 | return 0; |
|---|
| 1885 | } |
|---|
| 1886 | if (!(1 <= controller && controller <= 8)) { |
|---|
| 1887 | set_err(EINVAL, "Option '-d hpt,L/M/N' invalid controller id L supplied"); |
|---|
| 1888 | return 0; |
|---|
| 1889 | } |
|---|
| 1890 | if (!(1 <= channel && channel <= 16)) { |
|---|
| 1891 | set_err(EINVAL, "Option '-d hpt,L/M/N' invalid channel number M supplied"); |
|---|
| 1892 | return 0; |
|---|
| 1893 | } |
|---|
| 1894 | if (!(1 <= disknum && disknum <= 15)) { |
|---|
| 1895 | set_err(EINVAL, "Option '-d hpt,L/M/N' invalid pmport number N supplied"); |
|---|
| 1896 | return 0; |
|---|
| 1897 | } |
|---|
| 1898 | return new freebsd_highpoint_device(this, name, controller, channel, disknum); |
|---|
| 1899 | } |
|---|
| 1900 | |
|---|
| 1901 | // CCISS ? |
|---|
| 1902 | disknum = n1 = n2 = -1; |
|---|
| 1903 | if (sscanf(type, "cciss,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) { |
|---|
| 1904 | if (n2 != (int)strlen(type)) { |
|---|
| 1905 | set_err(EINVAL, "Option -d cciss,N requires N to be a non-negative integer"); |
|---|
| 1906 | return 0; |
|---|
| 1907 | } |
|---|
| 1908 | if (!(0 <= disknum && disknum <= 127)) { |
|---|
| 1909 | set_err(EINVAL, "Option -d cciss,N (N=%d) must have 0 <= N <= 127", disknum); |
|---|
| 1910 | return 0; |
|---|
| 1911 | } |
|---|
| 1912 | return get_sat_device("sat,auto", new freebsd_cciss_device(this, name, disknum)); |
|---|
| 1913 | } |
|---|
| 1914 | #if FREEBSDVER > 800100 |
|---|
| 1915 | // adaX devices ? |
|---|
| 1916 | if(!strcmp(type,"atacam")) |
|---|
| 1917 | return new freebsd_atacam_device(this, name, ""); |
|---|
| 1918 | #endif |
|---|
| 1919 | // Areca? |
|---|
| 1920 | disknum = n1 = n2 = -1; |
|---|
| 1921 | int encnum = 1; |
|---|
| 1922 | if (sscanf(type, "areca,%n%d/%d%n", &n1, &disknum, &encnum, &n2) >= 1 || n1 == 6) { |
|---|
| 1923 | if (!(1 <= disknum && disknum <= 128)) { |
|---|
| 1924 | set_err(EINVAL, "Option -d areca,N/E (N=%d) must have 1 <= N <= 128", disknum); |
|---|
| 1925 | return 0; |
|---|
| 1926 | } |
|---|
| 1927 | if (!(1 <= encnum && encnum <= 8)) { |
|---|
| 1928 | set_err(EINVAL, "Option -d areca,N/E (E=%d) must have 1 <= E <= 8", encnum); |
|---|
| 1929 | return 0; |
|---|
| 1930 | } |
|---|
| 1931 | return new freebsd_areca_ata_device(this, name, disknum, encnum); |
|---|
| 1932 | } |
|---|
| 1933 | |
|---|
| 1934 | return 0; |
|---|
| 1935 | } |
|---|
| 1936 | |
|---|
| 1937 | std::string freebsd_smart_interface::get_valid_custom_dev_types_str() |
|---|
| 1938 | { |
|---|
| 1939 | return "3ware,N, hpt,L/M/N, cciss,N, areca,N/E" |
|---|
| 1940 | #if FREEBSDVER > 800100 |
|---|
| 1941 | ", atacam" |
|---|
| 1942 | #endif |
|---|
| 1943 | ; |
|---|
| 1944 | } |
|---|
| 1945 | |
|---|
| 1946 | } // namespace |
|---|
| 1947 | |
|---|
| 1948 | ///////////////////////////////////////////////////////////////////////////// |
|---|
| 1949 | /// Initialize platform interface and register with smi() |
|---|
| 1950 | |
|---|
| 1951 | void smart_interface::init() |
|---|
| 1952 | { |
|---|
| 1953 | static os_freebsd::freebsd_smart_interface the_interface; |
|---|
| 1954 | smart_interface::set(&the_interface); |
|---|
| 1955 | } |
|---|