"SfR Fresh" - the SfR Freeware/Shareware Archive

Member "mapscsi-0.0.11/scsi_api.c" of archive mapscsi-0.0.11.tar.gz:


As a special service "SfR Fresh" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. That can be also achieved for any archive member file by clicking within an archive contents listing on the first character of the file(path) respectively on the according byte size field.
    1 #include <stdio.h>
    2 #include <stdlib.h>
    3 #include <sys/types.h>
    4 #include <sys/stat.h>
    5 #include <unistd.h>
    6 #include <string.h>
    7 #include <fcntl.h>
    8 #include <errno.h>
    9 #include <limits.h>
   10 #include <libgen.h>
   11 #include <signal.h>
   12 #include <syslog.h>
   13 
   14 #include "scsi_api.h"
   15 
   16 
   17 /* global linked list of scsi devices */
   18 scsi_device_t *scsidev_head = NULL;
   19 
   20 
   21 static int decode_int(void *dest, const char *str, size_t size)
   22 {
   23     return sscanf(str, "%d", (int*)dest);
   24 }
   25 
   26 
   27 static int encode_int(char *str, void *src, size_t size)
   28 {
   29     return snprintf(str, size, "%d", *(int*)src);
   30 }
   31 
   32 
   33 static int decode_str(void *dest, const char *str, size_t size)
   34 {
   35     int l = strlen(strncpy((char*)dest, str, size));
   36     return (l == size) ? -1 : l;
   37 }
   38 
   39 
   40 static int encode_str(char *str, void *src, size_t size)
   41 {
   42     int l = strlen(strncpy(str, (char*)src, size));
   43     return (l == size) ? -1 : l;
   44 }
   45 
   46 
   47 static int encode_strp(char *str, void *src, size_t size)
   48 {
   49     if(*((char**)src)) {
   50 	int l = strlen(strncpy(str, *((char**)src), size));
   51 	return (l == size) ? -1 : l;
   52     } else {
   53 	str[0] = '\0';
   54 	return 0;
   55     }
   56 }
   57 
   58 
   59 static int decode_strp(void *dest, const char *str, size_t size)
   60 {
   61     int l = strlen(*((char**)dest) = strdup(str));
   62     return (l == size) ? -1 : l;
   63 }
   64 
   65 
   66 static int decode_type(void *dest, const char *str, size_t size)
   67 {
   68     if(strcmp(str, "disk") == 0) {
   69 	*(int*)dest = TYPE_DISK;
   70 	return 1;
   71     } else if(strcmp(str, "processor") == 0) {
   72 	*(int*)dest = TYPE_PROCESSOR;
   73 	return 1;
   74     } else if(strcmp(str, "cdrom") == 0) {
   75 	*(int*)dest = TYPE_ROM;
   76 	return 1;
   77     } else if(strcmp(str, "worm") == 0) {
   78 	*(int*)dest = TYPE_WORM;
   79 	return 1;
   80     } else if(strcmp(str, "tape") == 0) {
   81 	*(int*)dest = TYPE_TAPE;
   82 	return 1;
   83     } else if(strcmp(str, "mod") == 0) {
   84 	*(int*)dest = TYPE_MOD;
   85 	return 1;
   86     } else if(strcmp(str, "scanner") == 0) {
   87 	*(int*)dest = TYPE_SCANNER;
   88 	return 1;
   89     }
   90     return 0;
   91 }
   92 
   93 
   94 static int encode_type(char *str, void *src, size_t size)
   95 {
   96     switch(*(int*)src) {
   97     case TYPE_DISK:
   98 	return snprintf(str, size, "%s", "disk");
   99 	break;
  100     case TYPE_PROCESSOR:
  101 	return snprintf(str, size, "%s", "processor");
  102 	break;
  103     case TYPE_ROM:
  104 	return snprintf(str, size, "%s", "cdrom");
  105 	break;
  106     case TYPE_WORM:
  107 	return snprintf(str, size, "%s", "worm");
  108 	break;
  109     case TYPE_TAPE:
  110 	return snprintf(str, size, "%s", "tape");
  111 	break;
  112     case TYPE_MOD:
  113 	return snprintf(str, size, "%s", "mod");
  114 	break;
  115     case TYPE_SCANNER:
  116 	return snprintf(str, size, "%s", "scanner");
  117 	break;
  118     default:
  119 	return snprintf(str, size, "%s", "other");
  120     }
  121 }
  122 
  123 
  124 scsi_device_param_t params[] = {
  125     { 'h', "host", "Host / adapter no", &encode_int, &decode_int,
  126       offsetof(scsi_device_t, host), 0 },
  127     { 'c', "channel", "Channel no", &encode_int, &decode_int,
  128       offsetof(scsi_device_t, channel), 0 },
  129     { 't', "id", "Target / scsi id", &encode_int, &decode_int,
  130       offsetof(scsi_device_t, id), 0 },
  131     { 'l', "lun", "Lun", &encode_int, &decode_int,
  132       offsetof(scsi_device_t, lun), 0 },
  133     { 'p', "part", "Partition number", &encode_int, &decode_int,
  134       offsetof(scsi_device_t, part), 0 },
  135     { 'w', "fcwwnn", "FC World Wide Node Name", &encode_str, &decode_str,
  136       offsetof(scsi_device_t, wwnn), msizeof(scsi_device_t, wwnn) },
  137     { 'W', "fcwwpn", "FC World Wide Port Name", &encode_str, &decode_str,
  138       offsetof(scsi_device_t, wwpn), msizeof(scsi_device_t, wwpn) },
  139     { 'I', "fcdid", "FC Port ID", &encode_str, &decode_str,
  140       offsetof(scsi_device_t, portid), msizeof(scsi_device_t, portid) },
  141     { 'L', "fclid", "FC Loop ID", &encode_int, &decode_int,
  142       offsetof(scsi_device_t, loopid), 0 },
  143     { 'B', "pci", "PCI bus:dev.func", &encode_str, &decode_str,
  144       offsetof(scsi_device_t, pciinfo), msizeof(scsi_device_t, pciinfo) },
  145     { 'H', "hostname", "HBA Driver name", &encode_str, &decode_str,
  146       offsetof(scsi_device_t, hostname), msizeof(scsi_device_t, hostname) },
  147     { 'V', "vendor", "Vendor Inquiry info", &encode_str, &decode_str,
  148       offsetof(scsi_device_t, vendor), msizeof(scsi_device_t, vendor) },
  149     { 'P', "product", "Product Inquiry info", &encode_str, &decode_str,
  150       offsetof(scsi_device_t, product), msizeof(scsi_device_t, product) },
  151     { 'R', "revision", "Revision Inquiry info", &encode_str, &decode_str,
  152       offsetof(scsi_device_t, revision), msizeof(scsi_device_t, revision) },
  153     { 'S', "serial", "Serial number Inquiry info", &encode_str, &decode_str,
  154       offsetof(scsi_device_t, serial), msizeof(scsi_device_t, serial) },
  155     { 'D', "prefix", "Device prefix (sd, scd)", &encode_str, &decode_str,
  156       offsetof(scsi_device_t, prefix), msizeof(scsi_device_t, prefix) },
  157     { 'T', "type", "Device type (disk, cdrom)", &encode_type, &decode_type,
  158       offsetof(scsi_device_t, type), 0 },
  159     { 'g', "device", "Generic device (/dev/sg0)", &encode_strp, &decode_strp,
  160       offsetof(scsi_device_t, device), msizeof(scsi_device_t, device) },
  161     { 'd', "sg_device", "Device (/dev/sda)", &encode_strp, &decode_strp,
  162       offsetof(scsi_device_t, sg_device), msizeof(scsi_device_t, sg_device) },
  163     { '\0', "", "", NULL, NULL, 0, 0 }
  164 };
  165 
  166 
  167 /* process a complete SCSI cmd. Use the generic SCSI interface. */
  168 int handle_scsi_cmd(int fd,
  169 		    unsigned cmd_len,         /* command length */
  170 		    unsigned in_size,         /* input data size */
  171 		    unsigned char *i_buff,    /* input buffer */
  172 		    unsigned out_size,        /* output data size */
  173 		    unsigned char *o_buff     /* output buffer */)
  174 {
  175     int status = 0;
  176     struct sg_header *sg_hd;
  177 
  178     /* safety checks */
  179     if (!cmd_len) return -1;            /* need a cmd_len != 0 */
  180     if (!i_buff) return -1;             /* need an input buffer != NULL */
  181     if (SCSI_OFF + cmd_len + in_size > 4096) return -1;
  182     if (SCSI_OFF + out_size > 4096) return -1;
  183 
  184     if (!o_buff) out_size = 0;      /* no output buffer, no output size */
  185 
  186     /* generic SCSI device header construction */
  187     sg_hd = (struct sg_header *) i_buff;
  188     sg_hd->reply_len   = SCSI_OFF + out_size;
  189     sg_hd->twelve_byte = cmd_len == 12;
  190     sg_hd->result = 0;
  191 
  192     /* send command */
  193     status = write( fd, i_buff, SCSI_OFF + cmd_len + in_size );
  194     if ( status < 0 || status != SCSI_OFF + cmd_len + in_size ||
  195 	 sg_hd->result ) {
  196 	/* some error happened */
  197 	fprintf( stderr, "write(generic) result = 0x%x cmd = 0x%x\n",
  198 		 sg_hd->result, i_buff[SCSI_OFF] );
  199 	perror("");
  200 	return status;
  201     }
  202 
  203     if (!o_buff) o_buff = i_buff;       /* buffer pointer check */
  204 
  205     /* retrieve result */
  206     status = read( fd, o_buff, SCSI_OFF + out_size);
  207     if ( status < 0 || status != SCSI_OFF + out_size || sg_hd->result ) {
  208 	/* some error happened */
  209 	fprintf( stderr, "read(generic) status = 0x%x, result = 0x%x, "
  210 		 "cmd = 0x%x\n",
  211 		 status, sg_hd->result, o_buff[SCSI_OFF] );
  212 	fprintf( stderr, "read(generic) sense "
  213 		 "%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n",
  214 		 sg_hd->sense_buffer[0], sg_hd->sense_buffer[1],
  215 		 sg_hd->sense_buffer[2], sg_hd->sense_buffer[3],
  216 		 sg_hd->sense_buffer[4], sg_hd->sense_buffer[5],
  217 		 sg_hd->sense_buffer[6], sg_hd->sense_buffer[7],
  218 		 sg_hd->sense_buffer[8], sg_hd->sense_buffer[9],
  219 		 sg_hd->sense_buffer[10], sg_hd->sense_buffer[11],
  220 		 sg_hd->sense_buffer[12], sg_hd->sense_buffer[13],
  221 		 sg_hd->sense_buffer[14], sg_hd->sense_buffer[15]);
  222 	if (status < 0)
  223 	    perror("");
  224     }
  225     /* Look if we got what we expected to get */
  226     if (status == SCSI_OFF + out_size) status = 0; /* got them all */
  227 
  228     return status;  /* 0 means no error */
  229 }
  230 
  231 
  232 /* request vendor brand and model - use evpd=0, op=0 */
  233 /* request serial number - use evpd=1, op=0x80 */
  234 unsigned char *scsi_inquiry (int fd, int evpd, int pg)
  235 {
  236     static unsigned char cmd[SCSI_OFF + 18];      /* SCSI command buffer */
  237     static unsigned char scsi_inquiry_buffer[SCSI_OFF + INQUIRY_REPLY_LEN];
  238     unsigned char cmdblk [ INQUIRY_CMDLEN ] =
  239     { INQUIRY_CMD,  /* command */
  240       evpd,  /* lun/reserved */
  241       pg,  /* page code */
  242       0,  /* reserved */
  243       INQUIRY_REPLY_LEN,  /* allocation length */
  244       0 };/* reserved/flag/link */
  245 
  246     memcpy( cmd + SCSI_OFF, cmdblk, sizeof(cmdblk) );
  247 
  248     if (handle_scsi_cmd(fd, sizeof(cmdblk), 0, cmd,
  249 			sizeof(scsi_inquiry_buffer) - SCSI_OFF,
  250 			scsi_inquiry_buffer)) {
  251 	fprintf( stderr, "Inquiry failed\n" );
  252 	exit(2);
  253     }
  254     return (scsi_inquiry_buffer + SCSI_OFF);
  255 }
  256 
  257 
  258 static void str_trim(char *p)
  259 {
  260     char *t = p + strlen(p) - 1;
  261     while(t >= p) {
  262 	if(*t == 0x20) *t-- = '\0';
  263 	else break;
  264     }
  265 }
  266 
  267 
  268 static void str_path(char *p)
  269 {
  270     char *t, *r, *w = p;
  271 
  272     t = r = strdup(p);
  273     while(*r) {
  274 	if(*r != ' ') *w++ = *r++;
  275 	else r++;
  276     }
  277     *w = '\0';
  278     free(t);
  279     while(*p) {
  280 	if(!isalnum(*p) && *p != '/') *p = '-';
  281 	p++;
  282     }
  283 }
  284 
  285 
  286 int get_scsi_dev_info(int fd, scsi_device_t *scsidev)
  287 {
  288     unsigned char *buf;
  289     Sg_scsi_id scsi_id;
  290     char hostname[HOSTNAME_LEN+1];
  291     int l;
  292 
  293     /* Get host no, channel and scsi id */
  294     if(ioctl(fd, SG_GET_SCSI_ID, &scsi_id) < 0) {
  295 	perror("ioctl");
  296 	exit(1);
  297     }
  298 
  299     scsidev->host = scsi_id.host_no;
  300     scsidev->channel = scsi_id.channel;
  301     scsidev->id = scsi_id.scsi_id;
  302     scsidev->lun = scsi_id.lun;
  303 
  304     /* Get scsi inquiry info */
  305     buf = scsi_inquiry(fd, 0, 0);
  306 
  307     scsidev->active = 1;
  308 
  309     scsidev->type = *(buf+INQUIRY_PERIPHERAL_OFFSET);
  310 
  311     memcpy(scsidev->vendor,
  312 	   buf + INQUIRY_VENDOR_OFFSET, INQUIRY_VENDOR_LENGTH);
  313     scsidev->vendor[INQUIRY_VENDOR_LENGTH] = '\0';
  314     str_trim(scsidev->vendor);
  315 
  316     memcpy(scsidev->revision,
  317 	   buf + INQUIRY_REVISION_OFFSET, INQUIRY_REVISION_LENGTH);
  318     scsidev->revision[INQUIRY_REVISION_LENGTH] = '\0';
  319     str_trim(scsidev->revision);
  320 
  321     memcpy(scsidev->product,
  322 	   buf + INQUIRY_PRODUCT_OFFSET, INQUIRY_PRODUCT_LENGTH);
  323     scsidev->product[INQUIRY_PRODUCT_LENGTH] = '\0';
  324     str_trim(scsidev->product);
  325 
  326     memcpy(scsidev->safteid, buf + INQUIRY_SAFTEID_OFFSET, INQUIRY_SAFTEID_LENGTH);
  327     scsidev->safteid[INQUIRY_SAFTEID_LENGTH] = '\0';
  328 
  329     scsidev->channelid = *(buf+INQUIRY_CHANNELID_OFFSET);
  330 
  331     /* Get serial number */
  332     buf = scsi_inquiry(fd, 1, 0x80);
  333     l = buf[3];
  334     memcpy(scsidev->serial, buf + 4, l);
  335     scsidev->serial[l] = '\0';
  336     str_trim(scsidev->serial);
  337 
  338     /* Get the hostname */
  339     *(int*)hostname = HOSTNAME_LEN;
  340     if(ioctl (fd, SCSI_IOCTL_PROBE_HOST, hostname) >= 0) {
  341 	strncpy(scsidev->hostname, hostname, HOSTNAME_LEN);
  342 	str_trim(scsidev->hostname);
  343     }
  344 
  345     /* Get the PCI bus:dev.fn */
  346     if(ioctl(fd, SCSI_IOCTL_GET_PCI, scsidev->pciinfo) < 0) {
  347 	/* we ignore the error, either ioctl is not supported
  348 	   or the scsi device is not PCI (ide-scsi) */
  349     }
  350 
  351     /* Get the WWN to scsi id map from Qlogic cards */
  352     if(strncmp(scsidev->hostname, "QLogic QLA2", 11) == 0)
  353 	qlogic_probe(fd);
  354 
  355     return 0;
  356 }
  357 
  358 
  359 void make_dev_name(char * fname, const char * leadin, int k,
  360 		   int do_numeric)
  361 {
  362     char buff[64];
  363     int  big,little;
  364 
  365     if(leadin[0] == '/') strcpy(fname, leadin);
  366     else sprintf(fname, "/dev/%s", leadin);
  367     if (do_numeric) {
  368 	sprintf(buff, "%d", k);
  369 	strcat(fname, buff);
  370     }
  371     else {
  372 	if (k < 26) {
  373 	    buff[0] = 'a' + (char)k;
  374 	    buff[1] = '\0';
  375 	    strcat(fname, buff);
  376 	}
  377 	else if (k <= 255) {
  378 	    /* assumes sequence goes x,y,z,aa,ab,ac etc */
  379 	    big    = k/26;
  380 	    little = k - (26 * big);
  381 	    big    = big - 1;
  382 
  383 	    buff[0] = 'a' + (char)big;
  384 	    buff[1] = 'a' + (char)little;
  385 	    buff[2] = '\0';
  386 	    strcat(fname, buff);
  387 	}
  388 	else
  389 	    strcat(fname, "xxxx");
  390     }
  391 }
  392 
  393 
  394 scsi_device_t* find_dev_by_loc(int host, int channel, int id, int lun)
  395 {
  396     scsi_device_t *scsidev = scsidev_head;
  397 
  398     while(scsidev->next) {
  399 	if (host == scsidev->host && channel == scsidev->channel &&
  400 	    id == scsidev->id && lun == scsidev->lun) return scsidev;
  401 	scsidev = scsidev->next;
  402     }
  403     return NULL;
  404 }
  405 
  406 
  407 scsi_device_t* find_dev_by_name(const char* device)
  408 {
  409     scsi_device_t *scsidev = scsidev_head;
  410     char device_temp[PATH_MAX+1];
  411 
  412 
  413     if(device[0] == '/') strcpy(device_temp, device);
  414     else sprintf(device_temp, "/dev/%s", device);
  415 
  416     while(scsidev->next) {
  417 	if (scsidev->device && strcmp(device_temp, scsidev->device) == 0)
  418 	    return scsidev;
  419 	scsidev = scsidev->next;
  420     }
  421     return NULL;
  422 }
  423 
  424 
  425 void map_sg_devices(int type, const char* prefix, int numeric)
  426 {
  427     scsi_device_t *scsidev = NULL;
  428     int fd, k, res, num_errors = 0;
  429     int host, channel, id, lun;
  430     scsi_idlun_t idlun;
  431     char device[PATH_MAX+1];
  432 
  433     /* find sd? devices that map to sg? devices */
  434     for (k = 0, res = 0; num_errors < MAX_ERRORS; k++) {
  435 
  436 	make_dev_name(device, prefix, k, numeric);
  437 
  438 	fd = open(device, O_RDONLY | O_NONBLOCK);
  439 	if (fd < 0) {
  440 	    if (EBUSY == errno) {
  441 		printf("Device %s is busy\n", device);
  442 		++num_errors;
  443 		continue;
  444 	    }
  445 	    else if ((ENODEV == errno) || (ENOENT == errno) ||
  446 		     (ENXIO == errno)) {
  447 		++num_errors;
  448 		continue;
  449 	    }
  450 	    else {
  451 		perror("open");
  452 		++num_errors;
  453 		continue;
  454 	    }
  455 	}
  456 
  457 	res = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &idlun);
  458 	if (res < 0) {
  459 	    perror("ioctl");
  460 	    ++num_errors;
  461 	    continue;
  462 	}
  463 	res = ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &host);
  464 	if (res < 0) {
  465 	    perror("ioctl");
  466 	    ++num_errors;
  467 	    continue;
  468 	}
  469 
  470 	id = idlun.dev_id & 0xff;
  471 	lun = (idlun.dev_id >> 8) & 0xff;
  472 	channel = (idlun.dev_id >> 16) & 0xff;
  473 
  474 	scsidev = find_dev_by_loc(host, channel, id, lun);
  475 	if (scsidev && scsidev->type == type) {
  476 	    scsidev->device = strdup(device);
  477 	    strncpy(scsidev->prefix, prefix, PREFIX_LEN);
  478 	    /* printf("%s - %d %d %d %d maps to %s\n",
  479 	       device, host, channel, id, lun, scsidev->sg_device); */
  480 	} else {
  481 	    /* printf("can't find sd device for %s", scsidev->sg_device); */
  482 	}
  483 
  484 	close(fd);
  485 
  486     }
  487 }
  488 
  489 
  490 int scan_scsi_devices(int sg_numeric)
  491 {
  492     char tmp[PATH_MAX];
  493     scsi_device_t *scsidev = scsidev_head;
  494     int fd, ind, k, last_sg_ind, num_errors = 0;
  495 
  496     for (k = 0; (k < MAX_SCSI_DEVS)  && (num_errors < MAX_ERRORS); k++) {
  497 
  498 	make_dev_name(tmp, "/dev/sg", k, sg_numeric);
  499 	scsidev->sg_device = strdup(tmp);
  500 
  501 	fd = open(scsidev->sg_device, O_RDWR);
  502 
  503 	if (fd < 0) {
  504 	    if (EBUSY == errno) {
  505 		scsidev->active = -2;
  506 		continue;
  507 	    } else if ((ENODEV == errno) ||
  508 		       (ENOENT == errno) || (ENXIO == errno)) {
  509 		++num_errors;
  510 		scsidev->active = -1;
  511 		continue;
  512 	    } else {
  513 		scsidev->active = 0;
  514 		++num_errors;
  515 		continue;
  516 	    }
  517 	}
  518 
  519 	get_scsi_dev_info(fd, scsidev);
  520 #if DEBUG
  521 	print_scsi_dev_info(scsidev);
  522 #endif
  523 	close(fd);
  524 	scsidev->next = calloc(1, sizeof(scsi_device_t));
  525 	scsidev = scsidev->next;
  526     }
  527 
  528     return 0;
  529 }
  530 
  531 
  532 void print_scsi_dev_info(scsi_device_t *scsidev)
  533 {
  534     char scsiinfo[1024] = "";
  535     char tmpbuf[128] = "";
  536     char* scsicodes = "ThctlVPRSBdg";
  537     char* fccodes = "wWIL";
  538     char *code;
  539     int l;
  540 
  541     code = scsicodes;
  542     while(*code) {
  543 	scsi_device_param_t *param = params;
  544 	while(param->substchar) {
  545 	    if(param->substchar == *code) break;
  546 	    param++;
  547 	}
  548 	if(param->enc) {
  549 	    if(param->enc(tmpbuf, ((void*)scsidev)+param->offset, 128) > 0) {
  550 		if(scsiinfo[0]) strncat(scsiinfo, " ", 1024-strlen(scsiinfo));
  551 		strncat(scsiinfo, param->name, 1024-strlen(scsiinfo));
  552 		strncat(scsiinfo, "=", 1024-strlen(scsiinfo));
  553 		if(param->enc != encode_int)
  554 		    strncat(scsiinfo, "\"", 1024-strlen(scsiinfo));
  555 		strncat(scsiinfo, tmpbuf, 1024-strlen(scsiinfo));
  556 		if(param->enc != encode_int)
  557 		    strncat(scsiinfo, "\"", 1024-strlen(scsiinfo));
  558 	    }
  559 	}
  560 	code++;
  561     }
  562     if(!scsidev->isfc) goto notfc;
  563     code = fccodes;
  564     while(*code) {
  565 	scsi_device_param_t *param = params;
  566 	while(param->substchar) {
  567 	    if(param->substchar == *code) break;
  568 	    param++;
  569 	}
  570 	if(param->enc) {
  571 	    if(param->enc(tmpbuf, ((void*)scsidev)+param->offset, 128) > 0) {
  572 		strncat(scsiinfo, " ", 1024-strlen(scsiinfo));
  573 		strncat(scsiinfo, param->name, 1024-strlen(scsiinfo));
  574 		strncat(scsiinfo, "=", 1024-strlen(scsiinfo));
  575 		if(param->enc != encode_int)
  576 		    strncat(scsiinfo, "\"", 1024-strlen(scsiinfo));
  577 		strncat(scsiinfo, tmpbuf, 1024-strlen(scsiinfo));
  578 		if(param->enc != encode_int)
  579 		    strncat(scsiinfo, "\"", 1024-strlen(scsiinfo));
  580 	    }
  581 	}
  582 	code++;
  583     }
  584 
  585  notfc:
  586     printf("%s\n", scsiinfo);
  587 }
  588 
  589 
  590 int free_scsidev(scsi_device_t *scsidev)
  591 {
  592     scsi_device_t *c_scsidev = scsidev;
  593 
  594     while(c_scsidev->next) {
  595 	scsi_device_t *t = c_scsidev;
  596 	c_scsidev = c_scsidev->next;
  597 	if(t->device) free(t->device);
  598 	if(t->sg_device) free(t->sg_device);
  599 	free(t);
  600     }
  601     return 0;
  602 }
  603 
  604