]>
Commit | Line | Data |
---|---|---|
832b75ed GG |
1 | /* |
2 | * os_freebsd.c | |
3 | * | |
4 | * Home page of code is: http://smartmontools.sourceforge.net | |
5 | * | |
34ad0c5f | 6 | * Copyright (C) 2003-8 Eduard Martinescu <smartmontools-support@lists.sourceforge.net> |
832b75ed GG |
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 <camlib.h> | |
24 | #include <cam/scsi/scsi_message.h> | |
2127e193 GI |
25 | #include <cam/scsi/scsi_pass.h> |
26 | #include <dev/usb/usb.h> | |
1953ff6d GG |
27 | #if defined(__DragonFly__) |
28 | #include <sys/nata.h> | |
29 | #else | |
832b75ed | 30 | #include <sys/ata.h> |
1953ff6d | 31 | #endif |
832b75ed GG |
32 | #include <sys/stat.h> |
33 | #include <unistd.h> | |
832b75ed | 34 | #include <glob.h> |
832b75ed | 35 | #include <stddef.h> |
2127e193 GI |
36 | #include <paths.h> |
37 | #include <sys/utsname.h> | |
832b75ed GG |
38 | |
39 | #include "config.h" | |
40 | #include "int64.h" | |
41 | #include "atacmds.h" | |
42 | #include "scsicmds.h" | |
a37e7145 | 43 | #include "cciss.h" |
832b75ed | 44 | #include "utility.h" |
a37e7145 | 45 | #include "extern.h" |
832b75ed GG |
46 | #include "os_freebsd.h" |
47 | ||
2127e193 GI |
48 | #include "dev_interface.h" |
49 | #include "dev_ata_cmd_set.h" | |
50 | ||
51 | #define USBDEV "/dev/usb" | |
832b75ed | 52 | |
2127e193 GI |
53 | #define CONTROLLER_UNKNOWN 0x00 |
54 | #define CONTROLLER_ATA 0x01 | |
55 | #define CONTROLLER_SCSI 0x02 | |
56 | #define CONTROLLER_3WARE 0x03 // set by -d option, but converted to one of three types below | |
57 | #define CONTROLLER_3WARE_678K 0x04 // NOT set by guess_device_type() | |
58 | #define CONTROLLER_3WARE_9000_CHAR 0x05 // set by guess_device_type() | |
59 | #define CONTROLLER_3WARE_678K_CHAR 0x06 // set by guess_device_type() | |
60 | #define CONTROLLER_MARVELL_SATA 0x07 // SATA drives behind Marvell controllers | |
61 | #define CONTROLLER_SAT 0x08 // SATA device behind a SCSI ATA Translation (SAT) layer | |
62 | #define CONTROLLER_HPT 0x09 // SATA drives behind HighPoint Raid controllers | |
63 | #define CONTROLLER_CCISS 0x10 // CCISS controller | |
64 | #define CONTROLLER_PARSEDEV 0x11 // "smartctl -r ataioctl,2 ..." output parser pseudo-device | |
65 | #define CONTROLLER_USBCYPRESS 0x12 // ATA device behind Cypress USB bridge | |
66 | #define CONTROLLER_ARECA 0x13 // Areca controller | |
832b75ed | 67 | |
2127e193 GI |
68 | static __unused const char *filenameandversion="$Id: os_freebsd.cpp 2879 2009-08-29 17:19:00Z chrfranke $"; |
69 | ||
70 | const char *os_XXXX_c_cvsid="$Id: os_freebsd.cpp 2879 2009-08-29 17:19:00Z chrfranke $" \ | |
71 | ATACMDS_H_CVSID CCISS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_FREEBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; | |
832b75ed | 72 | |
a37e7145 GG |
73 | extern smartmonctrl * con; |
74 | ||
832b75ed GG |
75 | // Private table of open devices: guaranteed zero on startup since |
76 | // part of static data. | |
77 | struct freebsd_dev_channel *devicetable[FREEBSD_MAXDEV]; | |
78 | ||
79 | // forward declaration | |
2127e193 | 80 | // static int parse_ata_chan_dev(const char * dev_name, struct freebsd_dev_channel *ch); |
a37e7145 | 81 | |
832b75ed GG |
82 | |
83 | // Returns 1 if device not available/open/found else 0. Also shifts fd into valid range. | |
84 | static int isnotopen(int *fd, struct freebsd_dev_channel** fdchan) { | |
85 | // put valid "file descriptor" into range 0...FREEBSD_MAXDEV-1 | |
86 | *fd -= FREEBSD_FDOFFSET; | |
87 | ||
88 | // check for validity of "file descriptor". | |
89 | if (*fd<0 || *fd>=FREEBSD_MAXDEV || !((*fdchan)=devicetable[*fd])) { | |
90 | errno = ENODEV; | |
91 | return 1; | |
92 | } | |
93 | ||
94 | return 0; | |
95 | } | |
96 | ||
832b75ed GG |
97 | #define NO_RETURN 0 |
98 | #define BAD_SMART 1 | |
99 | #define NO_DISK_3WARE 2 | |
100 | #define BAD_KERNEL 3 | |
101 | #define MAX_MSG 3 | |
102 | ||
103 | // Utility function for printing warnings | |
104 | void printwarning(int msgNo, const char* extra) { | |
105 | static int printed[] = {0,0,0,0}; | |
106 | static const char* message[]={ | |
107 | "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", | |
108 | ||
109 | "Error SMART Status command failed\nPlease get assistance from \n" PACKAGE_HOMEPAGE "\nRegister values returned from SMART Status command are:\n", | |
110 | ||
111 | "You must specify a DISK # for 3ware drives with -d 3ware,<n> where <n> begins with 1 for first disk drive\n", | |
112 | ||
113 | "ATA support is not provided for this kernel version. Please ugrade to a recent 5-CURRENT kernel (post 09/01/2003 or so)\n" | |
114 | }; | |
115 | ||
116 | if (msgNo >= 0 && msgNo <= MAX_MSG) { | |
117 | if (!printed[msgNo]) { | |
118 | printed[msgNo] = 1; | |
119 | pout("%s", message[msgNo]); | |
120 | if (extra) | |
121 | pout("%s",extra); | |
122 | } | |
123 | } | |
124 | return; | |
125 | } | |
126 | ||
a37e7145 | 127 | |
2127e193 | 128 | // Interface to ATA devices behind 3ware escalade RAID controller cards. See os_linux.c |
832b75ed | 129 | |
2127e193 GI |
130 | #define BUFFER_LEN_678K_CHAR ( sizeof(struct twe_usercommand) ) // 520 |
131 | #define BUFFER_LEN_9000_CHAR ( sizeof(TW_OSLI_IOCTL_NO_DATA_BUF) + sizeof(TWE_Command) ) // 2048 | |
132 | #define TW_IOCTL_BUFFER_SIZE ( MAX(BUFFER_LEN_678K_CHAR, BUFFER_LEN_9000_CHAR) ) | |
4d59bff9 | 133 | |
832b75ed | 134 | |
832b75ed | 135 | |
832b75ed | 136 | |
832b75ed | 137 | |
832b75ed | 138 | |
2127e193 GI |
139 | #ifndef ATA_DEVICE |
140 | #define ATA_DEVICE "/dev/ata" | |
832b75ed | 141 | #endif |
832b75ed | 142 | |
832b75ed | 143 | |
832b75ed | 144 | |
2127e193 GI |
145 | // global variable holding byte count of allocated memory |
146 | long long bytes; | |
832b75ed GG |
147 | |
148 | ||
2127e193 GI |
149 | /* |
150 | * dev_legacy.cpp | |
151 | * | |
152 | * Home page of code is: http://smartmontools.sourceforge.net | |
153 | * | |
154 | * Copyright (C) 2008 Christian Franke <smartmontools-support@lists.sourceforge.net> | |
155 | * | |
156 | * This program is free software; you can redistribute it and/or modify | |
157 | * it under the terms of the GNU General Public License as published by | |
158 | * the Free Software Foundation; either version 2, or (at your option) | |
159 | * any later version. | |
160 | * | |
161 | * You should have received a copy of the GNU General Public License | |
162 | * (for example COPYING); If not, see <http://www.gnu.org/licenses/>. | |
163 | * | |
164 | */ | |
832b75ed | 165 | |
832b75ed | 166 | |
2127e193 GI |
167 | const char * dev_freebsd_cpp_cvsid = "$Id: os_freebsd.cpp 2879 2009-08-29 17:19:00Z chrfranke $" |
168 | DEV_INTERFACE_H_CVSID; | |
832b75ed | 169 | |
2127e193 | 170 | extern smartmonctrl * con; // con->reportscsiioctl |
832b75ed | 171 | |
2127e193 | 172 | ///////////////////////////////////////////////////////////////////////////// |
832b75ed | 173 | |
2127e193 GI |
174 | #ifdef HAVE_ATA_IDENTIFY_IS_CACHED |
175 | int ata_identify_is_cached(int fd); | |
176 | #endif | |
832b75ed | 177 | |
2127e193 | 178 | ///////////////////////////////////////////////////////////////////////////// |
832b75ed | 179 | |
2127e193 | 180 | namespace os_freebsd { // No need to publish anything, name provided for Doxygen |
832b75ed | 181 | |
2127e193 GI |
182 | ///////////////////////////////////////////////////////////////////////////// |
183 | /// Implement shared open/close routines with old functions. | |
832b75ed | 184 | |
2127e193 GI |
185 | class freebsd_smart_device |
186 | : virtual public /*implements*/ smart_device | |
187 | { | |
188 | public: | |
189 | explicit freebsd_smart_device(const char * mode) | |
190 | : smart_device(never_called), | |
191 | m_fd(-1), m_mode(mode) { } | |
832b75ed | 192 | |
2127e193 | 193 | virtual ~freebsd_smart_device() throw(); |
832b75ed | 194 | |
2127e193 | 195 | virtual bool is_open() const; |
832b75ed | 196 | |
2127e193 | 197 | virtual bool open(); |
832b75ed | 198 | |
2127e193 | 199 | virtual bool close(); |
832b75ed | 200 | |
2127e193 GI |
201 | protected: |
202 | /// Return filedesc for derived classes. | |
203 | int get_fd() const | |
204 | { return m_fd; } | |
832b75ed | 205 | |
2127e193 GI |
206 | private: |
207 | int m_fd; ///< filedesc, -1 if not open. | |
208 | const char * m_mode; ///< Mode string for deviceopen(). | |
209 | }; | |
832b75ed | 210 | |
2127e193 GI |
211 | |
212 | freebsd_smart_device::~freebsd_smart_device() throw() | |
a37e7145 | 213 | { |
2127e193 GI |
214 | if (m_fd >= 0) |
215 | os_freebsd::freebsd_smart_device::close(); | |
a37e7145 GG |
216 | } |
217 | ||
2127e193 GI |
218 | // migration from the old_style |
219 | unsigned char m_controller_type; | |
220 | unsigned char m_controller_port; | |
832b75ed | 221 | |
2127e193 GI |
222 | // examples for smartctl |
223 | static const char smartctl_examples[] = | |
224 | "=================================================== SMARTCTL EXAMPLES =====\n\n" | |
225 | " smartctl -a /dev/ad0 (Prints all SMART information)\n\n" | |
226 | " smartctl --smart=on --offlineauto=on --saveauto=on /dev/ad0\n" | |
227 | " (Enables SMART on first disk)\n\n" | |
228 | " smartctl -t long /dev/ad0 (Executes extended disk self-test)\n\n" | |
229 | " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/ad0\n" | |
230 | " (Prints Self-Test & Attribute errors)\n" | |
231 | " (Prints Self-Test & Attribute errors)\n\n" | |
232 | " smartctl -a --device=3ware,2 /dev/twa0\n" | |
233 | " smartctl -a --device=3ware,2 /dev/twe0\n" | |
234 | " (Prints all SMART information for ATA disk on\n" | |
235 | " third port of first 3ware RAID controller)\n" | |
236 | ; | |
832b75ed | 237 | |
2127e193 GI |
238 | bool freebsd_smart_device::is_open() const |
239 | { | |
240 | return (m_fd >= 0); | |
241 | } | |
832b75ed | 242 | |
832b75ed | 243 | |
2127e193 GI |
244 | static int hpt_hba(const char* name) { |
245 | int i=0; | |
246 | const char *hpt_node[]={"hptmv", "hptmv6", "hptrr", "hptiop", "hptmviop", "hpt32xx", "rr2320", | |
247 | "rr232x", "rr2310", "rr2310_00", "rr2300", "rr2340", "rr1740", NULL}; | |
248 | while (hpt_node[i]) { | |
249 | if (!strncmp(name, hpt_node[i], strlen(hpt_node[i]))) | |
250 | return 1; | |
251 | i++; | |
832b75ed | 252 | } |
2127e193 GI |
253 | return 0; |
254 | } | |
832b75ed | 255 | |
2127e193 GI |
256 | static int get_tw_channel_unit (const char* name, int* unit, int* dev) { |
257 | const char *p; | |
832b75ed | 258 | |
2127e193 GI |
259 | /* device node sanity check */ |
260 | for (p = name + 3; *p; p++) | |
261 | if (*p < '0' || *p > '9') | |
262 | return -1; | |
263 | if (strlen(name) > 4 && *(name + 3) == '0') | |
832b75ed | 264 | return -1; |
832b75ed | 265 | |
2127e193 GI |
266 | if (dev != NULL) |
267 | *dev=atoi(name + 3); | |
832b75ed | 268 | |
2127e193 GI |
269 | /* no need for unit number */ |
270 | if (unit != NULL) | |
271 | *unit=0; | |
272 | return 0; | |
273 | } | |
832b75ed | 274 | |
2127e193 GI |
275 | #ifndef IOCATAREQUEST |
276 | static int get_ata_channel_unit ( const char* name, int* unit, int* dev) { | |
277 | #ifndef ATAREQUEST | |
278 | *dev=0; | |
279 | *unit=0; | |
280 | return 0; | |
281 | #else | |
282 | // there is no direct correlation between name 'ad0, ad1, ...' and | |
283 | // channel/unit number. So we need to iterate through the possible | |
284 | // channels and check each unit to see if we match names | |
285 | struct ata_cmd iocmd; | |
286 | int fd,maxunit; | |
832b75ed | 287 | |
2127e193 GI |
288 | bzero(&iocmd, sizeof(struct ata_cmd)); |
289 | ||
290 | if ((fd = open(ATA_DEVICE, O_RDWR)) < 0) | |
291 | return -errno; | |
832b75ed | 292 | |
2127e193 GI |
293 | iocmd.cmd = ATAGMAXCHANNEL; |
294 | if (ioctl(fd, IOCATA, &iocmd) < 0) { | |
295 | return -errno; | |
296 | close(fd); | |
832b75ed | 297 | } |
2127e193 GI |
298 | maxunit = iocmd.u.maxchan; |
299 | for (*unit = 0; *unit < maxunit; (*unit)++) { | |
300 | iocmd.channel = *unit; | |
301 | iocmd.device = -1; | |
302 | iocmd.cmd = ATAGPARM; | |
303 | if (ioctl(fd, IOCATA, &iocmd) < 0) { | |
304 | close(fd); | |
305 | return -errno; | |
306 | } | |
307 | if (iocmd.u.param.type[0] && !strcmp(name,iocmd.u.param.name[0])) { | |
308 | *dev = 0; | |
309 | break; | |
310 | } | |
311 | if (iocmd.u.param.type[1] && !strcmp(name,iocmd.u.param.name[1])) { | |
312 | *dev = 1; | |
313 | break; | |
314 | } | |
832b75ed | 315 | } |
2127e193 GI |
316 | close(fd); |
317 | if (*unit == maxunit) | |
318 | return -1; | |
319 | else | |
320 | return 0; | |
321 | #endif | |
322 | } | |
323 | #endif | |
324 | ||
325 | // Guess device type (ata or scsi) based on device name (FreeBSD | |
326 | // specific) SCSI device name in FreeBSD can be sd, sr, scd, st, nst, | |
327 | // osst, nosst and sg. | |
328 | static const char * fbsd_dev_prefix = _PATH_DEV; | |
329 | static const char * fbsd_dev_ata_disk_prefix = "ad"; | |
330 | static const char * fbsd_dev_scsi_disk_plus = "da"; | |
331 | static const char * fbsd_dev_scsi_pass = "pass"; | |
332 | static const char * fbsd_dev_scsi_tape1 = "sa"; | |
333 | static const char * fbsd_dev_scsi_tape2 = "nsa"; | |
334 | static const char * fbsd_dev_scsi_tape3 = "esa"; | |
335 | static const char * fbsd_dev_twe_ctrl = "twe"; | |
336 | static const char * fbsd_dev_twa_ctrl = "twa"; | |
337 | static const char * fbsd_dev_cciss = "ciss"; | |
338 | ||
339 | int parse_ata_chan_dev(const char * dev_name, struct freebsd_dev_channel *chan) { | |
340 | int len; | |
341 | int dev_prefix_len = strlen(fbsd_dev_prefix); | |
832b75ed | 342 | |
2127e193 GI |
343 | // if dev_name null, or string length zero |
344 | if (!dev_name || !(len = strlen(dev_name))) | |
345 | return CONTROLLER_UNKNOWN; | |
346 | ||
347 | // Remove the leading /dev/... if it's there | |
348 | if (!strncmp(fbsd_dev_prefix, dev_name, dev_prefix_len)) { | |
349 | if (len <= dev_prefix_len) | |
350 | // if nothing else in the string, unrecognized | |
351 | return CONTROLLER_UNKNOWN; | |
352 | // else advance pointer to following characters | |
353 | dev_name += dev_prefix_len; | |
832b75ed | 354 | } |
2127e193 GI |
355 | // form /dev/ad* or ad* |
356 | if (!strncmp(fbsd_dev_ata_disk_prefix, dev_name, | |
357 | strlen(fbsd_dev_ata_disk_prefix))) { | |
358 | #ifndef IOCATAREQUEST | |
359 | if (chan != NULL) { | |
360 | if (get_ata_channel_unit(dev_name,&(chan->channel),&(chan->device))<0) { | |
361 | return CONTROLLER_UNKNOWN; | |
362 | } | |
363 | } | |
832b75ed | 364 | #endif |
2127e193 | 365 | return CONTROLLER_ATA; |
832b75ed GG |
366 | } |
367 | ||
2127e193 GI |
368 | // form /dev/pass* or pass* |
369 | if (!strncmp(fbsd_dev_scsi_pass, dev_name, | |
370 | strlen(fbsd_dev_scsi_pass))) | |
371 | goto handlescsi; | |
372 | ||
373 | // form /dev/da* or da* | |
374 | if (!strncmp(fbsd_dev_scsi_disk_plus, dev_name, | |
375 | strlen(fbsd_dev_scsi_disk_plus))) | |
376 | goto handlescsi; | |
377 | ||
378 | // form /dev/sa* or sa* | |
379 | if (!strncmp(fbsd_dev_scsi_tape1, dev_name, | |
380 | strlen(fbsd_dev_scsi_tape1))) | |
381 | goto handlescsi; | |
382 | ||
383 | // form /dev/nsa* or nsa* | |
384 | if (!strncmp(fbsd_dev_scsi_tape2, dev_name, | |
385 | strlen(fbsd_dev_scsi_tape2))) | |
386 | goto handlescsi; | |
387 | ||
388 | // form /dev/esa* or esa* | |
389 | if (!strncmp(fbsd_dev_scsi_tape3, dev_name, | |
390 | strlen(fbsd_dev_scsi_tape3))) | |
391 | goto handlescsi; | |
832b75ed | 392 | |
2127e193 GI |
393 | if (!strncmp(fbsd_dev_twa_ctrl,dev_name, |
394 | strlen(fbsd_dev_twa_ctrl))) { | |
395 | if (chan != NULL) { | |
396 | if (get_tw_channel_unit(dev_name,&(chan->channel),&(chan->device))<0) { | |
397 | return CONTROLLER_UNKNOWN; | |
398 | } | |
399 | } | |
400 | else if (get_tw_channel_unit(dev_name,NULL,NULL)<0) { | |
401 | return CONTROLLER_UNKNOWN; | |
402 | } | |
403 | return CONTROLLER_3WARE_9000_CHAR; | |
832b75ed | 404 | } |
2127e193 GI |
405 | |
406 | if (!strncmp(fbsd_dev_twe_ctrl,dev_name, | |
407 | strlen(fbsd_dev_twe_ctrl))) { | |
408 | if (chan != NULL) { | |
409 | if (get_tw_channel_unit(dev_name,&(chan->channel),&(chan->device))<0) { | |
410 | return CONTROLLER_UNKNOWN; | |
411 | } | |
412 | } | |
413 | else if (get_tw_channel_unit(dev_name,NULL,NULL)<0) { | |
414 | return CONTROLLER_UNKNOWN; | |
415 | } | |
416 | return CONTROLLER_3WARE_678K_CHAR; | |
832b75ed GG |
417 | } |
418 | ||
2127e193 GI |
419 | if (hpt_hba(dev_name)) { |
420 | return CONTROLLER_HPT; | |
421 | } | |
422 | ||
423 | // form /dev/ciss* | |
424 | if (!strncmp(fbsd_dev_cciss, dev_name, | |
425 | strlen(fbsd_dev_cciss))) | |
426 | return CONTROLLER_CCISS; | |
427 | ||
428 | // we failed to recognize any of the forms | |
429 | return CONTROLLER_UNKNOWN; | |
430 | ||
431 | handlescsi: | |
432 | if (chan != NULL) { | |
433 | if (!(chan->devname = (char *)calloc(1,DEV_IDLEN+1))) | |
434 | return CONTROLLER_UNKNOWN; | |
832b75ed | 435 | |
2127e193 GI |
436 | if (cam_get_device(dev_name,chan->devname,DEV_IDLEN,&(chan->unitnum)) == -1) |
437 | return CONTROLLER_UNKNOWN; | |
832b75ed | 438 | } |
2127e193 | 439 | return CONTROLLER_SCSI; |
832b75ed | 440 | |
2127e193 GI |
441 | } |
442 | ||
443 | ||
444 | bool freebsd_smart_device::open() | |
445 | { | |
446 | ||
447 | const char *dev = get_dev_name(); | |
448 | struct freebsd_dev_channel *fdchan; | |
449 | int parse_ok, i; | |
450 | ||
451 | // Search table for a free entry | |
452 | for (i=0; i<FREEBSD_MAXDEV; i++) | |
453 | if (!devicetable[i]) | |
454 | break; | |
832b75ed | 455 | |
2127e193 GI |
456 | // If no free entry found, return error. We have max allowed number |
457 | // of "file descriptors" already allocated. | |
458 | if (i == FREEBSD_MAXDEV) { | |
459 | errno = EMFILE; | |
460 | return false; | |
832b75ed | 461 | } |
2127e193 GI |
462 | |
463 | fdchan = (struct freebsd_dev_channel *)calloc(1,sizeof(struct freebsd_dev_channel)); | |
464 | if (fdchan == NULL) { | |
465 | // errno already set by call to malloc() | |
466 | return false; | |
467 | } | |
468 | ||
469 | parse_ok = parse_ata_chan_dev(dev,fdchan); | |
832b75ed | 470 | |
2127e193 GI |
471 | if (parse_ok == CONTROLLER_UNKNOWN) { |
472 | free(fdchan); | |
473 | errno = ENOTTY; | |
474 | return false; // can't handle what we don't know | |
475 | } | |
832b75ed | 476 | |
2127e193 GI |
477 | if (parse_ok == CONTROLLER_ATA) { |
478 | #ifdef IOCATAREQUEST | |
479 | if ((fdchan->device = ::open(dev,O_RDONLY))<0) { | |
480 | #else | |
481 | if ((fdchan->atacommand = ::open("/dev/ata",O_RDWR))<0) { | |
482 | #endif | |
483 | int myerror = errno; // preserve across free call | |
484 | free(fdchan); | |
485 | errno = myerror; | |
486 | return false; | |
487 | } | |
488 | } | |
832b75ed | 489 | |
2127e193 GI |
490 | if (parse_ok == CONTROLLER_3WARE_678K_CHAR) { |
491 | char buf[512]; | |
492 | sprintf(buf,"/dev/twe%d",fdchan->device); | |
493 | #ifdef IOCATAREQUEST | |
494 | if ((fdchan->device = ::open(buf,O_RDWR))<0) { | |
495 | #else | |
496 | if ((fdchan->atacommand = ::open(buf,O_RDWR))<0) { | |
497 | #endif | |
498 | int myerror = errno; // preserve across free call | |
499 | free(fdchan); | |
500 | errno = myerror; | |
501 | return false; | |
502 | } | |
503 | } | |
832b75ed | 504 | |
2127e193 GI |
505 | if (parse_ok == CONTROLLER_3WARE_9000_CHAR) { |
506 | char buf[512]; | |
507 | sprintf(buf,"/dev/twa%d",fdchan->device); | |
508 | #ifdef IOCATAREQUEST | |
509 | if ((fdchan->device = ::open(buf,O_RDWR))<0) { | |
510 | #else | |
511 | if ((fdchan->atacommand = ::open(buf,O_RDWR))<0) { | |
512 | #endif | |
513 | int myerror = errno; // preserve across free call | |
514 | free(fdchan); | |
515 | errno = myerror; | |
516 | return false; | |
517 | } | |
518 | } | |
519 | ||
520 | if (parse_ok == CONTROLLER_HPT) { | |
521 | if ((fdchan->device = ::open(dev,O_RDWR))<0) { | |
522 | int myerror = errno; // preserve across free call | |
523 | free(fdchan); | |
524 | errno = myerror; | |
525 | return false; | |
526 | } | |
527 | } | |
528 | ||
529 | if (parse_ok == CONTROLLER_CCISS) { | |
530 | if ((fdchan->device = ::open(dev,O_RDWR))<0) { | |
531 | int myerror = errno; // preserve across free call | |
532 | free(fdchan); | |
533 | errno = myerror; | |
534 | return false; | |
535 | } | |
536 | } | |
537 | ||
538 | if (parse_ok == CONTROLLER_SCSI) { | |
539 | // this is really a NO-OP, as the parse takes care | |
540 | // of filling in correct details | |
541 | } | |
542 | ||
543 | // return pointer to "file descriptor" table entry, properly offset. | |
544 | devicetable[i]=fdchan; | |
545 | m_fd = i+FREEBSD_FDOFFSET; | |
546 | // endofold | |
547 | if (m_fd < 0) { | |
548 | set_err((errno==ENOENT || errno==ENOTDIR) ? ENODEV : errno); | |
549 | return false; | |
550 | } | |
551 | return true; | |
552 | } | |
553 | ||
554 | bool freebsd_smart_device::close() | |
555 | { | |
556 | int fd = m_fd; m_fd = -1; | |
557 | struct freebsd_dev_channel *fdchan; | |
558 | int failed = 0; | |
559 | ||
560 | // check for valid file descriptor | |
561 | if (isnotopen(&fd, &fdchan)) | |
562 | return false; | |
563 | ||
564 | ||
565 | // did we allocate a SCSI device name? | |
566 | if (fdchan->devname) | |
567 | free(fdchan->devname); | |
568 | ||
569 | // close device, if open | |
570 | if (fdchan->device) | |
571 | failed=::close(fdchan->device); | |
572 | #ifndef IOCATAREQUEST | |
573 | if (fdchan->atacommand) | |
574 | failed=::close(fdchan->atacommand); | |
575 | #endif | |
576 | ||
577 | // if close succeeded, then remove from device list | |
578 | // Eduard, should we also remove it from list if close() fails? I'm | |
579 | // not sure. Here I only remove it from list if close() worked. | |
580 | if (!failed) { | |
581 | free(fdchan); | |
582 | devicetable[fd]=NULL; | |
583 | } | |
584 | ||
585 | return failed; | |
586 | } | |
587 | ||
588 | ///////////////////////////////////////////////////////////////////////////// | |
589 | /// Implement standard ATA support with old functions | |
590 | ||
591 | class freebsd_ata_device | |
592 | : public /*implements*/ ata_device_with_command_set, | |
593 | public /*extends*/ freebsd_smart_device | |
594 | { | |
595 | public: | |
596 | freebsd_ata_device(smart_interface * intf, const char * dev_name, const char * req_type); | |
597 | ||
598 | #ifdef HAVE_ATA_IDENTIFY_IS_CACHED | |
599 | virtual bool ata_identify_is_cached() const; | |
600 | #endif | |
601 | ||
602 | protected: | |
603 | virtual int ata_command_interface(smart_command_set command, int select, char * data); | |
604 | }; | |
605 | ||
606 | freebsd_ata_device::freebsd_ata_device(smart_interface * intf, const char * dev_name, const char * req_type) | |
607 | : smart_device(intf, dev_name, "ata", req_type), | |
608 | freebsd_smart_device("ATA") | |
609 | { | |
610 | } | |
611 | ||
612 | int freebsd_ata_device::ata_command_interface(smart_command_set command, int select, char * data) | |
613 | { | |
614 | int fd=get_fd(); | |
615 | #if !defined(ATAREQUEST) && !defined(IOCATAREQUEST) | |
616 | // sorry, but without ATAng, we can't do anything here | |
617 | printwarning(BAD_KERNEL,NULL); | |
618 | errno = ENOSYS; | |
619 | return -1; | |
620 | #else | |
621 | struct freebsd_dev_channel* con; | |
622 | int retval, copydata=0; | |
623 | #ifdef IOCATAREQUEST | |
624 | struct ata_ioc_request request; | |
625 | #else | |
626 | struct ata_cmd iocmd; | |
627 | #endif | |
628 | unsigned char buff[512]; | |
629 | ||
630 | // check that "file descriptor" is valid | |
631 | if (isnotopen(&fd,&con)) | |
632 | return -1; | |
633 | ||
634 | bzero(buff,512); | |
635 | ||
636 | #ifdef IOCATAREQUEST | |
637 | bzero(&request,sizeof(struct ata_ioc_request)); | |
638 | #else | |
639 | bzero(&iocmd,sizeof(struct ata_cmd)); | |
640 | #endif | |
641 | bzero(buff,512); | |
642 | ||
643 | #ifndef IOCATAREQUEST | |
644 | iocmd.cmd=ATAREQUEST; | |
645 | iocmd.channel=con->channel; | |
646 | iocmd.device=con->device; | |
647 | #define request iocmd.u.request | |
648 | #endif | |
649 | ||
650 | request.u.ata.command=ATA_SMART_CMD; | |
651 | request.timeout=600; | |
652 | switch (command){ | |
653 | case READ_VALUES: | |
654 | request.u.ata.feature=ATA_SMART_READ_VALUES; | |
655 | request.u.ata.lba=0xc24f<<8; | |
656 | request.flags=ATA_CMD_READ; | |
657 | request.data=(char *)buff; | |
658 | request.count=512; | |
659 | copydata=1; | |
660 | break; | |
661 | case READ_THRESHOLDS: | |
662 | request.u.ata.feature=ATA_SMART_READ_THRESHOLDS; | |
663 | request.u.ata.count=1; | |
664 | request.u.ata.lba=1|(0xc24f<<8); | |
665 | request.flags=ATA_CMD_READ; | |
666 | request.data=(char *)buff; | |
667 | request.count=512; | |
668 | copydata=1; | |
669 | break; | |
670 | case READ_LOG: | |
671 | request.u.ata.feature=ATA_SMART_READ_LOG_SECTOR; | |
672 | request.u.ata.lba=select|(0xc24f<<8); | |
673 | request.u.ata.count=1; | |
674 | request.flags=ATA_CMD_READ; | |
675 | request.data=(char *)buff; | |
676 | request.count=512; | |
677 | copydata=1; | |
678 | break; | |
679 | case IDENTIFY: | |
680 | request.u.ata.command=ATA_IDENTIFY_DEVICE; | |
681 | request.flags=ATA_CMD_READ; | |
682 | request.data=(char *)buff; | |
683 | request.count=512; | |
684 | copydata=1; | |
685 | break; | |
686 | case PIDENTIFY: | |
687 | request.u.ata.command=ATA_IDENTIFY_PACKET_DEVICE; | |
688 | request.flags=ATA_CMD_READ; | |
689 | request.data=(char *)buff; | |
690 | request.count=512; | |
691 | copydata=1; | |
692 | break; | |
693 | case ENABLE: | |
694 | request.u.ata.feature=ATA_SMART_ENABLE; | |
695 | request.u.ata.lba=0xc24f<<8; | |
696 | request.flags=ATA_CMD_CONTROL; | |
697 | break; | |
698 | case DISABLE: | |
699 | request.u.ata.feature=ATA_SMART_DISABLE; | |
700 | request.u.ata.lba=0xc24f<<8; | |
701 | request.flags=ATA_CMD_CONTROL; | |
702 | break; | |
703 | case AUTO_OFFLINE: | |
704 | // NOTE: According to ATAPI 4 and UP, this command is obsolete | |
705 | request.u.ata.feature=ATA_SMART_AUTO_OFFLINE; | |
706 | request.u.ata.lba=0xc24f<<8; | |
707 | request.u.ata.count=select; | |
708 | request.flags=ATA_CMD_CONTROL; | |
709 | break; | |
710 | case AUTOSAVE: | |
711 | request.u.ata.feature=ATA_SMART_AUTOSAVE; | |
712 | request.u.ata.lba=0xc24f<<8; | |
713 | request.u.ata.count=select; | |
714 | request.flags=ATA_CMD_CONTROL; | |
715 | break; | |
716 | case IMMEDIATE_OFFLINE: | |
717 | request.u.ata.feature=ATA_SMART_IMMEDIATE_OFFLINE; | |
718 | request.u.ata.lba = select|(0xc24f<<8); // put test in sector | |
719 | request.flags=ATA_CMD_CONTROL; | |
720 | break; | |
721 | case STATUS_CHECK: // same command, no HDIO in FreeBSD | |
722 | case STATUS: | |
723 | // this command only says if SMART is working. It could be | |
724 | // replaced with STATUS_CHECK below. | |
725 | request.u.ata.feature=ATA_SMART_STATUS; | |
726 | request.u.ata.lba=0xc24f<<8; | |
727 | request.flags=ATA_CMD_CONTROL; | |
728 | break; | |
729 | case CHECK_POWER_MODE: | |
730 | request.u.ata.command=ATA_CHECK_POWER_MODE; | |
731 | request.u.ata.feature=0; | |
732 | request.flags=ATA_CMD_CONTROL; | |
733 | break; | |
734 | case WRITE_LOG: | |
735 | memcpy(buff, data, 512); | |
736 | request.u.ata.feature=ATA_SMART_WRITE_LOG_SECTOR; | |
737 | request.u.ata.lba=select|(0xc24f<<8); | |
738 | request.u.ata.count=1; | |
739 | request.flags=ATA_CMD_WRITE; | |
740 | request.data=(char *)buff; | |
741 | request.count=512; | |
742 | break; | |
743 | default: | |
744 | pout("Unrecognized command %d in ata_command_interface()\n" | |
745 | "Please contact " PACKAGE_BUGREPORT "\n", command); | |
746 | errno=ENOSYS; | |
747 | return -1; | |
748 | } | |
749 | ||
750 | if (command==STATUS_CHECK){ | |
751 | unsigned const char normal_lo=0x4f, normal_hi=0xc2; | |
752 | unsigned const char failed_lo=0xf4, failed_hi=0x2c; | |
753 | unsigned char low,high; | |
754 | ||
755 | #ifdef IOCATAREQUEST | |
756 | if ((retval=ioctl(con->device, IOCATAREQUEST, &request)) || request.error) | |
757 | #else | |
758 | if ((retval=ioctl(con->atacommand, IOCATA, &iocmd)) || request.error) | |
759 | #endif | |
760 | return -1; | |
761 | ||
762 | #if __FreeBSD_version < 502000 | |
763 | printwarning(NO_RETURN,NULL); | |
764 | #endif | |
765 | ||
766 | high = (request.u.ata.lba >> 16) & 0xff; | |
767 | low = (request.u.ata.lba >> 8) & 0xff; | |
768 | ||
769 | // Cyl low and Cyl high unchanged means "Good SMART status" | |
770 | if (low==normal_lo && high==normal_hi) | |
771 | return 0; | |
772 | ||
773 | // These values mean "Bad SMART status" | |
774 | if (low==failed_lo && high==failed_hi) | |
775 | return 1; | |
776 | ||
777 | // We haven't gotten output that makes sense; print out some debugging info | |
778 | char buf[512]; | |
779 | sprintf(buf,"CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n", | |
780 | (int)request.u.ata.command, | |
781 | (int)request.u.ata.feature, | |
782 | (int)request.u.ata.count, | |
783 | (int)((request.u.ata.lba) & 0xff), | |
784 | (int)((request.u.ata.lba>>8) & 0xff), | |
785 | (int)((request.u.ata.lba>>16) & 0xff), | |
786 | (int)request.error); | |
787 | printwarning(BAD_SMART,buf); | |
788 | return 0; | |
789 | } | |
790 | ||
791 | #ifdef IOCATAREQUEST | |
792 | if ((retval=ioctl(con->device, IOCATAREQUEST, &request)) || request.error) | |
793 | #else | |
794 | if ((retval=ioctl(con->atacommand, IOCATA, &iocmd)) || request.error) | |
795 | #endif | |
796 | { | |
797 | return -1; | |
798 | } | |
799 | // | |
800 | if (command == CHECK_POWER_MODE) { | |
801 | data[0] = request.u.ata.count & 0xff; | |
802 | return 0; | |
803 | } | |
804 | if (copydata) | |
805 | memcpy(data, buff, 512); | |
806 | ||
807 | return 0; | |
808 | #endif | |
809 | } | |
810 | ||
811 | #ifdef HAVE_ATA_IDENTIFY_IS_CACHED | |
812 | bool freebsd_ata_device::ata_identify_is_cached() const | |
813 | { | |
814 | return !!::ata_identify_is_cached(get_fd()); | |
815 | } | |
816 | #endif | |
817 | ||
818 | ||
819 | ///////////////////////////////////////////////////////////////////////////// | |
820 | /// Implement AMCC/3ware RAID support with old functions | |
821 | ||
822 | class freebsd_escalade_device | |
823 | : public /*implements*/ ata_device_with_command_set, | |
824 | public /*extends*/ freebsd_smart_device | |
825 | { | |
826 | public: | |
827 | freebsd_escalade_device(smart_interface * intf, const char * dev_name, | |
828 | int escalade_type, int disknum); | |
829 | ||
830 | protected: | |
831 | virtual int ata_command_interface(smart_command_set command, int select, char * data); | |
832 | ||
833 | private: | |
834 | int m_escalade_type; ///< Type string for escalade_command_interface(). | |
835 | int m_disknum; ///< Disk number. | |
836 | }; | |
837 | ||
838 | freebsd_escalade_device::freebsd_escalade_device(smart_interface * intf, const char * dev_name, | |
839 | int escalade_type, int disknum) | |
840 | : smart_device(intf, dev_name, "3ware", "3ware"), | |
841 | freebsd_smart_device( | |
842 | escalade_type==CONTROLLER_3WARE_9000_CHAR ? "ATA_3WARE_9000" : | |
843 | escalade_type==CONTROLLER_3WARE_678K_CHAR ? "ATA_3WARE_678K" : | |
844 | /* CONTROLLER_3WARE_678K */ "ATA" ), | |
845 | m_escalade_type(escalade_type), m_disknum(disknum) | |
846 | { | |
847 | set_info().info_name = strprintf("%s [3ware_disk_%02d]", dev_name, disknum); | |
848 | } | |
849 | ||
850 | int freebsd_escalade_device::ata_command_interface(smart_command_set command, int select, char * data) | |
851 | { | |
852 | // to hold true file descriptor | |
853 | int fd = get_fd(); | |
854 | struct freebsd_dev_channel* con; | |
855 | ||
856 | // return value and buffer for ioctl() | |
857 | int ioctlreturn, readdata=0; | |
858 | struct twe_usercommand* cmd_twe = NULL; | |
859 | TW_OSLI_IOCTL_NO_DATA_BUF* cmd_twa = NULL; | |
860 | TWE_Command_ATA* ata = NULL; | |
861 | ||
862 | // Used by both the SCSI and char interfaces | |
863 | char ioctl_buffer[TW_IOCTL_BUFFER_SIZE]; | |
864 | ||
865 | if (m_disknum < 0) { | |
866 | printwarning(NO_DISK_3WARE,NULL); | |
867 | return -1; | |
868 | } | |
869 | ||
870 | // check that "file descriptor" is valid | |
871 | if (isnotopen(&fd,&con)) | |
872 | return -1; | |
873 | ||
874 | memset(ioctl_buffer, 0, TW_IOCTL_BUFFER_SIZE); | |
875 | ||
876 | if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR) { | |
877 | cmd_twa = (TW_OSLI_IOCTL_NO_DATA_BUF*)ioctl_buffer; | |
878 | cmd_twa->pdata = ((TW_OSLI_IOCTL_WITH_PAYLOAD*)cmd_twa)->payload.data_buf; | |
879 | cmd_twa->driver_pkt.buffer_length = 512; | |
880 | ata = (TWE_Command_ATA*)&cmd_twa->cmd_pkt.command.cmd_pkt_7k; | |
881 | } else if (m_escalade_type==CONTROLLER_3WARE_678K_CHAR) { | |
882 | cmd_twe = (struct twe_usercommand*)ioctl_buffer; | |
883 | ata = &cmd_twe->tu_command.ata; | |
884 | } else { | |
885 | pout("Unrecognized escalade_type %d in freebsd_3ware_command_interface(disk %d)\n" | |
886 | "Please contact " PACKAGE_BUGREPORT "\n", m_escalade_type, m_disknum); | |
887 | errno=ENOSYS; | |
888 | return -1; | |
889 | } | |
890 | ||
891 | ata->opcode = TWE_OP_ATA_PASSTHROUGH; | |
892 | ||
893 | // Same for (almost) all commands - but some reset below | |
894 | ata->request_id = 0xFF; | |
895 | ata->unit = m_disknum; | |
896 | ata->status = 0; | |
897 | ata->flags = 0x1; | |
898 | ata->drive_head = 0x0; | |
899 | ata->sector_num = 0; | |
900 | ||
901 | // All SMART commands use this CL/CH signature. These are magic | |
902 | // values from the ATA specifications. | |
903 | ata->cylinder_lo = 0x4F; | |
904 | ata->cylinder_hi = 0xC2; | |
905 | ||
906 | // SMART ATA COMMAND REGISTER value | |
907 | ata->command = ATA_SMART_CMD; | |
908 | ||
909 | // Is this a command that reads or returns 512 bytes? | |
910 | // passthru->param values are: | |
911 | // 0x0 - non data command without TFR write check, | |
912 | // 0x8 - non data command with TFR write check, | |
913 | // 0xD - data command that returns data to host from device | |
914 | // 0xF - data command that writes data from host to device | |
915 | // passthru->size values are 0x5 for non-data and 0x07 for data | |
916 | if (command == READ_VALUES || | |
917 | command == READ_THRESHOLDS || | |
918 | command == READ_LOG || | |
919 | command == IDENTIFY || | |
920 | command == WRITE_LOG ) { | |
921 | readdata=1; | |
922 | if (m_escalade_type==CONTROLLER_3WARE_678K_CHAR) { | |
923 | cmd_twe->tu_data = data; | |
924 | cmd_twe->tu_size = 512; | |
925 | } | |
926 | ata->sgl_offset = 0x5; | |
927 | ata->size = 0x5; | |
928 | ata->param = 0xD; | |
929 | ata->sector_count = 0x1; | |
930 | // For 64-bit to work correctly, up the size of the command packet | |
931 | // in dwords by 1 to account for the 64-bit single sgl 'address' | |
932 | // field. Note that this doesn't agree with the typedefs but it's | |
933 | // right (agree with kernel driver behavior/typedefs). | |
934 | //if (sizeof(long)==8) | |
935 | // ata->size++; | |
936 | } | |
937 | else { | |
938 | // Non data command -- but doesn't use large sector | |
939 | // count register values. | |
940 | ata->sgl_offset = 0x0; | |
941 | ata->size = 0x5; | |
942 | ata->param = 0x8; | |
943 | ata->sector_count = 0x0; | |
944 | } | |
945 | ||
946 | // Now set ATA registers depending upon command | |
947 | switch (command){ | |
948 | case CHECK_POWER_MODE: | |
949 | ata->command = ATA_CHECK_POWER_MODE; | |
950 | ata->features = 0; | |
951 | ata->cylinder_lo = 0; | |
952 | ata->cylinder_hi = 0; | |
953 | break; | |
954 | case READ_VALUES: | |
955 | ata->features = ATA_SMART_READ_VALUES; | |
956 | break; | |
957 | case READ_THRESHOLDS: | |
958 | ata->features = ATA_SMART_READ_THRESHOLDS; | |
959 | break; | |
960 | case READ_LOG: | |
961 | ata->features = ATA_SMART_READ_LOG_SECTOR; | |
962 | // log number to return | |
963 | ata->sector_num = select; | |
964 | break; | |
965 | case WRITE_LOG: | |
966 | readdata=0; | |
967 | ata->features = ATA_SMART_WRITE_LOG_SECTOR; | |
968 | ata->sector_count = 1; | |
969 | ata->sector_num = select; | |
970 | ata->param = 0xF; // PIO data write | |
971 | break; | |
972 | case IDENTIFY: | |
973 | // ATA IDENTIFY DEVICE | |
974 | ata->command = ATA_IDENTIFY_DEVICE; | |
975 | ata->features = 0; | |
976 | ata->cylinder_lo = 0; | |
977 | ata->cylinder_hi = 0; | |
978 | break; | |
979 | case PIDENTIFY: | |
980 | // 3WARE controller can NOT have packet device internally | |
981 | pout("WARNING - NO DEVICE FOUND ON 3WARE CONTROLLER (disk %d)\n", m_disknum); | |
982 | errno=ENODEV; | |
983 | return -1; | |
984 | case ENABLE: | |
985 | ata->features = ATA_SMART_ENABLE; | |
986 | break; | |
987 | case DISABLE: | |
988 | ata->features = ATA_SMART_DISABLE; | |
989 | break; | |
990 | case AUTO_OFFLINE: | |
991 | ata->features = ATA_SMART_AUTO_OFFLINE; | |
992 | // Enable or disable? | |
993 | ata->sector_count = select; | |
994 | break; | |
995 | case AUTOSAVE: | |
996 | ata->features = ATA_SMART_AUTOSAVE; | |
997 | // Enable or disable? | |
998 | ata->sector_count = select; | |
999 | break; | |
1000 | case IMMEDIATE_OFFLINE: | |
1001 | ata->features = ATA_SMART_IMMEDIATE_OFFLINE; | |
1002 | // What test type to run? | |
1003 | ata->sector_num = select; | |
1004 | break; | |
1005 | case STATUS_CHECK: | |
1006 | ata->features = ATA_SMART_STATUS; | |
1007 | break; | |
1008 | case STATUS: | |
1009 | // This is JUST to see if SMART is enabled, by giving SMART status | |
1010 | // command. But it doesn't say if status was good, or failing. | |
1011 | // See below for the difference. | |
1012 | ata->features = ATA_SMART_STATUS; | |
1013 | break; | |
1014 | default: | |
1015 | pout("Unrecognized command %d in freebsd_3ware_command_interface(disk %d)\n" | |
1016 | "Please contact " PACKAGE_BUGREPORT "\n", command, m_disknum); | |
1017 | errno=ENOSYS; | |
1018 | return -1; | |
1019 | } | |
1020 | ||
1021 | // Now send the command down through an ioctl() | |
1022 | if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR) { | |
1023 | #ifdef IOCATAREQUEST | |
1024 | ioctlreturn=ioctl(con->device,TW_OSL_IOCTL_FIRMWARE_PASS_THROUGH,cmd_twa); | |
1025 | #else | |
1026 | ioctlreturn=ioctl(con->atacommand,TW_OSL_IOCTL_FIRMWARE_PASS_THROUGH,cmd_twa); | |
1027 | #endif | |
1028 | } else { | |
1029 | #ifdef IOCATAREQUEST | |
1030 | ioctlreturn=ioctl(con->device,TWEIO_COMMAND,cmd_twe); | |
1031 | #else | |
1032 | ioctlreturn=ioctl(con->atacommand,TWEIO_COMMAND,cmd_twe); | |
1033 | #endif | |
1034 | } | |
1035 | ||
1036 | // Deal with the different error cases | |
1037 | if (ioctlreturn) { | |
1038 | if (!errno) | |
1039 | errno=EIO; | |
1040 | return -1; | |
1041 | } | |
1042 | ||
1043 | // See if the ATA command failed. Now that we have returned from | |
1044 | // the ioctl() call, if passthru is valid, then: | |
1045 | // - ata->status contains the 3ware controller STATUS | |
1046 | // - ata->command contains the ATA STATUS register | |
1047 | // - ata->features contains the ATA ERROR register | |
1048 | // | |
1049 | // Check bits 0 (error bit) and 5 (device fault) of the ATA STATUS | |
1050 | // If bit 0 (error bit) is set, then ATA ERROR register is valid. | |
1051 | // While we *might* decode the ATA ERROR register, at the moment it | |
1052 | // doesn't make much sense: we don't care in detail why the error | |
1053 | // happened. | |
1054 | ||
1055 | if (ata->status || (ata->command & 0x21)) { | |
1056 | 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); | |
1057 | errno=EIO; | |
1058 | return -1; | |
1059 | } | |
1060 | ||
1061 | // If this is a read data command, copy data to output buffer | |
1062 | if (readdata) { | |
1063 | if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR) | |
1064 | memcpy(data, cmd_twa->pdata, 512); | |
1065 | } | |
1066 | ||
1067 | // For STATUS_CHECK, we need to check register values | |
1068 | if (command==STATUS_CHECK) { | |
1069 | ||
1070 | // To find out if the SMART RETURN STATUS is good or failing, we | |
1071 | // need to examine the values of the Cylinder Low and Cylinder | |
1072 | // High Registers. | |
1073 | ||
1074 | unsigned short cyl_lo=ata->cylinder_lo; | |
1075 | unsigned short cyl_hi=ata->cylinder_hi; | |
1076 | ||
1077 | // If values in Cyl-LO and Cyl-HI are unchanged, SMART status is good. | |
1078 | if (cyl_lo==0x4F && cyl_hi==0xC2) | |
1079 | return 0; | |
1080 | ||
1081 | // If values in Cyl-LO and Cyl-HI are as follows, SMART status is FAIL | |
1082 | if (cyl_lo==0xF4 && cyl_hi==0x2C) | |
1083 | return 1; | |
1084 | ||
1085 | errno=EIO; | |
1086 | return -1; | |
1087 | } | |
1088 | ||
1089 | // copy sector count register (one byte!) to return data | |
1090 | if (command==CHECK_POWER_MODE) | |
1091 | *data=*(char *)&(ata->sector_count); | |
1092 | ||
1093 | // look for nonexistent devices/ports | |
1094 | if (command==IDENTIFY && !nonempty((unsigned char *)data, 512)) { | |
1095 | errno=ENODEV; | |
1096 | return -1; | |
1097 | } | |
1098 | ||
1099 | return 0; | |
1100 | } | |
1101 | ||
1102 | ||
1103 | ///////////////////////////////////////////////////////////////////////////// | |
1104 | /// Implement Highpoint RAID support with old functions | |
1105 | ||
1106 | class freebsd_highpoint_device | |
1107 | : public /*implements*/ ata_device_with_command_set, | |
1108 | public /*extends*/ freebsd_smart_device | |
1109 | { | |
1110 | public: | |
1111 | freebsd_highpoint_device(smart_interface * intf, const char * dev_name, | |
1112 | unsigned char controller, unsigned char channel, unsigned char port); | |
1113 | ||
1114 | protected: | |
1115 | virtual int ata_command_interface(smart_command_set command, int select, char * data); | |
1116 | ||
1117 | private: | |
1118 | unsigned char m_hpt_data[3]; ///< controller/channel/port | |
1119 | }; | |
1120 | ||
1121 | ||
1122 | freebsd_highpoint_device::freebsd_highpoint_device(smart_interface * intf, const char * dev_name, | |
1123 | unsigned char controller, unsigned char channel, unsigned char port) | |
1124 | : smart_device(intf, dev_name, "hpt", "hpt"), | |
1125 | freebsd_smart_device("ATA") | |
1126 | { | |
1127 | m_hpt_data[0] = controller; m_hpt_data[1] = channel; m_hpt_data[2] = port; | |
1128 | 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]); | |
1129 | } | |
1130 | ||
1131 | int freebsd_highpoint_device::ata_command_interface(smart_command_set command, int select, char * data) | |
1132 | { | |
1133 | int fd=get_fd(); | |
1134 | int ids[2]; | |
1135 | struct freebsd_dev_channel* fbcon; | |
1136 | HPT_IOCTL_PARAM param; | |
1137 | HPT_CHANNEL_INFO_V2 info; | |
1138 | unsigned char* buff[512 + 2 * sizeof(HPT_PASS_THROUGH_HEADER)]; | |
1139 | PHPT_PASS_THROUGH_HEADER pide_pt_hdr, pide_pt_hdr_out; | |
1140 | ||
1141 | // check that "file descriptor" is valid | |
1142 | if (isnotopen(&fd, &fbcon)) | |
1143 | return -1; | |
1144 | ||
1145 | // get internal deviceid | |
1146 | ids[0] = m_hpt_data[0] - 1; | |
1147 | ids[1] = m_hpt_data[1] - 1; | |
1148 | ||
1149 | memset(¶m, 0, sizeof(HPT_IOCTL_PARAM)); | |
1150 | ||
1151 | param.magic = HPT_IOCTL_MAGIC; | |
1152 | param.ctrl_code = HPT_IOCTL_GET_CHANNEL_INFO_V2; | |
1153 | param.in = (unsigned char *)ids; | |
1154 | param.in_size = sizeof(unsigned int) * 2; | |
1155 | param.out = (unsigned char *)&info; | |
1156 | param.out_size = sizeof(HPT_CHANNEL_INFO_V2); | |
1157 | ||
1158 | if (m_hpt_data[2]==1) { | |
1159 | param.ctrl_code = HPT_IOCTL_GET_CHANNEL_INFO; | |
1160 | param.out_size = sizeof(HPT_CHANNEL_INFO); | |
1161 | } | |
1162 | if (ioctl(fbcon->device, HPT_DO_IOCONTROL, ¶m)!=0 || | |
1163 | info.devices[m_hpt_data[2]-1]==0) { | |
1164 | return -1; | |
1165 | } | |
1166 | ||
1167 | // perform smart action | |
1168 | memset(buff, 0, 512 + 2 * sizeof(HPT_PASS_THROUGH_HEADER)); | |
1169 | pide_pt_hdr = (PHPT_PASS_THROUGH_HEADER)buff; | |
1170 | ||
1171 | pide_pt_hdr->lbamid = 0x4f; | |
1172 | pide_pt_hdr->lbahigh = 0xc2; | |
1173 | pide_pt_hdr->command = ATA_SMART_CMD; | |
1174 | pide_pt_hdr->id = info.devices[m_hpt_data[2] - 1]; | |
1175 | ||
1176 | switch (command){ | |
1177 | case READ_VALUES: | |
1178 | pide_pt_hdr->feature=ATA_SMART_READ_VALUES; | |
1179 | pide_pt_hdr->protocol=HPT_READ; | |
1180 | break; | |
1181 | case READ_THRESHOLDS: | |
1182 | pide_pt_hdr->feature=ATA_SMART_READ_THRESHOLDS; | |
1183 | pide_pt_hdr->protocol=HPT_READ; | |
1184 | break; | |
1185 | case READ_LOG: | |
1186 | pide_pt_hdr->feature=ATA_SMART_READ_LOG_SECTOR; | |
1187 | pide_pt_hdr->lbalow=select; | |
1188 | pide_pt_hdr->protocol=HPT_READ; | |
1189 | break; | |
1190 | case IDENTIFY: | |
1191 | pide_pt_hdr->command=ATA_IDENTIFY_DEVICE; | |
1192 | pide_pt_hdr->protocol=HPT_READ; | |
1193 | break; | |
1194 | case ENABLE: | |
1195 | pide_pt_hdr->feature=ATA_SMART_ENABLE; | |
1196 | break; | |
1197 | case DISABLE: | |
1198 | pide_pt_hdr->feature=ATA_SMART_DISABLE; | |
1199 | break; | |
1200 | case AUTO_OFFLINE: | |
1201 | pide_pt_hdr->feature=ATA_SMART_AUTO_OFFLINE; | |
1202 | pide_pt_hdr->sectorcount=select; | |
1203 | break; | |
1204 | case AUTOSAVE: | |
1205 | pide_pt_hdr->feature=ATA_SMART_AUTOSAVE; | |
1206 | pide_pt_hdr->sectorcount=select; | |
1207 | break; | |
1208 | case IMMEDIATE_OFFLINE: | |
1209 | pide_pt_hdr->feature=ATA_SMART_IMMEDIATE_OFFLINE; | |
1210 | pide_pt_hdr->lbalow=select; | |
1211 | break; | |
1212 | case STATUS_CHECK: | |
1213 | case STATUS: | |
1214 | pide_pt_hdr->feature=ATA_SMART_STATUS; | |
1215 | break; | |
1216 | case CHECK_POWER_MODE: | |
1217 | pide_pt_hdr->command=ATA_CHECK_POWER_MODE; | |
1218 | break; | |
1219 | case WRITE_LOG: | |
1220 | memcpy(buff+sizeof(HPT_PASS_THROUGH_HEADER), data, 512); | |
1221 | pide_pt_hdr->feature=ATA_SMART_WRITE_LOG_SECTOR; | |
1222 | pide_pt_hdr->lbalow=select; | |
1223 | pide_pt_hdr->protocol=HPT_WRITE; | |
1224 | break; | |
1225 | default: | |
1226 | pout("Unrecognized command %d in highpoint_command_interface()\n" | |
1227 | "Please contact " PACKAGE_BUGREPORT "\n", command); | |
1228 | errno=ENOSYS; | |
1229 | return -1; | |
1230 | } | |
1231 | if (pide_pt_hdr->protocol!=0) { | |
1232 | pide_pt_hdr->sectors = 1; | |
1233 | pide_pt_hdr->sectorcount = 1; | |
1234 | } | |
1235 | ||
1236 | memset(¶m, 0, sizeof(HPT_IOCTL_PARAM)); | |
1237 | ||
1238 | param.magic = HPT_IOCTL_MAGIC; | |
1239 | param.ctrl_code = HPT_IOCTL_IDE_PASS_THROUGH; | |
1240 | param.in = (unsigned char *)buff; | |
1241 | param.in_size = sizeof(HPT_PASS_THROUGH_HEADER) + (pide_pt_hdr->protocol==HPT_READ ? 0 : pide_pt_hdr->sectors * 512); | |
1242 | param.out = (unsigned char *)buff+param.in_size; | |
1243 | param.out_size = sizeof(HPT_PASS_THROUGH_HEADER) + (pide_pt_hdr->protocol==HPT_READ ? pide_pt_hdr->sectors * 512 : 0); | |
1244 | ||
1245 | pide_pt_hdr_out = (PHPT_PASS_THROUGH_HEADER)param.out; | |
1246 | ||
1247 | if ((ioctl(fbcon->device, HPT_DO_IOCONTROL, ¶m)!=0) || | |
1248 | (pide_pt_hdr_out->command & 1)) { | |
1249 | return -1; | |
1250 | } | |
1251 | ||
1252 | if (command==STATUS_CHECK){ | |
1253 | unsigned const char normal_lo=0x4f, normal_hi=0xc2; | |
1254 | unsigned const char failed_lo=0xf4, failed_hi=0x2c; | |
1255 | unsigned char low,high; | |
1256 | ||
1257 | high = pide_pt_hdr_out->lbahigh; | |
1258 | low = pide_pt_hdr_out->lbamid; | |
1259 | ||
1260 | // Cyl low and Cyl high unchanged means "Good SMART status" | |
1261 | if (low==normal_lo && high==normal_hi) | |
1262 | return 0; | |
1263 | ||
1264 | // These values mean "Bad SMART status" | |
1265 | if (low==failed_lo && high==failed_hi) | |
1266 | return 1; | |
1267 | ||
1268 | // We haven't gotten output that makes sense; print out some debugging info | |
1269 | char buf[512]; | |
1270 | sprintf(buf,"CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n", | |
1271 | (int)pide_pt_hdr_out->command, | |
1272 | (int)pide_pt_hdr_out->feature, | |
1273 | (int)pide_pt_hdr_out->sectorcount, | |
1274 | (int)pide_pt_hdr_out->lbalow, | |
1275 | (int)pide_pt_hdr_out->lbamid, | |
1276 | (int)pide_pt_hdr_out->lbahigh, | |
1277 | (int)pide_pt_hdr_out->sectors); | |
1278 | printwarning(BAD_SMART,buf); | |
1279 | } | |
1280 | else if (command==CHECK_POWER_MODE) | |
1281 | data[0] = pide_pt_hdr_out->sectorcount & 0xff; | |
1282 | else if (pide_pt_hdr->protocol==HPT_READ) | |
1283 | memcpy(data, (unsigned char *)buff + 2 * sizeof(HPT_PASS_THROUGH_HEADER), pide_pt_hdr->sectors * 512); | |
1284 | return 0; | |
1285 | } | |
1286 | ||
1287 | ||
1288 | ///////////////////////////////////////////////////////////////////////////// | |
1289 | /// Implement standard SCSI support with old functions | |
1290 | ||
1291 | class freebsd_scsi_device | |
1292 | : public /*implements*/ scsi_device, | |
1293 | public /*extends*/ freebsd_smart_device | |
1294 | { | |
1295 | public: | |
1296 | freebsd_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type); | |
1297 | ||
1298 | virtual smart_device * autodetect_open(); | |
1299 | ||
1300 | virtual bool scsi_pass_through(scsi_cmnd_io * iop); | |
1301 | }; | |
1302 | ||
1303 | freebsd_scsi_device::freebsd_scsi_device(smart_interface * intf, | |
1304 | const char * dev_name, const char * req_type) | |
1305 | : smart_device(intf, dev_name, "scsi", req_type), | |
1306 | freebsd_smart_device("SCSI") | |
1307 | { | |
1308 | } | |
1309 | ||
1310 | // Interface to SCSI devices. See os_linux.c | |
1311 | int do_normal_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) | |
1312 | { | |
1313 | struct freebsd_dev_channel* con = NULL; | |
1314 | struct cam_device* cam_dev = NULL; | |
1315 | union ccb *ccb; | |
1316 | ||
1317 | ||
1318 | if (report > 0) { | |
1319 | unsigned int k; | |
1320 | const unsigned char * ucp = iop->cmnd; | |
1321 | const char * np; | |
1322 | ||
1323 | np = scsi_get_opcode_name(ucp[0]); | |
1324 | pout(" [%s: ", np ? np : "<unknown opcode>"); | |
1325 | for (k = 0; k < iop->cmnd_len; ++k) | |
1326 | pout("%02x ", ucp[k]); | |
1327 | if ((report > 1) && | |
1328 | (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { | |
1329 | int trunc = (iop->dxfer_len > 256) ? 1 : 0; | |
1330 | ||
1331 | pout("]\n Outgoing data, len=%d%s:\n", (int)iop->dxfer_len, | |
1332 | (trunc ? " [only first 256 bytes shown]" : "")); | |
1333 | dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); | |
1334 | } | |
1335 | else | |
1336 | pout("]"); | |
1337 | } | |
1338 | ||
1339 | // check that "file descriptor" is valid | |
1340 | if (isnotopen(&fd,&con)) | |
1341 | return -ENOTTY; | |
1342 | ||
1343 | ||
1344 | if (!(cam_dev = cam_open_spec_device(con->devname,con->unitnum,O_RDWR,NULL))) { | |
1345 | warnx("%s",cam_errbuf); | |
1346 | return -EIO; | |
1347 | } | |
1348 | ||
1349 | if (!(ccb = cam_getccb(cam_dev))) { | |
1350 | warnx("error allocating ccb"); | |
1351 | return -ENOMEM; | |
1352 | } | |
1353 | ||
1354 | // clear out structure, except for header that was filled in for us | |
1355 | bzero(&(&ccb->ccb_h)[1], | |
1356 | sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); | |
1357 | ||
1358 | cam_fill_csio(&ccb->csio, | |
1359 | /*retrires*/ 1, | |
1360 | /*cbfcnp*/ NULL, | |
1361 | /* flags */ (iop->dxfer_dir == DXFER_NONE ? CAM_DIR_NONE :(iop->dxfer_dir == DXFER_FROM_DEVICE ? CAM_DIR_IN : CAM_DIR_OUT)), | |
1362 | /* tagaction */ MSG_SIMPLE_Q_TAG, | |
1363 | /* dataptr */ iop->dxferp, | |
1364 | /* datalen */ iop->dxfer_len, | |
1365 | /* senselen */ iop->max_sense_len, | |
1366 | /* cdblen */ iop->cmnd_len, | |
1367 | /* timout (converted to seconds) */ iop->timeout*1000); | |
1368 | memcpy(ccb->csio.cdb_io.cdb_bytes,iop->cmnd,iop->cmnd_len); | |
1369 | ||
1370 | if (cam_send_ccb(cam_dev,ccb) < 0) { | |
1371 | warn("error sending SCSI ccb"); | |
1372 | #if __FreeBSD_version > 500000 | |
1373 | cam_error_print(cam_dev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr); | |
1374 | #endif | |
1375 | cam_freeccb(ccb); | |
1376 | return -EIO; | |
1377 | } | |
1378 | ||
1379 | if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { | |
1380 | #if __FreeBSD_version > 500000 | |
1381 | cam_error_print(cam_dev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr); | |
1382 | #endif | |
1383 | cam_freeccb(ccb); | |
1384 | return -EIO; | |
1385 | } | |
1386 | ||
1387 | if (iop->sensep) { | |
1388 | memcpy(iop->sensep,&(ccb->csio.sense_data),sizeof(struct scsi_sense_data)); | |
1389 | iop->resp_sense_len = sizeof(struct scsi_sense_data); | |
1390 | } | |
1391 | ||
1392 | iop->scsi_status = ccb->csio.scsi_status; | |
1393 | ||
1394 | cam_freeccb(ccb); | |
1395 | ||
1396 | if (cam_dev) | |
1397 | cam_close_device(cam_dev); | |
1398 | ||
1399 | if (report > 0) { | |
1400 | int trunc; | |
1401 | ||
1402 | pout(" status=0\n"); | |
1403 | trunc = (iop->dxfer_len > 256) ? 1 : 0; | |
1404 | ||
1405 | pout(" Incoming data, len=%d%s:\n", (int)iop->dxfer_len, | |
1406 | (trunc ? " [only first 256 bytes shown]" : "")); | |
1407 | dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); | |
1408 | } | |
1409 | return 0; | |
1410 | } | |
1411 | ||
1412 | ||
1413 | /* Check and call the right interface. Maybe when the do_generic_scsi_cmd_io interface is better | |
1414 | we can take off this crude way of calling the right interface */ | |
1415 | int do_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report) | |
1416 | { | |
1417 | struct freebsd_dev_channel *fdchan; | |
1418 | switch(m_controller_type) | |
1419 | { | |
1420 | case CONTROLLER_CCISS: | |
1421 | #ifdef HAVE_DEV_CISS_CISSIO_H | |
1422 | // check that "file descriptor" is valid | |
1423 | if (isnotopen(&dev_fd,&fdchan)) | |
1424 | return -ENOTTY; | |
1425 | return cciss_io_interface(fdchan->device, m_controller_port-1, iop, report); | |
1426 | #else | |
1427 | { | |
1428 | static int warned = 0; | |
1429 | if (!warned) { | |
1430 | pout("CCISS support is not available in this build of smartmontools,\n" | |
1431 | "/usr/src/sys/dev/ciss/cissio.h was not available at build time.\n\n"); | |
1432 | warned = 1; | |
1433 | } | |
1434 | } | |
1435 | return -ENOSYS; | |
1436 | #endif | |
1437 | // not reached | |
1438 | break; | |
1439 | default: | |
1440 | return do_normal_scsi_cmnd_io(dev_fd, iop, report); | |
1441 | // not reached | |
1442 | break; | |
1443 | } | |
1444 | } | |
1445 | ||
1446 | bool freebsd_scsi_device::scsi_pass_through(scsi_cmnd_io * iop) | |
1447 | { | |
1448 | unsigned char oldtype = m_controller_type, oldport = m_controller_port; | |
1449 | m_controller_type = CONTROLLER_SCSI; m_controller_port = 0; | |
1450 | int status = do_scsi_cmnd_io(get_fd(), iop, con->reportscsiioctl); | |
1451 | m_controller_type = oldtype; m_controller_port = oldport; | |
1452 | if (status < 0) { | |
1453 | set_err(-status); | |
1454 | return false; | |
1455 | } | |
1456 | return true; | |
1457 | } | |
1458 | ||
1459 | ||
1460 | ///////////////////////////////////////////////////////////////////////////// | |
1461 | /// Implement CCISS RAID support with old functions | |
1462 | ||
1463 | class freebsd_cciss_device | |
1464 | : public /*implements*/ scsi_device, | |
1465 | public /*extends*/ freebsd_smart_device | |
1466 | { | |
1467 | public: | |
1468 | freebsd_cciss_device(smart_interface * intf, const char * name, unsigned char disknum); | |
1469 | ||
1470 | virtual bool scsi_pass_through(scsi_cmnd_io * iop); | |
1471 | ||
1472 | private: | |
1473 | unsigned char m_disknum; ///< Disk number. | |
1474 | }; | |
1475 | ||
1476 | ||
1477 | freebsd_cciss_device::freebsd_cciss_device(smart_interface * intf, | |
1478 | const char * dev_name, unsigned char disknum) | |
1479 | : smart_device(intf, dev_name, "cciss", "cciss"), | |
1480 | freebsd_smart_device("SCSI"), | |
1481 | m_disknum(disknum) | |
1482 | { | |
1483 | set_info().info_name = strprintf("%s [cciss_disk_%02d]", dev_name, disknum); | |
1484 | } | |
1485 | ||
1486 | bool freebsd_cciss_device::scsi_pass_through(scsi_cmnd_io * iop) | |
1487 | { | |
1488 | // See os_linux.cpp | |
1489 | unsigned char oldtype = m_controller_type, oldport = m_controller_port; | |
1490 | m_controller_type = CONTROLLER_CCISS; m_controller_port = m_disknum+1; | |
1491 | int status = do_scsi_cmnd_io(get_fd(), iop, con->reportscsiioctl); | |
1492 | m_controller_type = oldtype; m_controller_port = oldport; | |
1493 | if (status < 0) { | |
1494 | set_err(-status); | |
1495 | return false; | |
1496 | } | |
1497 | return true; | |
1498 | } | |
1499 | ||
1500 | ||
1501 | ///////////////////////////////////////////////////////////////////////////// | |
1502 | /// SCSI open with autodetection support | |
1503 | ||
1504 | smart_device * freebsd_scsi_device::autodetect_open() | |
1505 | { | |
1506 | // Open device | |
1507 | if (!open()) | |
1508 | return this; | |
1509 | ||
1510 | // No Autodetection if device type was specified by user | |
1511 | if (*get_req_type()) | |
1512 | return this; | |
1513 | ||
1514 | // The code below is based on smartd.cpp:SCSIFilterKnown() | |
1515 | ||
1516 | // Get INQUIRY | |
1517 | unsigned char req_buff[64] = {0, }; | |
1518 | int req_len = 36; | |
1519 | if (scsiStdInquiry(this, req_buff, req_len)) { | |
1520 | // Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices | |
1521 | // watch this spot ... other devices could lock up here | |
1522 | req_len = 64; | |
1523 | if (scsiStdInquiry(this, req_buff, req_len)) { | |
1524 | // device doesn't like INQUIRY commands | |
1525 | close(); | |
1526 | set_err(EIO, "INQUIRY failed"); | |
1527 | return this; | |
1528 | } | |
1529 | } | |
1530 | ||
1531 | int avail_len = req_buff[4] + 5; | |
1532 | int len = (avail_len < req_len ? avail_len : req_len); | |
1533 | if (len < 36) | |
1534 | return this; | |
1535 | ||
1536 | // Use INQUIRY to detect type | |
1537 | smart_device * newdev = 0; | |
1538 | try { | |
1539 | // 3ware ? | |
1540 | if (!memcmp(req_buff + 8, "3ware", 5) || !memcmp(req_buff + 8, "AMCC", 4)) { | |
1541 | close(); | |
1542 | #if defined(_WIN32) || defined(__CYGWIN__) | |
1543 | set_err(EINVAL, "AMCC/3ware controller, please try changing device to %s,N", get_dev_name()); | |
1544 | #else | |
1545 | set_err(EINVAL, "AMCC/3ware controller, please try adding '-d 3ware,N',\n" | |
1546 | "you may need to replace %s with /dev/twaN or /dev/tweN", get_dev_name()); | |
1547 | #endif | |
1548 | return this; | |
1549 | } | |
1550 | ||
1551 | // SAT or USB ? | |
1552 | newdev = smi()->autodetect_sat_device(this, req_buff, len); | |
1553 | if (newdev) | |
1554 | // NOTE: 'this' is now owned by '*newdev' | |
1555 | return newdev; | |
1556 | } | |
1557 | catch (...) { | |
1558 | // Cleanup if exception occurs after newdev was allocated | |
1559 | delete newdev; | |
1560 | throw; | |
1561 | } | |
1562 | ||
1563 | // Nothing special found | |
1564 | return this; | |
1565 | } | |
1566 | ||
1567 | ||
1568 | ///////////////////////////////////////////////////////////////////////////// | |
1569 | /// Implement platform interface with old functions. | |
1570 | ||
1571 | class freebsd_smart_interface | |
1572 | : public /*implements*/ smart_interface | |
1573 | { | |
1574 | public: | |
1575 | virtual const char * get_os_version_str(); | |
1576 | ||
1577 | virtual const char * get_app_examples(const char * appname); | |
1578 | ||
1579 | virtual bool scan_smart_devices(smart_device_list & devlist, const char * type, | |
1580 | const char * pattern = 0); | |
1581 | ||
1582 | protected: | |
1583 | virtual ata_device * get_ata_device(const char * name, const char * type); | |
1584 | ||
1585 | virtual scsi_device * get_scsi_device(const char * name, const char * type); | |
1586 | ||
1587 | virtual smart_device * autodetect_smart_device(const char * name); | |
1588 | ||
1589 | virtual smart_device * get_custom_smart_device(const char * name, const char * type); | |
1590 | ||
1591 | virtual const char * get_valid_custom_dev_types_str(); | |
1592 | }; | |
1593 | ||
1594 | ||
1595 | ////////////////////////////////////////////////////////////////////// | |
1596 | char sysname[256]; | |
1597 | const char * freebsd_smart_interface::get_os_version_str() | |
1598 | { | |
1599 | struct utsname osname; | |
1600 | uname(&osname); | |
1601 | snprintf(sysname, sizeof(sysname),"%s %s %s",osname.sysname, osname.release, | |
1602 | osname.machine); | |
1603 | return sysname; | |
1604 | } | |
1605 | ||
1606 | const char * freebsd_smart_interface::get_app_examples(const char * appname) | |
1607 | { | |
1608 | if (!strcmp(appname, "smartctl")) | |
1609 | return smartctl_examples; | |
1610 | return 0; | |
1611 | } | |
1612 | ||
1613 | ata_device * freebsd_smart_interface::get_ata_device(const char * name, const char * type) | |
1614 | { | |
1615 | return new freebsd_ata_device(this, name, type); | |
1616 | } | |
1617 | ||
1618 | scsi_device * freebsd_smart_interface::get_scsi_device(const char * name, const char * type) | |
1619 | { | |
1620 | return new freebsd_scsi_device(this, name, type); | |
1621 | } | |
832b75ed | 1622 | |
2127e193 GI |
1623 | static int |
1624 | cam_getumassno(char * devname) { | |
1625 | union ccb ccb; | |
1626 | int bufsize, fd; | |
1627 | unsigned int i; | |
1628 | int error = -1; | |
1629 | char devstring[256]; | |
1630 | ||
1631 | if ((fd = open(XPT_DEVICE, O_RDWR)) == -1) { | |
1632 | warn("couldn't open %s", XPT_DEVICE); | |
1633 | return(1); | |
1634 | } | |
1635 | bzero(&ccb, sizeof(union ccb)); | |
1636 | ||
1637 | ccb.ccb_h.path_id = CAM_XPT_PATH_ID; | |
1638 | ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; | |
1639 | ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; | |
1640 | ||
1641 | ccb.ccb_h.func_code = XPT_DEV_MATCH; | |
1642 | bufsize = sizeof(struct dev_match_result) * 100; | |
1643 | ccb.cdm.match_buf_len = bufsize; | |
1644 | ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize); | |
1645 | if (ccb.cdm.matches == NULL) { | |
1646 | warnx("can't malloc memory for matches"); | |
1647 | close(fd); | |
1648 | return(1); | |
1649 | } | |
1650 | ccb.cdm.num_matches = 0; | |
1651 | /* | |
1652 | * We fetch all nodes, since we display most of them in the default | |
1653 | * case, and all in the verbose case. | |
1654 | */ | |
1655 | ccb.cdm.num_patterns = 0; | |
1656 | ccb.cdm.pattern_buf_len = 0; | |
1657 | /* | |
1658 | * We do the ioctl multiple times if necessary, in case there are | |
1659 | * more than 100 nodes in the EDT. | |
1660 | */ | |
1661 | ||
1662 | do { | |
1663 | if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) { | |
1664 | warn("error sending CAMIOCOMMAND ioctl"); | |
1665 | error = -1; | |
1666 | break; | |
1667 | } | |
1668 | if ((ccb.ccb_h.status != CAM_REQ_CMP) | |
1669 | || ((ccb.cdm.status != CAM_DEV_MATCH_LAST) | |
1670 | && (ccb.cdm.status != CAM_DEV_MATCH_MORE))) { | |
1671 | warnx("got CAM error %#x, CDM error %d\n", | |
1672 | ccb.ccb_h.status, ccb.cdm.status); | |
1673 | error = -1; | |
1674 | break; | |
1675 | } | |
1676 | ||
1677 | struct bus_match_result *bus_result = 0; | |
1678 | for (i = 0; i < ccb.cdm.num_matches; i++) { | |
1679 | switch (ccb.cdm.matches[i].type) { | |
1680 | case DEV_MATCH_BUS: { | |
1681 | // struct bus_match_result *bus_result; | |
1682 | bus_result = | |
1683 | &ccb.cdm.matches[i].result.bus_result; | |
1684 | break; | |
1685 | } | |
1686 | case DEV_MATCH_DEVICE: { | |
1687 | /* we are not interested in device name */ | |
1688 | break; | |
1689 | } | |
1690 | case DEV_MATCH_PERIPH: { | |
1691 | struct periph_match_result *periph_result; | |
1692 | ||
1693 | periph_result = | |
1694 | &ccb.cdm.matches[i].result.periph_result; | |
1695 | ||
1696 | snprintf(devstring,sizeof(devstring),"%s%d",periph_result->periph_name,periph_result->unit_number); | |
1697 | if(strcmp(devstring,devname)==0){ /* found our device */ | |
1698 | if(strcmp(bus_result->dev_name,"umass-sim")) { | |
1699 | close(fd); | |
1700 | return -1; /* non usb device found, giving up */ | |
1701 | } | |
1702 | /* return bus number */ | |
1703 | return bus_result->unit_number; | |
1704 | } | |
1705 | break; | |
1706 | } | |
1707 | ||
1708 | default: | |
1709 | fprintf(stdout, "WARN: unknown match type\n"); | |
1710 | break; | |
1711 | } | |
1712 | } | |
1713 | ||
1714 | } while ((ccb.ccb_h.status == CAM_REQ_CMP) | |
1715 | && (ccb.cdm.status == CAM_DEV_MATCH_MORE)); | |
1716 | close(fd); | |
1717 | free(ccb.cdm.matches); | |
1718 | return(error); /* no device found */ | |
832b75ed GG |
1719 | } |
1720 | ||
1721 | ||
2127e193 GI |
1722 | // we are using CAM subsystem XPT enumerator to found all SCSI devices on system |
1723 | // despite of it's names | |
1724 | // | |
1725 | // If any errors occur, leave errno set as it was returned by the | |
1726 | // system call, and return <0. | |
1727 | // | |
1728 | // Return values: | |
1729 | // -1: error | |
1730 | // >=0: number of discovered devices | |
832b75ed | 1731 | |
2127e193 GI |
1732 | int get_dev_names_scsi(char*** names) { |
1733 | int n = 0; | |
1734 | char** mp = NULL; | |
1735 | unsigned int i; | |
1736 | union ccb ccb; | |
1737 | int bufsize, fd = -1; | |
1738 | int skip_device = 0, skip_bus = 0, changed = 0; | |
1739 | char *devname = NULL; | |
1740 | int serrno=-1; | |
1741 | ||
1742 | // in case of non-clean exit | |
1743 | *names=NULL; | |
1744 | ccb.cdm.matches = NULL; | |
1745 | ||
1746 | if ((fd = open(XPT_DEVICE, O_RDWR)) == -1) { | |
1747 | if (errno == ENOENT) /* There are no CAM device on this computer */ | |
1748 | return 0; | |
1749 | serrno = errno; | |
1750 | pout("%s control device couldn't opened: %s\n", XPT_DEVICE, strerror(errno)); | |
1751 | n = -1; | |
1752 | goto end; | |
832b75ed | 1753 | } |
2127e193 GI |
1754 | |
1755 | // allocate space for up to MAX_NUM_DEV number of ATA devices | |
1756 | mp = (char **)calloc(MAX_NUM_DEV, sizeof(char*)); | |
1757 | if (mp == NULL) { | |
1758 | serrno=errno; | |
1759 | pout("Out of memory constructing scan device list (on line %d)\n", __LINE__); | |
1760 | n = -1; | |
1761 | goto end; | |
1762 | }; | |
1763 | ||
1764 | bzero(&ccb, sizeof(union ccb)); | |
1765 | ||
1766 | ccb.ccb_h.path_id = CAM_XPT_PATH_ID; | |
1767 | ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; | |
1768 | ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; | |
1769 | ||
1770 | ccb.ccb_h.func_code = XPT_DEV_MATCH; | |
1771 | bufsize = sizeof(struct dev_match_result) * MAX_NUM_DEV; | |
1772 | ccb.cdm.match_buf_len = bufsize; | |
1773 | ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize); | |
1774 | if (ccb.cdm.matches == NULL) { | |
1775 | serrno = errno; | |
1776 | pout("can't malloc memory for matches on line %d\n", __LINE__); | |
1777 | n = -1; | |
1778 | goto end; | |
1779 | } | |
1780 | ccb.cdm.num_matches = 0; | |
1781 | ||
1782 | ccb.cdm.num_patterns = 0; | |
1783 | ccb.cdm.pattern_buf_len = 0; | |
1784 | ||
1785 | /* | |
1786 | * We do the ioctl multiple times if necessary, in case there are | |
1787 | * more than MAX_NUM_DEV nodes in the EDT. | |
1788 | */ | |
1789 | do { | |
1790 | if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) { | |
1791 | serrno = errno; | |
1792 | pout("error sending CAMIOCOMMAND ioctl: %s\n", strerror(errno)); | |
1793 | n = -1; | |
832b75ed GG |
1794 | break; |
1795 | } | |
2127e193 GI |
1796 | |
1797 | if ((ccb.ccb_h.status != CAM_REQ_CMP) | |
1798 | || ((ccb.cdm.status != CAM_DEV_MATCH_LAST) | |
1799 | && (ccb.cdm.status != CAM_DEV_MATCH_MORE))) { | |
1800 | pout("got CAM error %#x, CDM error %d\n", ccb.ccb_h.status, ccb.cdm.status); | |
1801 | serrno = ENXIO; | |
1802 | n = -1; | |
1803 | goto end; | |
832b75ed | 1804 | } |
2127e193 GI |
1805 | |
1806 | for (i = 0; i < ccb.cdm.num_matches && n < MAX_NUM_DEV; i++) { | |
1807 | struct bus_match_result *bus_result; | |
1808 | struct device_match_result *dev_result; | |
1809 | struct periph_match_result *periph_result; | |
1810 | ||
1811 | if (ccb.cdm.matches[i].type == DEV_MATCH_BUS) { | |
1812 | bus_result = &ccb.cdm.matches[i].result.bus_result; | |
1813 | ||
1814 | if (strcmp(bus_result->dev_name,"ata") == 0 /* ATAPICAM devices will be probed as ATA devices, skip'em there */ | |
1815 | || strcmp(bus_result->dev_name,"xpt") == 0) /* skip XPT bus at all */ | |
1816 | skip_bus = 1; | |
1817 | else | |
1818 | skip_bus = 0; | |
1819 | changed = 1; | |
1820 | } else if (ccb.cdm.matches[i].type == DEV_MATCH_DEVICE) { | |
1821 | dev_result = &ccb.cdm.matches[i].result.device_result; | |
1822 | ||
1823 | if (dev_result->flags & DEV_RESULT_UNCONFIGURED || skip_bus == 1) | |
1824 | skip_device = 1; | |
1825 | else | |
1826 | skip_device = 0; | |
1827 | ||
1828 | // /* Shall we skip non T_DIRECT devices ? */ | |
1829 | // if (dev_result->inq_data.device != T_DIRECT) | |
1830 | // skip_device = 1; | |
1831 | changed = 1; | |
1832 | } else if (ccb.cdm.matches[i].type == DEV_MATCH_PERIPH && skip_device == 0) { | |
1833 | /* One device may be populated as many peripherals (pass0 & da0 for example). | |
1834 | * We are searching for latest name | |
1835 | */ | |
1836 | periph_result = &ccb.cdm.matches[i].result.periph_result; | |
1837 | free(devname); | |
1838 | asprintf(&devname, "%s%s%d", _PATH_DEV, periph_result->periph_name, periph_result->unit_number); | |
1839 | if (devname == NULL) { | |
1840 | serrno=errno; | |
1841 | pout("Out of memory constructing scan SCSI device list (on line %d)\n", __LINE__); | |
1842 | n = -1; | |
1843 | goto end; | |
1844 | }; | |
1845 | changed = 0; | |
1846 | }; | |
1847 | ||
1848 | if (changed == 1 && devname != NULL) { | |
1849 | mp[n] = devname; | |
1850 | devname = NULL; | |
1851 | bytes+=1+strlen(mp[n]); | |
1852 | n++; | |
1853 | changed = 0; | |
1854 | }; | |
1855 | } | |
1856 | ||
1857 | } while ((ccb.ccb_h.status == CAM_REQ_CMP) && (ccb.cdm.status == CAM_DEV_MATCH_MORE) && n < MAX_NUM_DEV); | |
1858 | ||
1859 | if (devname != NULL) { | |
1860 | mp[n] = devname; | |
1861 | devname = NULL; | |
1862 | bytes+=1+strlen(mp[n]); | |
1863 | n++; | |
1864 | }; | |
1865 | ||
1866 | mp = (char **)reallocf(mp,n*(sizeof (char*))); // shrink to correct size | |
1867 | bytes += (n)*(sizeof(char*)); // and set allocated byte count | |
1868 | ||
1869 | end: | |
1870 | free(ccb.cdm.matches); | |
1871 | if (fd>-1) | |
1872 | close(fd); | |
1873 | if (n <= 0) { | |
1874 | free(mp); | |
1875 | mp = NULL; | |
832b75ed | 1876 | } |
2127e193 GI |
1877 | |
1878 | *names=mp; | |
1879 | ||
1880 | if (serrno>-1) | |
1881 | errno=serrno; | |
1882 | return(n); | |
832b75ed | 1883 | } |
832b75ed | 1884 | |
2127e193 GI |
1885 | // we are using ATA subsystem enumerator to found all ATA devices on system |
1886 | // despite of it's names | |
1887 | // | |
1888 | // If any errors occur, leave errno set as it was returned by the | |
1889 | // system call, and return <0. | |
832b75ed | 1890 | |
2127e193 GI |
1891 | // Return values: |
1892 | // -1: error | |
1893 | // >=0: number of discovered devices | |
1894 | int get_dev_names_ata(char*** names) { | |
1895 | struct ata_ioc_devices devices; | |
1896 | int fd=-1,maxchannel,serrno=-1,n=0; | |
1897 | char **mp = NULL; | |
1898 | ||
1899 | *names=NULL; | |
1900 | ||
1901 | if ((fd = open(ATA_DEVICE, O_RDWR)) < 0) { | |
1902 | if (errno == ENOENT) /* There are no ATA device on this computer */ | |
1903 | return 0; | |
1904 | serrno = errno; | |
1905 | pout("%s control device can't be opened: %s\n", ATA_DEVICE, strerror(errno)); | |
1906 | n = -1; | |
1907 | goto end; | |
1908 | }; | |
832b75ed | 1909 | |
2127e193 GI |
1910 | if (ioctl(fd, IOCATAGMAXCHANNEL, &maxchannel) < 0) { |
1911 | serrno = errno; | |
1912 | pout("ioctl(IOCATAGMAXCHANNEL) on /dev/ata failed: %s\n", strerror(errno)); | |
1913 | n = -1; | |
1914 | goto end; | |
1915 | }; | |
1916 | ||
1917 | // allocate space for up to MAX_NUM_DEV number of ATA devices | |
1918 | mp = (char **)calloc(MAX_NUM_DEV, sizeof(char*)); | |
1919 | if (mp == NULL) { | |
1920 | serrno=errno; | |
1921 | pout("Out of memory constructing scan device list (on line %d)\n", __LINE__); | |
1922 | n = -1; | |
1923 | goto end; | |
1924 | }; | |
1925 | ||
1926 | for (devices.channel = 0; devices.channel < maxchannel && n < MAX_NUM_DEV; devices.channel++) { | |
1927 | int j; | |
1928 | ||
1929 | if (ioctl(fd, IOCATADEVICES, &devices) < 0) { | |
1930 | if (errno == ENXIO) | |
1931 | continue; /* such channel not exist */ | |
1932 | pout("ioctl(IOCATADEVICES) on %s channel %d failed: %s\n", ATA_DEVICE, devices.channel, strerror(errno)); | |
1933 | n = -1; | |
1934 | goto end; | |
1935 | }; | |
1936 | for (j=0;j<=1 && n<MAX_NUM_DEV;j++) { | |
1937 | if (devices.name[j][0] != '\0') { | |
1938 | asprintf(mp+n, "%s%s", _PATH_DEV, devices.name[j]); | |
1939 | if (mp[n] == NULL) { | |
1940 | pout("Out of memory constructing scan ATA device list (on line %d)\n", __LINE__); | |
1941 | n = -1; | |
1942 | goto end; | |
1943 | }; | |
1944 | bytes+=1+strlen(mp[n]); | |
1945 | n++; | |
1946 | }; | |
1947 | }; | |
1948 | }; | |
1949 | mp = (char **)reallocf(mp,n*(sizeof (char*))); // shrink to correct size | |
1950 | bytes += (n)*(sizeof(char*)); // and set allocated byte count | |
1951 | ||
1952 | end: | |
1953 | if (fd>=0) | |
1954 | close(fd); | |
1955 | if (n <= 0) { | |
1956 | free(mp); | |
1957 | mp = NULL; | |
832b75ed | 1958 | } |
832b75ed | 1959 | |
2127e193 | 1960 | *names=mp; |
832b75ed | 1961 | |
2127e193 GI |
1962 | if (serrno>-1) |
1963 | errno=serrno; | |
1964 | return n; | |
1965 | } | |
832b75ed | 1966 | |
2127e193 GI |
1967 | |
1968 | ||
1969 | bool freebsd_smart_interface::scan_smart_devices(smart_device_list & devlist, | |
1970 | const char * type, const char * pattern /*= 0*/) | |
1971 | { | |
1972 | if (pattern) { | |
1973 | set_err(EINVAL, "DEVICESCAN with pattern not implemented yet"); | |
1974 | return false; | |
832b75ed GG |
1975 | } |
1976 | ||
2127e193 GI |
1977 | // Make namelists |
1978 | char * * atanames = 0; int numata = 0; | |
1979 | if (!type || !strcmp(type, "ata")) { | |
1980 | numata = get_dev_names_ata(&atanames); | |
1981 | if (numata < 0) { | |
1982 | set_err(ENOMEM); | |
1983 | return false; | |
832b75ed | 1984 | } |
2127e193 GI |
1985 | } |
1986 | ||
1987 | char * * scsinames = 0; int numscsi = 0; | |
1988 | if (!type || !strcmp(type, "scsi")) { | |
1989 | numscsi = get_dev_names_scsi(&scsinames); | |
1990 | if (numscsi < 0) { | |
1991 | set_err(ENOMEM); | |
1992 | return false; | |
832b75ed | 1993 | } |
832b75ed GG |
1994 | } |
1995 | ||
2127e193 GI |
1996 | // Add to devlist |
1997 | int i; | |
1998 | if (type==NULL) | |
1999 | type=""; | |
2000 | for (i = 0; i < numata; i++) { | |
2001 | ata_device * atadev = get_ata_device(atanames[i], type); | |
2002 | if (atadev) | |
2003 | devlist.add(atadev); | |
2004 | } | |
832b75ed | 2005 | |
2127e193 GI |
2006 | for (i = 0; i < numscsi; i++) { |
2007 | scsi_device * scsidev = get_scsi_device(scsinames[i], type); | |
2008 | if (scsidev) | |
2009 | devlist.add(scsidev); | |
832b75ed | 2010 | } |
2127e193 | 2011 | return true; |
832b75ed GG |
2012 | } |
2013 | ||
2127e193 GI |
2014 | |
2015 | static char done[USB_MAX_DEVICES]; | |
2016 | // static unsigned short vendor_id = 0, product_id = 0, version = 0; | |
2017 | ||
2018 | static int usbdevinfo(int f, int a, int rec, int busno, unsigned short & vendor_id, | |
2019 | unsigned short & product_id, unsigned short & version) | |
2020 | { | |
2021 | struct usb_device_info di; | |
2022 | int e, p, i; | |
2023 | char devname[256]; | |
2024 | ||
2025 | snprintf(devname, sizeof(devname),"umass%d",busno); | |
2026 | ||
2027 | di.udi_addr = a; | |
2028 | e = ioctl(f, USB_DEVICEINFO, &di); | |
2029 | if (e) { | |
2030 | if (errno != ENXIO) | |
2031 | printf("addr %d: I/O error\n", a); | |
2032 | return 0; | |
2033 | } | |
2034 | done[a] = 1; | |
2035 | ||
2036 | // list devices | |
2037 | for (i = 0; i < USB_MAX_DEVNAMES; i++) { | |
2038 | if (di.udi_devnames[i][0]) { | |
2039 | if(strcmp(di.udi_devnames[i],devname)==0) { | |
2040 | // device found! | |
2041 | vendor_id = di.udi_vendorNo; | |
2042 | product_id = di.udi_productNo; | |
2043 | version = di.udi_releaseNo; | |
2044 | return 1; | |
2045 | // FIXME | |
2046 | } | |
2047 | } | |
2048 | } | |
2049 | if (!rec) | |
2050 | return 0; | |
2051 | for (p = 0; p < di.udi_nports; p++) { | |
2052 | int s = di.udi_ports[p]; | |
2053 | if (s >= USB_MAX_DEVICES) { | |
2054 | continue; | |
2055 | } | |
2056 | if (s == 0) | |
2057 | printf("addr 0 should never happen!\n"); | |
2058 | else { | |
2059 | if(usbdevinfo(f, s, 1, busno, vendor_id, product_id, version)) return 1; | |
2060 | } | |
2061 | } | |
2062 | return 0; | |
832b75ed GG |
2063 | } |
2064 | ||
832b75ed | 2065 | |
832b75ed | 2066 | |
832b75ed | 2067 | |
2127e193 GI |
2068 | static int usbdevlist(int busno,unsigned short & vendor_id, |
2069 | unsigned short & product_id, unsigned short & version) | |
2070 | { | |
2071 | int i, f, a, rc; | |
2072 | char buf[50]; | |
2073 | int ncont; | |
2074 | ||
2075 | for (ncont = 0, i = 0; i < 10; i++) { | |
2076 | snprintf(buf, sizeof(buf), "%s%d", USBDEV, i); | |
2077 | f = open(buf, O_RDONLY); | |
2078 | if (f >= 0) { | |
2079 | memset(done, 0, sizeof done); | |
2080 | for (a = 1; a < USB_MAX_DEVICES; a++) { | |
2081 | if (!done[a]) { | |
2082 | rc = usbdevinfo(f, a, 1, busno,vendor_id, product_id, version); | |
2083 | if(rc) return 1; | |
2084 | } | |
2085 | ||
2086 | } | |
2087 | close(f); | |
2088 | } else { | |
2089 | if (errno == ENOENT || errno == ENXIO) | |
2090 | continue; | |
2091 | warn("%s", buf); | |
2092 | } | |
2093 | ncont++; | |
2094 | } | |
2095 | return 0; | |
2096 | } | |
2097 | ||
2098 | // Get USB bridge ID for "/dev/daX" | |
2099 | static bool get_usb_id(const char * path, unsigned short & vendor_id, | |
2100 | unsigned short & product_id, unsigned short & version) | |
2101 | { | |
2102 | // Only "/dev/daX" supported | |
2103 | if (!(!strncmp(path, "/dev/da", 7) && !strchr(path + 7, '/'))) | |
2104 | return false; | |
2105 | int bus = cam_getumassno((char *)path+5); | |
2106 | ||
2107 | if (bus == -1) | |
2108 | return false; | |
2109 | ||
2110 | usbdevlist(bus,vendor_id, | |
2111 | product_id, version); | |
2112 | ||
2113 | return true; | |
2114 | } | |
832b75ed | 2115 | |
832b75ed | 2116 | |
2127e193 GI |
2117 | smart_device * freebsd_smart_interface::autodetect_smart_device(const char * name) |
2118 | { | |
2119 | int guess = parse_ata_chan_dev(name,NULL); | |
2120 | unsigned short vendor_id = 0, product_id = 0, version = 0; | |
832b75ed | 2121 | |
2127e193 GI |
2122 | switch (guess) { |
2123 | case CONTROLLER_ATA : | |
2124 | return new freebsd_ata_device(this, name, ""); | |
2125 | case CONTROLLER_SCSI: | |
2126 | // Try to detect possible USB->(S)ATA bridge | |
2127 | if (get_usb_id(name, vendor_id, product_id, version)) { | |
2128 | const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id, version); | |
2129 | if (!usbtype) | |
2130 | return 0; | |
2131 | // Return SAT/USB device for this type | |
2132 | // (Note: freebsd_scsi_device::autodetect_open() will not be called in this case) | |
2133 | return get_sat_device(usbtype, new freebsd_scsi_device(this, name, "")); | |
832b75ed | 2134 | } |
2127e193 GI |
2135 | // non usb device, handle as normal scsi |
2136 | return new freebsd_scsi_device(this, name, ""); | |
2137 | case CONTROLLER_CCISS: | |
2138 | // device - cciss, but no ID known | |
2139 | set_err(EINVAL, "Option -d cciss,N requires N to be a non-negative integer"); | |
2140 | return 0; | |
832b75ed | 2141 | } |
832b75ed | 2142 | |
2127e193 GI |
2143 | |
2144 | // TODO: Test autodetect device here | |
2145 | return 0; | |
2146 | } | |
2147 | ||
2148 | ||
2149 | smart_device * freebsd_smart_interface::get_custom_smart_device(const char * name, const char * type) | |
2150 | { | |
2151 | // 3Ware ? | |
2152 | int disknum = -1, n1 = -1, n2 = -1; | |
2153 | if (sscanf(type, "3ware,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) { | |
2154 | if (n2 != (int)strlen(type)) { | |
2155 | set_err(EINVAL, "Option -d 3ware,N requires N to be a non-negative integer"); | |
2156 | return 0; | |
2157 | } | |
2158 | if (!(0 <= disknum && disknum <= 127)) { | |
2159 | set_err(EINVAL, "Option -d 3ware,N (N=%d) must have 0 <= N <= 127", disknum); | |
2160 | return 0; | |
2161 | } | |
2162 | int contr = parse_ata_chan_dev(name,NULL); | |
2163 | if (contr != CONTROLLER_3WARE_9000_CHAR && contr != CONTROLLER_3WARE_678K_CHAR) | |
2164 | contr = CONTROLLER_3WARE_678K; | |
2165 | return new freebsd_escalade_device(this, name, contr, disknum); | |
2166 | } | |
2167 | ||
2168 | // Highpoint ? | |
2169 | int controller = -1, channel = -1; disknum = 1; | |
2170 | n1 = n2 = -1; int n3 = -1; | |
2171 | if (sscanf(type, "hpt,%n%d/%d%n/%d%n", &n1, &controller, &channel, &n2, &disknum, &n3) >= 2 || n1 == 4) { | |
2172 | int len = strlen(type); | |
2173 | if (!(n2 == len || n3 == len)) { | |
2174 | set_err(EINVAL, "Option '-d hpt,L/M/N' supports 2-3 items"); | |
2175 | return 0; | |
2176 | } | |
2177 | if (!(1 <= controller && controller <= 8)) { | |
2178 | set_err(EINVAL, "Option '-d hpt,L/M/N' invalid controller id L supplied"); | |
2179 | return 0; | |
2180 | } | |
2181 | if (!(1 <= channel && channel <= 8)) { | |
2182 | set_err(EINVAL, "Option '-d hpt,L/M/N' invalid channel number M supplied"); | |
2183 | return 0; | |
2184 | } | |
2185 | if (!(1 <= disknum && disknum <= 15)) { | |
2186 | set_err(EINVAL, "Option '-d hpt,L/M/N' invalid pmport number N supplied"); | |
2187 | return 0; | |
2188 | } | |
2189 | return new freebsd_highpoint_device(this, name, controller, channel, disknum); | |
832b75ed GG |
2190 | } |
2191 | ||
2127e193 GI |
2192 | // CCISS ? |
2193 | disknum = n1 = n2 = -1; | |
2194 | if (sscanf(type, "cciss,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) { | |
2195 | if (n2 != (int)strlen(type)) { | |
2196 | set_err(EINVAL, "Option -d cciss,N requires N to be a non-negative integer"); | |
2197 | return 0; | |
2198 | } | |
2199 | if (!(0 <= disknum && disknum <= 15)) { | |
2200 | set_err(EINVAL, "Option -d cciss,N (N=%d) must have 0 <= N <= 15", disknum); | |
2201 | return 0; | |
2202 | } | |
2203 | return new freebsd_cciss_device(this, name, disknum); | |
832b75ed GG |
2204 | } |
2205 | ||
2127e193 | 2206 | return 0; |
832b75ed GG |
2207 | } |
2208 | ||
2127e193 GI |
2209 | const char * freebsd_smart_interface::get_valid_custom_dev_types_str() |
2210 | { | |
2211 | return "marvell, 3ware,N, hpt,L/M/N, cciss,N"; | |
2212 | } | |
2213 | ||
2214 | ||
2215 | } // namespace | |
2216 | ||
2217 | ||
2218 | ///////////////////////////////////////////////////////////////////////////// | |
2219 | /// Initialize platform interface and register with smi() | |
2220 | ||
2221 | void smart_interface::init() | |
2222 | { | |
2223 | static os_freebsd::freebsd_smart_interface the_interface; | |
2224 | smart_interface::set(&the_interface); | |
832b75ed | 2225 | } |