]>
Commit | Line | Data |
---|---|---|
832b75ed GG |
1 | /* |
2 | * os_freebsd.c | |
3 | * | |
4 | * Home page of code is: http://smartmontools.sourceforge.net | |
5 | * | |
6 | * Copyright (C) 2003-6 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 <camlib.h> | |
24 | #include <cam/scsi/scsi_message.h> | |
25 | #include <sys/ata.h> | |
26 | #include <sys/stat.h> | |
27 | #include <unistd.h> | |
28 | #include <fcntl.h> | |
29 | #include <glob.h> | |
30 | #include <fcntl.h> | |
31 | #include <stddef.h> | |
32 | ||
33 | ||
34 | #include "config.h" | |
35 | #include "int64.h" | |
36 | #include "atacmds.h" | |
37 | #include "scsicmds.h" | |
38 | #include "utility.h" | |
39 | #include "os_freebsd.h" | |
40 | ||
4d59bff9 | 41 | static const char *filenameandversion="$Id: os_freebsd.cpp,v 1.51 2006/09/17 03:17:53 dpgilbert Exp $"; |
832b75ed | 42 | |
4d59bff9 | 43 | const char *os_XXXX_c_cvsid="$Id: os_freebsd.cpp,v 1.51 2006/09/17 03:17:53 dpgilbert Exp $" \ |
832b75ed GG |
44 | ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_FREEBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; |
45 | ||
46 | // to hold onto exit code for atexit routine | |
47 | extern int exitstatus; | |
48 | ||
49 | // Private table of open devices: guaranteed zero on startup since | |
50 | // part of static data. | |
51 | struct freebsd_dev_channel *devicetable[FREEBSD_MAXDEV]; | |
52 | ||
53 | // forward declaration | |
54 | static int parse_ata_chan_dev(const char * dev_name, struct freebsd_dev_channel *ch); | |
55 | ||
56 | // print examples for smartctl | |
57 | void print_smartctl_examples(){ | |
58 | printf("=================================================== SMARTCTL EXAMPLES =====\n\n"); | |
59 | #ifdef HAVE_GETOPT_LONG | |
60 | printf( | |
61 | " smartctl -a /dev/ad0 (Prints all SMART information)\n\n" | |
62 | " smartctl --smart=on --offlineauto=on --saveauto=on /dev/ad0\n" | |
63 | " (Enables SMART on first disk)\n\n" | |
64 | " smartctl -t long /dev/ad0 (Executes extended disk self-test)\n\n" | |
65 | " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/ad0\n" | |
66 | " (Prints Self-Test & Attribute errors)\n" | |
67 | " (Prints Self-Test & Attribute errors)\n\n" | |
68 | " smartctl -a --device=3ware,2 /dev/twa0\n" | |
69 | " smartctl -a --device=3ware,2 /dev/twe0\n" | |
70 | " (Prints all SMART information for ATA disk on\n" | |
71 | " third port of first 3ware RAID controller)\n" | |
72 | ); | |
73 | #else | |
74 | printf( | |
75 | " smartctl -a /dev/ad0 (Prints all SMART information)\n" | |
76 | " smartctl -s on -o on -S on /dev/ad0 (Enables SMART on first disk)\n" | |
77 | " smartctl -t long /dev/ad0 (Executes extended disk self-test)\n" | |
78 | " smartctl -A -l selftest -q errorsonly /dev/ad0\n" | |
79 | " (Prints Self-Test & Attribute errors)\n" | |
80 | " smartctl -a -d 3ware,2 /dev/twa0\n" | |
81 | " smartctl -a -d 3ware,2 /dev/twe0\n" | |
82 | ); | |
83 | #endif | |
84 | return; | |
85 | } | |
86 | ||
87 | // Like open(). Return positive integer handle, used by functions below only. mode=="ATA" or "SCSI". | |
88 | int deviceopen (const char* dev, char* mode __unused) { | |
89 | struct freebsd_dev_channel *fdchan; | |
90 | int parse_ok, i; | |
91 | ||
92 | // Search table for a free entry | |
93 | for (i=0; i<FREEBSD_MAXDEV; i++) | |
94 | if (!devicetable[i]) | |
95 | break; | |
96 | ||
97 | // If no free entry found, return error. We have max allowed number | |
98 | // of "file descriptors" already allocated. | |
99 | if (i==FREEBSD_MAXDEV) { | |
100 | errno=EMFILE; | |
101 | return -1; | |
102 | } | |
103 | ||
4d59bff9 | 104 | fdchan = (struct freebsd_dev_channel *)calloc(1,sizeof(struct freebsd_dev_channel)); |
832b75ed GG |
105 | if (fdchan == NULL) { |
106 | // errno already set by call to malloc() | |
107 | return -1; | |
108 | } | |
109 | ||
110 | parse_ok = parse_ata_chan_dev (dev,fdchan); | |
111 | if (parse_ok == CONTROLLER_UNKNOWN) { | |
112 | free(fdchan); | |
113 | errno = ENOTTY; | |
114 | return -1; // can't handle what we don't know | |
115 | } | |
116 | ||
117 | if (parse_ok == CONTROLLER_ATA) { | |
118 | #ifdef IOCATAREQUEST | |
119 | if ((fdchan->device = open(dev,O_RDONLY))<0) { | |
120 | #else | |
121 | if ((fdchan->atacommand = open("/dev/ata",O_RDWR))<0) { | |
122 | #endif | |
123 | int myerror = errno; //preserve across free call | |
124 | free (fdchan); | |
125 | errno = myerror; | |
126 | return -1; | |
127 | } | |
128 | } | |
129 | ||
130 | if (parse_ok == CONTROLLER_3WARE_678K_CHAR) { | |
131 | char buf[512]; | |
132 | sprintf(buf,"/dev/twe%d",fdchan->device); | |
133 | #ifdef IOCATAREQUEST | |
134 | if ((fdchan->device = open(buf,O_RDWR))<0) { | |
135 | #else | |
136 | if ((fdchan->atacommand = open(buf,O_RDWR))<0) { | |
137 | #endif | |
138 | int myerror = errno; // preserver across free call | |
139 | free(fdchan); | |
140 | errno=myerror; | |
141 | return -1; | |
142 | } | |
143 | } | |
144 | ||
145 | if (parse_ok == CONTROLLER_3WARE_9000_CHAR) { | |
146 | char buf[512]; | |
147 | sprintf(buf,"/dev/twa%d",fdchan->device); | |
148 | #ifdef IOCATAREQUEST | |
149 | if ((fdchan->device = open(buf,O_RDWR))<0) { | |
150 | #else | |
151 | if ((fdchan->atacommand = open(buf,O_RDWR))<0) { | |
152 | #endif | |
153 | int myerror = errno; // preserver across free call | |
154 | free(fdchan); | |
155 | errno=myerror; | |
156 | return -1; | |
157 | } | |
158 | } | |
159 | ||
160 | if (parse_ok == CONTROLLER_SCSI) { | |
161 | // this is really a NO-OP, as the parse takes care | |
162 | // of filling in correct details | |
163 | } | |
164 | ||
165 | // return pointer to "file descriptor" table entry, properly offset. | |
166 | devicetable[i]=fdchan; | |
167 | return i+FREEBSD_FDOFFSET; | |
168 | } | |
169 | ||
170 | // Returns 1 if device not available/open/found else 0. Also shifts fd into valid range. | |
171 | static int isnotopen(int *fd, struct freebsd_dev_channel** fdchan) { | |
172 | // put valid "file descriptor" into range 0...FREEBSD_MAXDEV-1 | |
173 | *fd -= FREEBSD_FDOFFSET; | |
174 | ||
175 | // check for validity of "file descriptor". | |
176 | if (*fd<0 || *fd>=FREEBSD_MAXDEV || !((*fdchan)=devicetable[*fd])) { | |
177 | errno = ENODEV; | |
178 | return 1; | |
179 | } | |
180 | ||
181 | return 0; | |
182 | } | |
183 | ||
184 | // Like close(). Acts on handles returned by above function. | |
185 | int deviceclose (int fd) { | |
186 | struct freebsd_dev_channel *fdchan; | |
187 | int failed = 0; | |
188 | ||
189 | // check for valid file descriptor | |
190 | if (isnotopen(&fd, &fdchan)) | |
191 | return -1; | |
192 | ||
193 | ||
194 | // did we allocate a SCSI device name? | |
195 | if (fdchan->devname) | |
196 | free(fdchan->devname); | |
197 | ||
198 | // close device, if open | |
199 | #ifdef IOCATAREQUEST | |
200 | if (fdchan->device) | |
201 | failed=close(fdchan->device); | |
202 | #else | |
203 | if (fdchan->atacommand) | |
204 | failed=close(fdchan->atacommand); | |
205 | #endif | |
206 | ||
207 | if (fdchan->scsicontrol) | |
208 | failed=close(fdchan->scsicontrol); | |
209 | ||
210 | // if close succeeded, then remove from device list | |
211 | // Eduard, should we also remove it from list if close() fails? I'm | |
212 | // not sure. Here I only remove it from list if close() worked. | |
213 | if (!failed) { | |
214 | free(fdchan); | |
215 | devicetable[fd]=NULL; | |
216 | } | |
217 | ||
218 | return failed; | |
219 | } | |
220 | ||
221 | #define NO_RETURN 0 | |
222 | #define BAD_SMART 1 | |
223 | #define NO_DISK_3WARE 2 | |
224 | #define BAD_KERNEL 3 | |
225 | #define MAX_MSG 3 | |
226 | ||
227 | // Utility function for printing warnings | |
228 | void printwarning(int msgNo, const char* extra) { | |
229 | static int printed[] = {0,0,0,0}; | |
230 | static const char* message[]={ | |
231 | "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", | |
232 | ||
233 | "Error SMART Status command failed\nPlease get assistance from \n" PACKAGE_HOMEPAGE "\nRegister values returned from SMART Status command are:\n", | |
234 | ||
235 | "You must specify a DISK # for 3ware drives with -d 3ware,<n> where <n> begins with 1 for first disk drive\n", | |
236 | ||
237 | "ATA support is not provided for this kernel version. Please ugrade to a recent 5-CURRENT kernel (post 09/01/2003 or so)\n" | |
238 | }; | |
239 | ||
240 | if (msgNo >= 0 && msgNo <= MAX_MSG) { | |
241 | if (!printed[msgNo]) { | |
242 | printed[msgNo] = 1; | |
243 | pout("%s", message[msgNo]); | |
244 | if (extra) | |
245 | pout("%s",extra); | |
246 | } | |
247 | } | |
248 | return; | |
249 | } | |
250 | ||
251 | ||
252 | // Interface to ATA devices. See os_linux.c | |
253 | int marvell_command_interface(int fd __unused, smart_command_set command __unused, int select __unused, char *data __unused) { | |
254 | return -1; | |
255 | } | |
256 | ||
4d59bff9 GG |
257 | int highpoint_command_interface(int fd __unused, smart_command_set command __unused, int select __unused, char *data __unused) |
258 | { | |
259 | return -1; | |
260 | } | |
261 | ||
832b75ed GG |
262 | int ata_command_interface(int fd, smart_command_set command, int select, char *data) { |
263 | #if !defined(ATAREQUEST) && !defined(IOCATAREQUEST) | |
264 | // sorry, but without ATAng, we can't do anything here | |
265 | printwarning(BAD_KERNEL,NULL); | |
266 | errno = ENOSYS; | |
267 | return -1; | |
268 | #else | |
269 | struct freebsd_dev_channel* con; | |
270 | int retval, copydata=0; | |
271 | #ifdef IOCATAREQUEST | |
272 | struct ata_ioc_request request; | |
273 | #else | |
274 | struct ata_cmd iocmd; | |
275 | #endif | |
276 | unsigned char buff[512]; | |
277 | ||
278 | // check that "file descriptor" is valid | |
279 | if (isnotopen(&fd,&con)) | |
280 | return -1; | |
281 | ||
282 | bzero(buff,512); | |
283 | ||
284 | #ifdef IOCATAREQUEST | |
285 | bzero(&request,sizeof(struct ata_ioc_request)); | |
286 | #else | |
287 | bzero(&iocmd,sizeof(struct ata_cmd)); | |
288 | #endif | |
289 | bzero(buff,512); | |
290 | ||
291 | #ifndef IOCATAREQUEST | |
292 | iocmd.cmd=ATAREQUEST; | |
293 | iocmd.channel=con->channel; | |
294 | iocmd.device=con->device; | |
295 | #define request iocmd.u.request | |
296 | #endif | |
297 | ||
298 | request.u.ata.command=ATA_SMART_CMD; | |
299 | request.timeout=600; | |
300 | switch (command){ | |
301 | case READ_VALUES: | |
302 | request.u.ata.feature=ATA_SMART_READ_VALUES; | |
303 | request.u.ata.lba=0xc24f<<8; | |
304 | request.flags=ATA_CMD_READ; | |
4d59bff9 | 305 | request.data=(char *)buff; |
832b75ed GG |
306 | request.count=512; |
307 | copydata=1; | |
308 | break; | |
309 | case READ_THRESHOLDS: | |
310 | request.u.ata.feature=ATA_SMART_READ_THRESHOLDS; | |
311 | request.u.ata.count=1; | |
312 | request.u.ata.lba=1|(0xc24f<<8); | |
313 | request.flags=ATA_CMD_READ; | |
4d59bff9 | 314 | request.data=(char *)buff; |
832b75ed GG |
315 | request.count=512; |
316 | copydata=1; | |
317 | break; | |
318 | case READ_LOG: | |
319 | request.u.ata.feature=ATA_SMART_READ_LOG_SECTOR; | |
320 | request.u.ata.lba=select|(0xc24f<<8); | |
321 | request.u.ata.count=1; | |
322 | request.flags=ATA_CMD_READ; | |
4d59bff9 | 323 | request.data=(char *)buff; |
832b75ed GG |
324 | request.count=512; |
325 | copydata=1; | |
326 | break; | |
327 | case IDENTIFY: | |
328 | request.u.ata.command=ATA_IDENTIFY_DEVICE; | |
329 | request.flags=ATA_CMD_READ; | |
4d59bff9 | 330 | request.data=(char *)buff; |
832b75ed GG |
331 | request.count=512; |
332 | copydata=1; | |
333 | break; | |
334 | case PIDENTIFY: | |
335 | request.u.ata.command=ATA_IDENTIFY_PACKET_DEVICE; | |
336 | request.flags=ATA_CMD_READ; | |
4d59bff9 | 337 | request.data=(char *)buff; |
832b75ed GG |
338 | request.count=512; |
339 | copydata=1; | |
340 | break; | |
341 | case ENABLE: | |
342 | request.u.ata.feature=ATA_SMART_ENABLE; | |
343 | request.u.ata.lba=0xc24f<<8; | |
344 | request.flags=ATA_CMD_CONTROL; | |
345 | break; | |
346 | case DISABLE: | |
347 | request.u.ata.feature=ATA_SMART_DISABLE; | |
348 | request.u.ata.lba=0xc24f<<8; | |
349 | request.flags=ATA_CMD_CONTROL; | |
350 | break; | |
351 | case AUTO_OFFLINE: | |
352 | // NOTE: According to ATAPI 4 and UP, this command is obsolete | |
353 | request.u.ata.feature=ATA_SMART_AUTO_OFFLINE; | |
354 | request.u.ata.lba=select|(0xc24f<<8); | |
355 | request.flags=ATA_CMD_CONTROL; | |
356 | break; | |
357 | case AUTOSAVE: | |
358 | request.u.ata.feature=ATA_SMART_AUTOSAVE; | |
359 | request.u.ata.count=0xf1; // to enable autosave | |
360 | request.u.ata.lba=0xc24f<<8; | |
361 | request.flags=ATA_CMD_CONTROL; | |
362 | break; | |
363 | case IMMEDIATE_OFFLINE: | |
364 | request.u.ata.feature=ATA_SMART_IMMEDIATE_OFFLINE; | |
365 | request.u.ata.lba = select|(0xc24f<<8); // put test in sector | |
366 | request.flags=ATA_CMD_CONTROL; | |
367 | break; | |
368 | case STATUS_CHECK: // same command, no HDIO in FreeBSD | |
369 | case STATUS: | |
370 | // this command only says if SMART is working. It could be | |
371 | // replaced with STATUS_CHECK below. | |
372 | request.u.ata.feature=ATA_SMART_STATUS; | |
373 | request.u.ata.lba=0xc24f<<8; | |
374 | request.flags=ATA_CMD_CONTROL; | |
375 | break; | |
376 | default: | |
377 | pout("Unrecognized command %d in ata_command_interface()\n" | |
378 | "Please contact " PACKAGE_BUGREPORT "\n", command); | |
379 | errno=ENOSYS; | |
380 | return -1; | |
381 | } | |
382 | ||
383 | if (command==STATUS_CHECK){ | |
384 | unsigned const char normal_lo=0x4f, normal_hi=0xc2; | |
385 | unsigned const char failed_lo=0xf4, failed_hi=0x2c; | |
386 | unsigned char low,high; | |
387 | ||
388 | #ifdef IOCATAREQUEST | |
389 | if ((retval=ioctl(con->device, IOCATAREQUEST, &request)) || request.error) | |
390 | #else | |
391 | if ((retval=ioctl(con->atacommand, IOCATA, &iocmd)) || request.error) | |
392 | #endif | |
393 | return -1; | |
394 | ||
395 | #if __FreeBSD_version < 502000 | |
396 | printwarning(NO_RETURN,NULL); | |
397 | #endif | |
398 | ||
399 | high = (request.u.ata.lba >> 16) & 0xff; | |
400 | low = (request.u.ata.lba >> 8) & 0xff; | |
401 | ||
402 | // Cyl low and Cyl high unchanged means "Good SMART status" | |
403 | if (low==normal_lo && high==normal_hi) | |
404 | return 0; | |
405 | ||
406 | // These values mean "Bad SMART status" | |
407 | if (low==failed_lo && high==failed_hi) | |
408 | return 1; | |
409 | ||
410 | // We haven't gotten output that makes sense; print out some debugging info | |
411 | char buf[512]; | |
412 | sprintf(buf,"CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n", | |
413 | (int)request.u.ata.command, | |
414 | (int)request.u.ata.feature, | |
415 | (int)request.u.ata.count, | |
416 | (int)((request.u.ata.lba) & 0xff), | |
417 | (int)((request.u.ata.lba>>8) & 0xff), | |
418 | (int)((request.u.ata.lba>>16) & 0xff), | |
419 | (int)request.error); | |
420 | printwarning(BAD_SMART,buf); | |
421 | return 0; | |
422 | } | |
423 | ||
424 | #ifdef IOCATAREQUEST | |
425 | if ((retval=ioctl(con->device, IOCATAREQUEST, &request)) || request.error) | |
426 | #else | |
427 | if ((retval=ioctl(con->atacommand, IOCATA, &iocmd)) || request.error) | |
428 | #endif | |
429 | { | |
430 | return -1; | |
431 | } | |
432 | // | |
433 | if (copydata) | |
434 | memcpy(data, buff, 512); | |
435 | ||
436 | return 0; | |
437 | #endif | |
438 | } | |
439 | ||
440 | ||
441 | // Interface to SCSI devices. See os_linux.c | |
442 | int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) | |
443 | { | |
444 | struct freebsd_dev_channel* con = NULL; | |
445 | struct cam_device* cam_dev = NULL; | |
446 | union ccb *ccb; | |
447 | ||
448 | ||
449 | if (report > 0) { | |
450 | unsigned int k; | |
451 | const unsigned char * ucp = iop->cmnd; | |
452 | const char * np; | |
453 | ||
454 | np = scsi_get_opcode_name(ucp[0]); | |
455 | pout(" [%s: ", np ? np : "<unknown opcode>"); | |
456 | for (k = 0; k < iop->cmnd_len; ++k) | |
457 | pout("%02x ", ucp[k]); | |
458 | if ((report > 1) && | |
459 | (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { | |
460 | int trunc = (iop->dxfer_len > 256) ? 1 : 0; | |
461 | ||
462 | pout("]\n Outgoing data, len=%d%s:\n", (int)iop->dxfer_len, | |
463 | (trunc ? " [only first 256 bytes shown]" : "")); | |
464 | dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); | |
465 | } | |
466 | else | |
467 | pout("]"); | |
468 | } | |
469 | ||
470 | // check that "file descriptor" is valid | |
471 | if (isnotopen(&fd,&con)) | |
472 | return -ENOTTY; | |
473 | ||
474 | ||
475 | if (!(cam_dev = cam_open_spec_device(con->devname,con->unitnum,O_RDWR,NULL))) { | |
476 | warnx("%s",cam_errbuf); | |
477 | return -1; | |
478 | } | |
479 | ||
480 | if (!(ccb = cam_getccb(cam_dev))) { | |
481 | warnx("error allocating ccb"); | |
482 | return -ENOMEM; | |
483 | } | |
484 | ||
485 | // clear out structure, except for header that was filled in for us | |
486 | bzero(&(&ccb->ccb_h)[1], | |
487 | sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); | |
488 | ||
489 | cam_fill_csio(&ccb->csio, | |
490 | /*retrires*/ 1, | |
491 | /*cbfcnp*/ NULL, | |
492 | /* flags */ (iop->dxfer_dir == DXFER_NONE ? CAM_DIR_NONE :(iop->dxfer_dir == DXFER_FROM_DEVICE ? CAM_DIR_IN : CAM_DIR_OUT)), | |
493 | /* tagaction */ MSG_SIMPLE_Q_TAG, | |
494 | /* dataptr */ iop->dxferp, | |
495 | /* datalen */ iop->dxfer_len, | |
496 | /* senselen */ iop->max_sense_len, | |
497 | /* cdblen */ iop->cmnd_len, | |
498 | /* timout (converted to seconds) */ iop->timeout*1000); | |
499 | memcpy(ccb->csio.cdb_io.cdb_bytes,iop->cmnd,iop->cmnd_len); | |
500 | ||
501 | if (cam_send_ccb(cam_dev,ccb) < 0) { | |
502 | warn("error sending SCSI ccb"); | |
503 | #if __FreeBSD_version > 500000 | |
504 | cam_error_print(cam_dev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr); | |
505 | #endif | |
506 | cam_freeccb(ccb); | |
507 | return -1; | |
508 | } | |
509 | ||
510 | if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { | |
511 | #if __FreeBSD_version > 500000 | |
512 | cam_error_print(cam_dev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr); | |
513 | #endif | |
514 | cam_freeccb(ccb); | |
515 | return -1; | |
516 | } | |
517 | ||
518 | if (iop->sensep) { | |
519 | memcpy(iop->sensep,&(ccb->csio.sense_data),sizeof(struct scsi_sense_data)); | |
520 | iop->resp_sense_len = sizeof(struct scsi_sense_data); | |
521 | } | |
522 | ||
523 | iop->scsi_status = ccb->csio.scsi_status; | |
524 | ||
525 | cam_freeccb(ccb); | |
526 | ||
527 | if (cam_dev) | |
528 | cam_close_device(cam_dev); | |
529 | ||
530 | if (report > 0) { | |
531 | int trunc; | |
532 | ||
533 | pout(" status=0\n"); | |
534 | trunc = (iop->dxfer_len > 256) ? 1 : 0; | |
535 | ||
536 | pout(" Incoming data, len=%d%s:\n", (int)iop->dxfer_len, | |
537 | (trunc ? " [only first 256 bytes shown]" : "")); | |
538 | dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); | |
539 | } | |
540 | return 0; | |
541 | } | |
542 | ||
543 | // Interface to ATA devices behind 3ware escalade RAID controller cards. See os_linux.c | |
544 | ||
545 | #define BUFFER_LEN_678K_CHAR ( sizeof(struct twe_usercommand) ) // 520 | |
546 | #define BUFFER_LEN_9000_CHAR ( sizeof(TW_OSLI_IOCTL_NO_DATA_BUF) + sizeof(TWE_Command) ) // 2048 | |
547 | #define TW_IOCTL_BUFFER_SIZE ( MAX(BUFFER_LEN_678K_CHAR, BUFFER_LEN_9000_CHAR) ) | |
548 | ||
549 | int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data) { | |
550 | // to hold true file descriptor | |
551 | struct freebsd_dev_channel* con; | |
552 | ||
553 | // return value and buffer for ioctl() | |
554 | int ioctlreturn, readdata=0; | |
555 | struct twe_usercommand* cmd_twe = NULL; | |
556 | TW_OSLI_IOCTL_NO_DATA_BUF* cmd_twa = NULL; | |
557 | TWE_Command_ATA* ata = NULL; | |
558 | ||
559 | // Used by both the SCSI and char interfaces | |
560 | char ioctl_buffer[TW_IOCTL_BUFFER_SIZE]; | |
561 | ||
562 | if (disknum < 0) { | |
563 | printwarning(NO_DISK_3WARE,NULL); | |
564 | return -1; | |
565 | } | |
566 | ||
567 | // check that "file descriptor" is valid | |
568 | if (isnotopen(&fd,&con)) | |
569 | return -1; | |
570 | ||
571 | memset(ioctl_buffer, 0, TW_IOCTL_BUFFER_SIZE); | |
572 | ||
573 | if (escalade_type==CONTROLLER_3WARE_9000_CHAR) { | |
574 | cmd_twa = (TW_OSLI_IOCTL_NO_DATA_BUF*)ioctl_buffer; | |
575 | cmd_twa->pdata = ((TW_OSLI_IOCTL_WITH_PAYLOAD*)cmd_twa)->payload.data_buf; | |
576 | cmd_twa->driver_pkt.buffer_length = 512; | |
577 | ata = (TWE_Command_ATA*)&cmd_twa->cmd_pkt.command.cmd_pkt_7k; | |
578 | } else if (escalade_type==CONTROLLER_3WARE_678K_CHAR) { | |
579 | cmd_twe = (struct twe_usercommand*)ioctl_buffer; | |
580 | ata = &cmd_twe->tu_command.ata; | |
581 | } else { | |
582 | pout("Unrecognized escalade_type %d in freebsd_3ware_command_interface(disk %d)\n" | |
583 | "Please contact " PACKAGE_BUGREPORT "\n", escalade_type, disknum); | |
584 | errno=ENOSYS; | |
585 | return -1; | |
586 | } | |
587 | ||
588 | ata->opcode = TWE_OP_ATA_PASSTHROUGH; | |
589 | ||
590 | // Same for (almost) all commands - but some reset below | |
591 | ata->request_id = 0xFF; | |
592 | ata->unit = disknum; | |
593 | ata->host_id = 0; | |
594 | ata->status = 0; | |
595 | ata->flags = 0x1; | |
596 | ata->drive_head = 0x0; | |
597 | ata->sector_num = 0; | |
598 | ||
599 | // All SMART commands use this CL/CH signature. These are magic | |
600 | // values from the ATA specifications. | |
601 | ata->cylinder_lo = 0x4F; | |
602 | ata->cylinder_hi = 0xC2; | |
603 | ||
604 | // SMART ATA COMMAND REGISTER value | |
605 | ata->command = ATA_SMART_CMD; | |
606 | ||
607 | // Is this a command that reads or returns 512 bytes? | |
608 | // passthru->param values are: | |
609 | // 0x0 - non data command without TFR write check, | |
610 | // 0x8 - non data command with TFR write check, | |
611 | // 0xD - data command that returns data to host from device | |
612 | // 0xF - data command that writes data from host to device | |
613 | // passthru->size values are 0x5 for non-data and 0x07 for data | |
614 | if (command == READ_VALUES || | |
615 | command == READ_THRESHOLDS || | |
616 | command == READ_LOG || | |
617 | command == IDENTIFY || | |
618 | command == WRITE_LOG ) { | |
619 | readdata=1; | |
620 | if (escalade_type==CONTROLLER_3WARE_678K_CHAR) { | |
621 | cmd_twe->tu_data = data; | |
622 | cmd_twe->tu_size = 512; | |
623 | } | |
624 | ata->sgl_offset = 0x5; | |
625 | ata->size = 0x5; | |
626 | ata->param = 0xD; | |
627 | ata->sector_count = 0x1; | |
628 | // For 64-bit to work correctly, up the size of the command packet | |
629 | // in dwords by 1 to account for the 64-bit single sgl 'address' | |
630 | // field. Note that this doesn't agree with the typedefs but it's | |
631 | // right (agree with kernel driver behavior/typedefs). | |
632 | //if (sizeof(long)==8) | |
633 | // ata->size++; | |
634 | } | |
635 | else { | |
636 | // Non data command -- but doesn't use large sector | |
637 | // count register values. | |
638 | ata->sgl_offset = 0x0; | |
639 | ata->size = 0x5; | |
640 | ata->param = 0x8; | |
641 | ata->sector_count = 0x0; | |
642 | } | |
643 | ||
644 | // Now set ATA registers depending upon command | |
645 | switch (command){ | |
646 | case CHECK_POWER_MODE: | |
647 | ata->command = ATA_CHECK_POWER_MODE; | |
648 | ata->features = 0; | |
649 | ata->cylinder_lo = 0; | |
650 | ata->cylinder_hi = 0; | |
651 | break; | |
652 | case READ_VALUES: | |
653 | ata->features = ATA_SMART_READ_VALUES; | |
654 | break; | |
655 | case READ_THRESHOLDS: | |
656 | ata->features = ATA_SMART_READ_THRESHOLDS; | |
657 | break; | |
658 | case READ_LOG: | |
659 | ata->features = ATA_SMART_READ_LOG_SECTOR; | |
660 | // log number to return | |
661 | ata->sector_num = select; | |
662 | break; | |
663 | case WRITE_LOG: | |
664 | readdata=0; | |
665 | ata->features = ATA_SMART_WRITE_LOG_SECTOR; | |
666 | ata->sector_count = 1; | |
667 | ata->sector_num = select; | |
668 | ata->param = 0xF; // PIO data write | |
669 | break; | |
670 | case IDENTIFY: | |
671 | // ATA IDENTIFY DEVICE | |
672 | ata->command = ATA_IDENTIFY_DEVICE; | |
673 | ata->features = 0; | |
674 | ata->cylinder_lo = 0; | |
675 | ata->cylinder_hi = 0; | |
676 | break; | |
677 | case PIDENTIFY: | |
678 | // 3WARE controller can NOT have packet device internally | |
679 | pout("WARNING - NO DEVICE FOUND ON 3WARE CONTROLLER (disk %d)\n", disknum); | |
680 | errno=ENODEV; | |
681 | return -1; | |
682 | case ENABLE: | |
683 | ata->features = ATA_SMART_ENABLE; | |
684 | break; | |
685 | case DISABLE: | |
686 | ata->features = ATA_SMART_DISABLE; | |
687 | break; | |
688 | case AUTO_OFFLINE: | |
689 | ata->features = ATA_SMART_AUTO_OFFLINE; | |
690 | // Enable or disable? | |
691 | ata->sector_count = select; | |
692 | break; | |
693 | case AUTOSAVE: | |
694 | ata->features = ATA_SMART_AUTOSAVE; | |
695 | // Enable or disable? | |
696 | ata->sector_count = select; | |
697 | break; | |
698 | case IMMEDIATE_OFFLINE: | |
699 | ata->features = ATA_SMART_IMMEDIATE_OFFLINE; | |
700 | // What test type to run? | |
701 | ata->sector_num = select; | |
702 | break; | |
703 | case STATUS_CHECK: | |
704 | ata->features = ATA_SMART_STATUS; | |
705 | break; | |
706 | case STATUS: | |
707 | // This is JUST to see if SMART is enabled, by giving SMART status | |
708 | // command. But it doesn't say if status was good, or failing. | |
709 | // See below for the difference. | |
710 | ata->features = ATA_SMART_STATUS; | |
711 | break; | |
712 | default: | |
713 | pout("Unrecognized command %d in freebsd_3ware_command_interface(disk %d)\n" | |
714 | "Please contact " PACKAGE_BUGREPORT "\n", command, disknum); | |
715 | errno=ENOSYS; | |
716 | return -1; | |
717 | } | |
718 | ||
719 | // Now send the command down through an ioctl() | |
720 | if (escalade_type==CONTROLLER_3WARE_9000_CHAR) { | |
721 | #ifdef IOCATAREQUEST | |
722 | ioctlreturn=ioctl(con->device,TW_OSL_IOCTL_FIRMWARE_PASS_THROUGH,cmd_twa); | |
723 | #else | |
724 | ioctlreturn=ioctl(con->atacommand,TW_OSL_IOCTL_FIRMWARE_PASS_THROUGH,cmd_twa); | |
725 | #endif | |
726 | } else { | |
727 | #ifdef IOCATAREQUEST | |
728 | ioctlreturn=ioctl(con->device,TWEIO_COMMAND,cmd_twe); | |
729 | #else | |
730 | ioctlreturn=ioctl(con->atacommand,TWEIO_COMMAND,cmd_twe); | |
731 | #endif | |
732 | } | |
733 | ||
734 | // Deal with the different error cases | |
735 | if (ioctlreturn) { | |
736 | if (!errno) | |
737 | errno=EIO; | |
738 | return -1; | |
739 | } | |
740 | ||
741 | // See if the ATA command failed. Now that we have returned from | |
742 | // the ioctl() call, if passthru is valid, then: | |
743 | // - ata->status contains the 3ware controller STATUS | |
744 | // - ata->command contains the ATA STATUS register | |
745 | // - ata->features contains the ATA ERROR register | |
746 | // | |
747 | // Check bits 0 (error bit) and 5 (device fault) of the ATA STATUS | |
748 | // If bit 0 (error bit) is set, then ATA ERROR register is valid. | |
749 | // While we *might* decode the ATA ERROR register, at the moment it | |
750 | // doesn't make much sense: we don't care in detail why the error | |
751 | // happened. | |
752 | ||
753 | if (ata->status || (ata->command & 0x21)) { | |
754 | 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); | |
755 | errno=EIO; | |
756 | return -1; | |
757 | } | |
758 | ||
759 | // If this is a read data command, copy data to output buffer | |
760 | if (readdata) { | |
761 | if (escalade_type==CONTROLLER_3WARE_9000_CHAR) | |
762 | memcpy(data, cmd_twa->pdata, 512); | |
763 | } | |
764 | ||
765 | // For STATUS_CHECK, we need to check register values | |
766 | if (command==STATUS_CHECK) { | |
767 | ||
768 | // To find out if the SMART RETURN STATUS is good or failing, we | |
769 | // need to examine the values of the Cylinder Low and Cylinder | |
770 | // High Registers. | |
771 | ||
772 | unsigned short cyl_lo=ata->cylinder_lo; | |
773 | unsigned short cyl_hi=ata->cylinder_hi; | |
774 | ||
775 | // If values in Cyl-LO and Cyl-HI are unchanged, SMART status is good. | |
776 | if (cyl_lo==0x4F && cyl_hi==0xC2) | |
777 | return 0; | |
778 | ||
779 | // If values in Cyl-LO and Cyl-HI are as follows, SMART status is FAIL | |
780 | if (cyl_lo==0xF4 && cyl_hi==0x2C) | |
781 | return 1; | |
782 | ||
783 | errno=EIO; | |
784 | return -1; | |
785 | } | |
786 | ||
787 | // copy sector count register (one byte!) to return data | |
788 | if (command==CHECK_POWER_MODE) | |
789 | *data=*(char *)&(ata->sector_count); | |
790 | ||
791 | // look for nonexistent devices/ports | |
792 | if (command==IDENTIFY && !nonempty((unsigned char *)data, 512)) { | |
793 | errno=ENODEV; | |
794 | return -1; | |
795 | } | |
796 | ||
797 | return 0; | |
798 | } | |
799 | ||
800 | static int get_tw_channel_unit (const char* name, int* unit, int* dev) { | |
801 | const char *p; | |
802 | ||
803 | /* device node sanity check */ | |
804 | for (p = name + 3; *p; p++) | |
805 | if (*p < '0' || *p > '9') | |
806 | return -1; | |
807 | if (strlen(name) > 4 && *(name + 3) == '0') | |
808 | return -1; | |
809 | ||
810 | if (dev != NULL) | |
811 | *dev=atoi(name + 3); | |
812 | ||
813 | /* no need for unit number */ | |
814 | if (unit != NULL) | |
815 | *unit=0; | |
816 | return 0; | |
817 | } | |
818 | ||
819 | ||
820 | #ifndef IOCATAREQUEST | |
821 | static int get_ata_channel_unit ( const char* name, int* unit, int* dev) { | |
822 | #ifndef ATAREQUEST | |
823 | *dev=0; | |
824 | *unit=0; | |
825 | return 0; | |
826 | #else | |
827 | // there is no direct correlation between name 'ad0, ad1, ...' and | |
828 | // channel/unit number. So we need to iterate through the possible | |
829 | // channels and check each unit to see if we match names | |
830 | struct ata_cmd iocmd; | |
831 | int fd,maxunit; | |
832 | ||
833 | bzero(&iocmd, sizeof(struct ata_cmd)); | |
834 | ||
835 | if ((fd = open("/dev/ata", O_RDWR)) < 0) | |
836 | return -errno; | |
837 | ||
838 | iocmd.cmd = ATAGMAXCHANNEL; | |
839 | if (ioctl(fd, IOCATA, &iocmd) < 0) { | |
840 | return -errno; | |
841 | close(fd); | |
842 | } | |
843 | maxunit = iocmd.u.maxchan; | |
844 | for (*unit = 0; *unit < maxunit; (*unit)++) { | |
845 | iocmd.channel = *unit; | |
846 | iocmd.device = -1; | |
847 | iocmd.cmd = ATAGPARM; | |
848 | if (ioctl(fd, IOCATA, &iocmd) < 0) { | |
849 | close(fd); | |
850 | return -errno; | |
851 | } | |
852 | if (iocmd.u.param.type[0] && !strcmp(name,iocmd.u.param.name[0])) { | |
853 | *dev = 0; | |
854 | break; | |
855 | } | |
856 | if (iocmd.u.param.type[1] && !strcmp(name,iocmd.u.param.name[1])) { | |
857 | *dev = 1; | |
858 | break; | |
859 | } | |
860 | } | |
861 | close(fd); | |
862 | if (*unit == maxunit) | |
863 | return -1; | |
864 | else | |
865 | return 0; | |
866 | #endif | |
867 | } | |
868 | #endif | |
869 | ||
870 | // Guess device type (ata or scsi) based on device name (FreeBSD | |
871 | // specific) SCSI device name in FreeBSD can be sd, sr, scd, st, nst, | |
872 | // osst, nosst and sg. | |
873 | static const char * fbsd_dev_prefix = "/dev/"; | |
874 | static const char * fbsd_dev_ata_disk_prefix = "ad"; | |
875 | static const char * fbsd_dev_scsi_disk_plus = "da"; | |
876 | static const char * fbsd_dev_scsi_tape1 = "sa"; | |
877 | static const char * fbsd_dev_scsi_tape2 = "nsa"; | |
878 | static const char * fbsd_dev_scsi_tape3 = "esa"; | |
879 | static const char * fbsd_dev_twe_ctrl = "twe"; | |
880 | static const char * fbsd_dev_twa_ctrl = "twa"; | |
881 | ||
882 | static int parse_ata_chan_dev(const char * dev_name, struct freebsd_dev_channel *chan) { | |
883 | int len; | |
884 | int dev_prefix_len = strlen(fbsd_dev_prefix); | |
885 | ||
886 | // if dev_name null, or string length zero | |
887 | if (!dev_name || !(len = strlen(dev_name))) | |
888 | return CONTROLLER_UNKNOWN; | |
889 | ||
890 | // Remove the leading /dev/... if it's there | |
891 | if (!strncmp(fbsd_dev_prefix, dev_name, dev_prefix_len)) { | |
892 | if (len <= dev_prefix_len) | |
893 | // if nothing else in the string, unrecognized | |
894 | return CONTROLLER_UNKNOWN; | |
895 | // else advance pointer to following characters | |
896 | dev_name += dev_prefix_len; | |
897 | } | |
898 | // form /dev/ad* or ad* | |
899 | if (!strncmp(fbsd_dev_ata_disk_prefix, dev_name, | |
900 | strlen(fbsd_dev_ata_disk_prefix))) { | |
901 | #ifndef IOCATAREQUEST | |
902 | if (chan != NULL) { | |
903 | if (get_ata_channel_unit(dev_name,&(chan->channel),&(chan->device))<0) { | |
904 | return CONTROLLER_UNKNOWN; | |
905 | } | |
906 | } | |
907 | #endif | |
908 | return CONTROLLER_ATA; | |
909 | } | |
910 | ||
911 | // form /dev/da* or da* | |
912 | if (!strncmp(fbsd_dev_scsi_disk_plus, dev_name, | |
913 | strlen(fbsd_dev_scsi_disk_plus))) | |
914 | goto handlescsi; | |
915 | ||
916 | // form /dev/sa* or sa* | |
917 | if (!strncmp(fbsd_dev_scsi_tape1, dev_name, | |
918 | strlen(fbsd_dev_scsi_tape1))) | |
919 | goto handlescsi; | |
920 | ||
921 | // form /dev/nsa* or nsa* | |
922 | if (!strncmp(fbsd_dev_scsi_tape2, dev_name, | |
923 | strlen(fbsd_dev_scsi_tape2))) | |
924 | goto handlescsi; | |
925 | ||
926 | // form /dev/esa* or esa* | |
927 | if (!strncmp(fbsd_dev_scsi_tape3, dev_name, | |
928 | strlen(fbsd_dev_scsi_tape3))) | |
929 | goto handlescsi; | |
930 | ||
931 | if (!strncmp(fbsd_dev_twa_ctrl,dev_name, | |
932 | strlen(fbsd_dev_twa_ctrl))) { | |
933 | if (chan != NULL) { | |
934 | if (get_tw_channel_unit(dev_name,&(chan->channel),&(chan->device))<0) { | |
935 | return CONTROLLER_UNKNOWN; | |
936 | } | |
937 | } | |
938 | else if (get_tw_channel_unit(dev_name,NULL,NULL)<0) { | |
939 | return CONTROLLER_UNKNOWN; | |
940 | } | |
941 | return CONTROLLER_3WARE_9000_CHAR; | |
942 | } | |
943 | ||
944 | if (!strncmp(fbsd_dev_twe_ctrl,dev_name, | |
945 | strlen(fbsd_dev_twe_ctrl))) { | |
946 | if (chan != NULL) { | |
947 | if (get_tw_channel_unit(dev_name,&(chan->channel),&(chan->device))<0) { | |
948 | return CONTROLLER_UNKNOWN; | |
949 | } | |
950 | } | |
951 | else if (get_tw_channel_unit(dev_name,NULL,NULL)<0) { | |
952 | return CONTROLLER_UNKNOWN; | |
953 | } | |
954 | return CONTROLLER_3WARE_678K_CHAR; | |
955 | } | |
956 | ||
957 | // we failed to recognize any of the forms | |
958 | return CONTROLLER_UNKNOWN; | |
959 | ||
960 | handlescsi: | |
961 | if (chan != NULL) { | |
4d59bff9 | 962 | if (!(chan->devname = (char *)calloc(1,DEV_IDLEN+1))) |
832b75ed GG |
963 | return CONTROLLER_UNKNOWN; |
964 | ||
965 | if (cam_get_device(dev_name,chan->devname,DEV_IDLEN,&(chan->unitnum)) == -1) | |
966 | return CONTROLLER_UNKNOWN; | |
967 | } | |
968 | return CONTROLLER_SCSI; | |
969 | ||
970 | } | |
971 | ||
972 | int guess_device_type (const char* dev_name) { | |
973 | return parse_ata_chan_dev(dev_name,NULL); | |
974 | } | |
975 | ||
976 | // global variable holding byte count of allocated memory | |
977 | extern long long bytes; | |
978 | ||
979 | // we are going to take advantage of the fact that FreeBSD's devfs will only | |
980 | // have device entries for devices that exist. So if we get the equivilent of | |
981 | // ls /dev/ad?, we have all the ATA devices on the system | |
982 | // | |
983 | // If any errors occur, leave errno set as it was returned by the | |
984 | // system call, and return <0. | |
985 | ||
986 | // Return values: | |
987 | // -1 out of memory | |
988 | // -2 to -5 errors in glob | |
989 | ||
990 | int get_dev_names(char*** names, const char* prefix) { | |
991 | int n = 0; | |
992 | char** mp; | |
993 | int retglob,lim; | |
994 | glob_t globbuf; | |
995 | int i; | |
996 | char pattern1[128],pattern2[128]; | |
997 | ||
998 | bzero(&globbuf,sizeof(globbuf)); | |
999 | // in case of non-clean exit | |
1000 | *names=NULL; | |
1001 | ||
1002 | // handle 0-99 possible devices, will still be limited by MAX_NUM_DEV | |
1003 | sprintf(pattern1,"/dev/%s[0-9]",prefix); | |
1004 | sprintf(pattern2,"/dev/%s[0-9][0-9]",prefix); | |
1005 | ||
1006 | // Use glob to look for any directory entries matching the patterns | |
1007 | // first call inits with first pattern match, second call appends | |
1008 | // to first list. Turn on NOCHECK for second call. This results in no | |
1009 | // error if no more matches found, however it does append the actual | |
1010 | // pattern to the list of paths.... | |
1011 | if ((retglob=glob(pattern1, GLOB_ERR, NULL, &globbuf)) || | |
1012 | (retglob=glob(pattern2, GLOB_ERR|GLOB_APPEND|GLOB_NOCHECK,NULL,&globbuf))) { | |
1013 | int retval = -1; | |
1014 | // glob failed | |
1015 | if (retglob==GLOB_NOSPACE) | |
1016 | pout("glob(3) ran out of memory matching patterns (%s),(%s)\n", | |
1017 | pattern1, pattern2); | |
1018 | else if (retglob==GLOB_ABORTED) | |
1019 | pout("glob(3) aborted matching patterns (%s),(%s)\n", | |
1020 | pattern1, pattern2); | |
1021 | else if (retglob==GLOB_NOMATCH) { | |
1022 | pout("glob(3) found no matches for patterns (%s),(%s)\n", | |
1023 | pattern1, pattern2); | |
1024 | retval = 0; | |
1025 | } | |
1026 | else if (retglob) | |
1027 | pout("Unexplained error in glob(3) of patterns (%s),(%s)\n", | |
1028 | pattern1, pattern2); | |
1029 | ||
1030 | // Free memory and return | |
1031 | globfree(&globbuf); | |
1032 | ||
1033 | return retval; | |
1034 | } | |
1035 | ||
1036 | // did we find too many paths? | |
1037 | // did we find too many paths? | |
1038 | lim = globbuf.gl_pathc < MAX_NUM_DEV ? globbuf.gl_pathc : MAX_NUM_DEV; | |
1039 | if (lim < globbuf.gl_pathc) | |
1040 | pout("glob(3) found %d > MAX=%d devices matching patterns (%s),(%s): ignoring %d paths\n", | |
1041 | globbuf.gl_pathc, MAX_NUM_DEV, pattern1,pattern2, | |
1042 | globbuf.gl_pathc-MAX_NUM_DEV); | |
1043 | ||
1044 | // allocate space for up to lim number of ATA devices | |
1045 | if (!(mp = (char **)calloc(lim, sizeof(char*)))){ | |
1046 | pout("Out of memory constructing scan device list\n"); | |
1047 | return -1; | |
1048 | } | |
1049 | ||
1050 | // now step through the list returned by glob. No link checking needed | |
1051 | // in FreeBSD | |
1052 | for (i=0; i<globbuf.gl_pathc; i++){ | |
1053 | // becuase of the NO_CHECK on second call to glob, | |
1054 | // the pattern itself will be added to path list.. | |
1055 | // so ignore any paths that have the ']' from pattern | |
1056 | if (strchr(globbuf.gl_pathv[i],']') == NULL) | |
1057 | mp[n++] = CustomStrDup(globbuf.gl_pathv[i], 1, __LINE__, filenameandversion); | |
1058 | } | |
1059 | ||
1060 | globfree(&globbuf); | |
4d59bff9 | 1061 | mp = (char **)realloc(mp,n*(sizeof(char*))); // shrink to correct size |
832b75ed GG |
1062 | bytes += (n)*(sizeof(char*)); // and set allocated byte count |
1063 | *names=mp; | |
1064 | return n; | |
1065 | } | |
1066 | ||
1067 | int make_device_names (char*** devlist, const char* name) { | |
1068 | if (!strcmp(name,"SCSI")) | |
1069 | return get_dev_names(devlist,"da"); | |
1070 | else if (!strcmp(name,"ATA")) | |
1071 | return get_dev_names(devlist,"ad"); | |
1072 | else | |
1073 | return 0; | |
1074 | } |