Commit 6190b834 authored by Andrey Filippov's avatar Andrey Filippov
Browse files

implemented ssd raw data sending

parent 7b0fd0dd
Loading
Loading
Loading
Loading
+150 −25
Original line number Original line Diff line number Diff line
@@ -14,7 +14,7 @@
 * You should have received a copy of the GNU General Public License
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 */

#define _LARGEFILE64_SOURCE
#include <unistd.h>
#include <unistd.h>
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
@@ -23,6 +23,7 @@
#include <errno.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <sys/stat.h>
#include <time.h>
#include <time.h>
#include <string.h>
#include <string.h>
@@ -66,6 +67,23 @@
#define GLOBALPARS_SNGL(x) (globalPars[(x)-FRAMEPAR_GLOBALS])     ///< should work in drivers and applications, First 32 parameter values are not erased with initGlobalPars
#define GLOBALPARS_SNGL(x) (globalPars[(x)-FRAMEPAR_GLOBALS])     ///< should work in drivers and applications, First 32 parameter values are not erased with initGlobalPars
#endif
#endif


int cumulHistImage ( // returns number of pixels
		uint16_t * src,
		int *      offs,
		int        len,
		int        width,
		int        height,
		int        telem,
		int        bin_shift,
		int        hist_size,
		int        hist_min,
		int *      hist16, // if null, will only calculate *sum_val, *mn and *mx
		long long* sum_val,
		int *      mn,
		int *      mx
		);


/**
/**
 * @struct file_set
 * @struct file_set
 * This structure holds a set of file names and file descriptors corresponding to
 * This structure holds a set of file names and file descriptors corresponding to
@@ -110,6 +128,8 @@ struct file_set {
    int            sensor_port;
    int            sensor_port;
    int            timestamp_name;
    int            timestamp_name;
    int            base_chn;
    int            base_chn;
    char          *ssd_name;
    int            ssd_fd; // SSD raw disk file descriptor
    // data to convert TIFF to 8-bit uncompressed bmp for quick preview
    // data to convert TIFF to 8-bit uncompressed bmp for quick preview
//    int            tiff_mn;
//    int            tiff_mn;
//    int            tiff_mx;
//    int            tiff_mx;
@@ -120,7 +140,7 @@ struct file_set {
};
};


static struct file_set  files[SENSOR_PORTS];
static struct file_set  files[SENSOR_PORTS];

static char ssd_name[256];
unsigned long * ccam_dma_buf;
unsigned long * ccam_dma_buf;
char trailer[TRAILER_SIZE] = { 0xff, 0xd9 };
char trailer[TRAILER_SIZE] = { 0xff, 0xd9 };
const char *circbuf_fnames[] = {
const char *circbuf_fnames[] = {
@@ -217,7 +237,9 @@ const char url_args[] = "This server supports sequence of commands entered in th
		"tiff_auto=[value] may preceed tiff_convert and calculate and set min/max for conversion using tiff_stat internally.\n"
		"tiff_auto=[value] may preceed tiff_convert and calculate and set min/max for conversion using tiff_stat internally.\n"
		"          Value is a 2-digit decimal, low digit applies to cut from the lower (cold) values, high digit - to cut from the high (hot) ones:\n"
		"          Value is a 2-digit decimal, low digit applies to cut from the lower (cold) values, high digit - to cut from the high (hot) ones:\n"
		"          Value 0 - use asolute min/max values, 1 - 0.1%% to 100.0%%, 10 - 0%% to 99.9%%, 22 - from 0.5%% to 99.5%%, 03 - from 1%% to 100%%,\n"
		"          Value 0 - use asolute min/max values, 1 - 0.1%% to 100.0%%, 10 - 0%% to 99.9%%, 22 - from 0.5%% to 99.5%%, 03 - from 1%% to 100%%,\n"
		"          Value 24 - from 5%% to 99.5%%, 55 - 10%% to 90%%.\n";
		"          Value 24 - from 5%% to 99.5%%, 55 - 10%% to 90%%.\n"
		"read raw SSD data (device provided in command line, such as 'imgsrv -p 2323 -s /dev/sda2')"
		"ssdO...O:C...C - send raw SSD data at byte offset O...O, count C...C bytes";


// path to file containing serial number
// path to file containing serial number
static const char path_to_serial[] = "/sys/devices/soc0/elphel393-init/serial";
static const char path_to_serial[] = "/sys/devices/soc0/elphel393-init/serial";
@@ -249,11 +271,11 @@ unsigned long getCurrentFrameNumberCompressor( struct file_set *fset);
 * @param[in]   serial     pointer to buffer
 * @param[in]   serial     pointer to buffer
 * @return      0 if data was extracted successfully and negative error code otherwise
 * @return      0 if data was extracted successfully and negative error code otherwise
 */
 */
int readCameraSerialNumberFromSysFS(char *path,char *serial){
int readCameraSerialNumberFromSysFS(const char *path, const char *serial){
	int fd;
	int fd;
	fd = open(path,O_RDONLY);
	fd = open(path,O_RDONLY);
	if (fd<0) return -1;
	if (fd<0) return -1;
	read(fd,serial,strlen(serial));
	read(fd,(char *) serial,strlen(serial));
	close(fd);
	close(fd);
	return 0;
	return 0;
}
}
@@ -336,7 +358,7 @@ int printExifXML(int exif_page, struct file_set *fset)
	//	fset->exif_dev_fd = fd_exif;
	//	fset->exif_dev_fd = fd_exif;
	exif_page_start = lseek(fset->exif_dev_fd, exif_page, SEEK_END); // select specified Exif page
	exif_page_start = lseek(fset->exif_dev_fd, exif_page, SEEK_END); // select specified Exif page
    printf("<debugExifPage>\"%d (0x%x)\"</debugExifPage>\n", exif_page,exif_page);
    printf("<debugExifPage>\"%d (0x%x)\"</debugExifPage>\n", exif_page,exif_page);
    printf("<debugExifPageStart>\"0x%08x\"</debugExifPageStart>\n", exif_page_start);
    printf("<debugExifPageStart>\"0x%08lx\"</debugExifPageStart>\n", exif_page_start);
	if (exif_page_start < 0) {
	if (exif_page_start < 0) {
		printf("<error>\"Exif page (%d) is out of range\"</error>\n", exif_page);
		printf("<error>\"Exif page (%d) is out of range\"</error>\n", exif_page);
	    fprintf(stderr, "%s:%d:%s: Exif page (%d) is out of range\n", __FILE__, __LINE__, __FUNCTION__, exif_page);
	    fprintf(stderr, "%s:%d:%s: Exif page (%d) is out of range\n", __FILE__, __LINE__, __FUNCTION__, exif_page);
@@ -345,7 +367,7 @@ int printExifXML(int exif_page, struct file_set *fset)
		return -1; // Error opening Exif
		return -1; // Error opening Exif
	}
	}
	// Camera Serial Number
	// Camera Serial Number
	readCameraSerialNumberFromSysFS(&path_to_serial,CameraSerialNumber);
	readCameraSerialNumberFromSysFS((const char *) (&path_to_serial),CameraSerialNumber);
	printf("<CameraSerialNumber>\"%.2s:%.2s:%.2s:%.2s:%.2s:%.2s\"</CameraSerialNumber>\n",
	printf("<CameraSerialNumber>\"%.2s:%.2s:%.2s:%.2s:%.2s:%.2s\"</CameraSerialNumber>\n",
			&CameraSerialNumber[0],
			&CameraSerialNumber[0],
			&CameraSerialNumber[2],
			&CameraSerialNumber[2],
@@ -681,7 +703,7 @@ int metaXML(struct file_set *fset, int mode)
			"<signffff> 0x%x </signffff>\n" \
			"<signffff> 0x%x </signffff>\n" \
            "<currentSensorFrame>%d</currentSensorFrame>\n" \
            "<currentSensorFrame>%d</currentSensorFrame>\n" \
            "<latestCompressedFrame>%d</latestCompressedFrame>\n"\
            "<latestCompressedFrame>%d</latestCompressedFrame>\n"\
            "<timestampHex>  %08x %08x</timestampHex>\n" \
            "<timestampHex>  %08lx %08lx</timestampHex>\n" \
            "<currentSensorFrameHex>0x%08x</currentSensorFrameHex>\n" \
            "<currentSensorFrameHex>0x%08x</currentSensorFrameHex>\n" \
            "<latestCompressedFrameHex>0x%08x</latestCompressedFrameHex>\n"
            "<latestCompressedFrameHex>0x%08x</latestCompressedFrameHex>\n"


@@ -740,7 +762,7 @@ unsigned long getCurrentFrameNumberSensor(struct file_set *fset)
        return 0;
        return 0;
    }
    }
	frame_number = lseek(fset->framepars_dev_fd, 0, SEEK_CUR );
	frame_number = lseek(fset->framepars_dev_fd, 0, SEEK_CUR );
    fprintf(stderr, "%s:%d:%s: frame_number= %d\n", __FILE__, __LINE__, __FUNCTION__, frame_number);
    fprintf(stderr, "%s:%d:%s: frame_number= %ld\n", __FILE__, __LINE__, __FUNCTION__, frame_number);
	return frame_number;
	return frame_number;
}
}


@@ -756,7 +778,7 @@ unsigned long getCurrentFrameNumberCompressor(struct file_set *fset)
        return 0;
        return 0;
    }
    }
    frame_number = lseek(fset->circbuf_fd, LSEEK_CIRC_GETFRAME, SEEK_END );
    frame_number = lseek(fset->circbuf_fd, LSEEK_CIRC_GETFRAME, SEEK_END );
    fprintf(stderr, "%s:%d:%s: frame_number= %d\n", __FILE__, __LINE__, __FUNCTION__, frame_number);
    fprintf(stderr, "%s:%d:%s: frame_number= %ld\n", __FILE__, __LINE__, __FUNCTION__, frame_number);
    return frame_number;
    return frame_number;
}
}


@@ -816,8 +838,9 @@ int framePointersXML(struct file_set *fset)
#endif
#endif
//	int fd_circ = fset->circbuf_fd;
//	int fd_circ = fset->circbuf_fd;
//    fprintf(stderr, "%s:%d:%s: Starting\n", __FILE__, __LINE__, __FUNCTION__);
//    fprintf(stderr, "%s:%d:%s: Starting\n", __FILE__, __LINE__, __FUNCTION__);
    if (fset->framepars_dev_fd <0)
    if (fset->framepars_dev_fd <0) {
        fset->framepars_dev_fd = open(fset->framepars_dev_name, O_RDWR);
        fset->framepars_dev_fd = open(fset->framepars_dev_name, O_RDWR);
    }
	if (fset->framepars_dev_fd < 0) { // check control OK
	if (fset->framepars_dev_fd < 0) { // check control OK
		printf("Error opening %s\n", fset->framepars_dev_name);
		printf("Error opening %s\n", fset->framepars_dev_name);
		fprintf(stderr, "%s:%d:%s: Error opening %s\n", __FILE__, __LINE__, __FUNCTION__, fset->framepars_dev_name);
		fprintf(stderr, "%s:%d:%s: Error opening %s\n", __FILE__, __LINE__, __FUNCTION__, fset->framepars_dev_name);
@@ -948,7 +971,7 @@ int TiffStats(struct file_set * fset,
	int buff_size;
	int buff_size;
	int jpeg_len;   // bytes
	int jpeg_len;   // bytes
	int jpeg_start; // bytes
	int jpeg_start; // bytes
	int l, i, j;
	int l, i; // , j;
	int color_mode;
	int color_mode;
	int timestamp_start;
	int timestamp_start;
	int width;
	int width;
@@ -962,7 +985,7 @@ int TiffStats(struct file_set * fset,


	char *out_str; // 1024
	char *out_str; // 1024
	char *out_strp;
	char *out_strp;
	char perc_name[64];
//	char perc_name[64];
	int out_size = 4096; // 15 percentiles - 827, should be sufficient to fit XML
	int out_size = 4096; // 15 percentiles - 827, should be sufficient to fit XML


//	const float percentiles[] = {0.1,1.0,10,50.0,90.0,99.0,99.9};
//	const float percentiles[] = {0.1,1.0,10,50.0,90.0,99.0,99.9};
@@ -1233,9 +1256,9 @@ int frameNumbersXML(struct file_set *fset)
{
{
	// very rarely exceeds 512
	// very rarely exceeds 512
	char s[528]; // 512 // 341;// VERIFY size!)
	char s[528]; // 512 // 341;// VERIFY size!)
	int p; //, wp, rp;
//	int p; //, wp, rp;
	int nf = 0;
//	int nf = 0;
	int nfl = 0;
//	int nfl = 0;
	int frame_number, compressed_frame_number;
	int frame_number, compressed_frame_number;
	int frame16, sensor_state, compressor_state;
	int frame16, sensor_state, compressor_state;
	char *cp_sensor_state, *cp_compressor_state;
	char *cp_sensor_state, *cp_compressor_state;
@@ -1246,8 +1269,9 @@ int frameNumbersXML(struct file_set *fset)


//	int fd_circ = fset->circbuf_fd;
//	int fd_circ = fset->circbuf_fd;
//    fprintf(stderr, "%s:%d:%s: Starting\n", __FILE__, __LINE__, __FUNCTION__);
//    fprintf(stderr, "%s:%d:%s: Starting\n", __FILE__, __LINE__, __FUNCTION__);
    if (fset->framepars_dev_fd <0)
    if (fset->framepars_dev_fd <0) {
        fset->framepars_dev_fd = open(fset->framepars_dev_name, O_RDWR);
        fset->framepars_dev_fd = open(fset->framepars_dev_name, O_RDWR);
    }
	if (fset->framepars_dev_fd < 0) { // check control OK
	if (fset->framepars_dev_fd < 0) { // check control OK
		printf("Error opening %s\n", fset->framepars_dev_name);
		printf("Error opening %s\n", fset->framepars_dev_name);
		fprintf(stderr, "%s:%d:%s: Error opening %s\n", __FILE__, __LINE__, __FUNCTION__, fset->framepars_dev_name);
		fprintf(stderr, "%s:%d:%s: Error opening %s\n", __FILE__, __LINE__, __FUNCTION__, fset->framepars_dev_name);
@@ -1585,9 +1609,10 @@ uint8_t * convertImage (
		int        width,
		int        width,
		int        height,
		int        height,
		int        telem){
		int        telem){
	uint16_t pix16;
///	uint16_t pix16;
	int ipix;
	int ipix;
	uint8_t * pix8;
///	uint8_t * pix8;
	uint8_t pix8;
	int iscale;
	int iscale;
	int start_pix = 0;    // index in src to start copying
	int start_pix = 0;    // index in src to start copying
	int end_pix =   len;  // index in src to end copying
	int end_pix =   len;  // index in src to end copying
@@ -1971,10 +1996,10 @@ int sendImage(struct file_set *fset,
	D(fprintf(stderr, "MIME = '%s', extension = '%s',bufferImageData=%df\n",mime_type, extension,bufferImageData));
	D(fprintf(stderr, "MIME = '%s', extension = '%s',bufferImageData=%df\n",mime_type, extension,bufferImageData));
	printf("Content-Type: image/%s\r\n", mime_type);
	printf("Content-Type: image/%s\r\n", mime_type);
    if (saveImage) {
    if (saveImage) {
        printf("Content-Disposition: attachment; filename=\"%s%ld_%06d_%d.%s\"\r\n",      // does not open, asks for filename to save
        printf("Content-Disposition: attachment; filename=\"%s%ld_%06ld_%d.%s\"\r\n",      // does not open, asks for filename to save
                fset->timestamp_name?"":"elphelimg_", frame_params.timestamp_sec,  frame_params.timestamp_usec, fset->sensor_port + fset->base_chn, extension);
                fset->timestamp_name?"":"elphelimg_", frame_params.timestamp_sec,  frame_params.timestamp_usec, fset->sensor_port + fset->base_chn, extension);
    } else {
    } else {
        printf("Content-Disposition: inline; filename=\"%s%ld_%06d_%d.%s\"\r\n",                    // opens in browser, asks to save on right-click
        printf("Content-Disposition: inline; filename=\"%s%ld_%06ld_%d.%s\"\r\n",                    // opens in browser, asks to save on right-click
                fset->timestamp_name?"":"elphelimg_", frame_params.timestamp_sec,  frame_params.timestamp_usec, fset->sensor_port + fset->base_chn, extension);
                fset->timestamp_name?"":"elphelimg_", frame_params.timestamp_sec,  frame_params.timestamp_usec, fset->sensor_port + fset->base_chn, extension);
    }
    }
    // bufferImageData - always with tiff_convert
    // bufferImageData - always with tiff_convert
@@ -2127,13 +2152,13 @@ void listener_loop(struct file_set *fset)
    int tiff_bin_shift =   3; // histogram bin size is 1<<3 == 8
    int tiff_bin_shift =   3; // histogram bin size is 1<<3 == 8
    int tiff_bin = 1;
    int tiff_bin = 1;
    int tiff_auto = -1;
    int tiff_auto = -1;

    long long ssd_offs_byte, ssd_count_bytes, ssd_sent_bytes; //, ssd_set_byte;
    // imgsrv -p 2323 -s /dev/sda2


//	  int    bin_shift,   // binsize = 1 << bin_shift
//	  int    bin_shift,   // binsize = 1 << bin_shift
	int    tiff_hist_size;   // size of the histogram = ( 1 << size_shift)
	int    tiff_hist_size;   // size of the histogram = ( 1 << size_shift)
	int    tiff_hist_min;   // subtract from value before binning
	int    tiff_hist_min;   // subtract from value before binning



	memset((char*)&sock, 0, sizeof(sock));
	memset((char*)&sock, 0, sizeof(sock));
	sock.sin_port = htons(fset->port_num);
	sock.sin_port = htons(fset->port_num);
	sock.sin_family = AF_INET;
	sock.sin_family = AF_INET;
@@ -2180,6 +2205,81 @@ void listener_loop(struct file_set *fset)
				fflush(stdout);
				fflush(stdout);
				_exit(0);
				_exit(0);
			}
			}
			// Send SSD raw data
			if (strncmp(cp, "ssd", 3) == 0) {
				cp+=3;
//    long long ssd_start_byte, ssd_len_bytes;
//    long long strtoll(const char *nptr, char **endptr, int base);
				ssd_offs_byte = strtoll(cp, &cp1, 10);
				if (strncmp(cp1, ":", 1)){
					fprintf(stderr, "Separator ':' expected between start byte and length, got %s (%s)\n", cp1, cp);
					out1x1gif();
				}
				cp1++;
				ssd_count_bytes = strtoll(cp1, &cp2, 10);
				if ((ssd_offs_byte < 0) || (ssd_count_bytes <= 0)){
					fprintf(stderr, "Non-negative start (byte) and positive length (bytes) are expected, got %lld, %lld (%s)\n", \
							ssd_offs_byte, ssd_count_bytes, cp);
					out1x1gif();
					fflush(stdout);
					_exit(0);
				}
				fprintf(stderr, "SSD name='%s'\n", fset->ssd_name);
				fset->ssd_fd = open64(fset->ssd_name, O_RDONLY);
				if (fset->ssd_fd < 0) { // check control OK
					fprintf(stderr, "Error opening %s\n", fset->ssd_name);
					out1x1gif();
					fflush(stdout);
					_exit(0);
				}
				printf("HTTP/1.0 200 OK\r\n");
				printf("Server: Elphel Imgsrv\r\n");
				printf("Access-Control-Allow-Origin: *\r\n");
				printf("Access-Control-Expose-Headers: Content-Disposition\r\n");
				printf("Content-Length: %lld\r\n",ssd_count_bytes);
				printf("Content-Type: text/plain\r\n");
				printf("\r\n");
				// ssd_sent_bytes
				// 				out1x1gif();
				//STDOUT_FILENO
//ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
				fprintf(stderr, "Sending %lld bytes, offs=%lld\n", ssd_count_bytes, ssd_offs_byte);
#if 0
				ssd_offs_byte=lseek64(fset->ssd_fd, ssd_offs_byte, SEEK_SET);
				fprintf(stderr, "lseek64()-> %lld, STDOUT_FILENO=%d, errno=%d\n", ssd_offs_byte, STDOUT_FILENO, errno);
				while (ssd_count_bytes > 0) {
					ssd_sent_bytes = sendfile64(STDOUT_FILENO, fset->ssd_fd, NULL, ssd_count_bytes);
					if (ssd_sent_bytes <= 0){
						fprintf(stderr, "sendfile64() failed: sent %lld bytes (wanted %lld), offs=%lld, errno=%d\n", \
								ssd_sent_bytes, ssd_count_bytes, ssd_offs_byte, errno);
						break;
					}
					if (ssd_sent_bytes < ssd_count_bytes) {
						fprintf(stderr, "sendfile64() did not send all: sent %lld bytes (wanted %lld), offs=%lld\n", ssd_sent_bytes, ssd_count_bytes, ssd_offs_byte);
					}
					ssd_count_bytes -= ssd_sent_bytes;
				}
#else
				while (ssd_count_bytes > 0) {
					ssd_sent_bytes = sendfile64(STDOUT_FILENO, fset->ssd_fd, &ssd_offs_byte, ssd_count_bytes);
					if (ssd_sent_bytes <= 0){
						fprintf(stderr, "sendfile64() failed: sent %lld bytes (wanted %lld), offs=%lld, errno=%d\n", \
								ssd_sent_bytes, ssd_count_bytes, ssd_offs_byte, errno);
						break;
					}
					if (ssd_sent_bytes < ssd_count_bytes) {
						fprintf(stderr, "sendfile64() did not send all: sent %lld bytes (wanted %lld), offs=%lld\n", ssd_sent_bytes, ssd_count_bytes, ssd_offs_byte);
					}
					ssd_count_bytes -= ssd_sent_bytes;
				}
#endif
				fprintf(stderr, "Sent %lld bytes (wanted %lld), offs=%lld\n", ssd_sent_bytes, ssd_count_bytes, ssd_offs_byte);
				close (fset->ssd_fd);
				fset->ssd_fd = -1;
				fflush(stdout);
				_exit(0);
			}

			// Process 'frame' and 'wframe' commands - they do not need circbuf (now they do!)
			// Process 'frame' and 'wframe' commands - they do not need circbuf (now they do!)
			if ((strncmp(cp, "frame", 5) == 0) || (strncmp(cp, "wframe", 6) == 0) || (strncmp(cp, "sframe", 6) == 0)	) {
			if ((strncmp(cp, "frame", 5) == 0) || (strncmp(cp, "wframe", 6) == 0) || (strncmp(cp, "sframe", 6) == 0)	) {
				if (strncmp(cp, "wframe", 6) == 0) waitFrameSync(fset);
				if (strncmp(cp, "wframe", 6) == 0) waitFrameSync(fset);
@@ -2465,12 +2565,15 @@ void listener_loop(struct file_set *fset)


/**
/**
 * @brief Parse command line options
 * @brief Parse command line options
 * imgsrv -p 2323
 * imgsrv -p 2323 -s /dev/sda2
 * @param[in]       argc   the number of command line arguments; passed from main()
 * @param[in]       argc   the number of command line arguments; passed from main()
 * @param[in]       argv   array of pointers to command line arguments; passed from main()
 * @param[in]       argv   array of pointers to command line arguments; passed from main()
 * @param[in,out]   fset   array of #file_set structures; this function will set port
 * @param[in,out]   fset   array of #file_set structures; this function will set port
 *  numbers in the structures
 *  numbers in the structures
 * @param[in]       fset_sz the number of elements in @e fset
 * @param[in]       fset_sz the number of elements in @e fset
 * @return          0 if command line options were successfully processed and
 * @return          0 if command line options were successfully processed and
 * Add -s /dev/sda for the ssd partition to open
 * -1 in case of an error
 * -1 in case of an error
 */
 */
int parse_cmd_line(int argc, const char *argv[], struct file_set *fset, int fset_sz)
int parse_cmd_line(int argc, const char *argv[], struct file_set *fset, int fset_sz)
@@ -2482,7 +2585,8 @@ int parse_cmd_line(int argc, const char *argv[], struct file_set *fset, int fset
		printf(app_args, argv[0]);
		printf(app_args, argv[0]);
		printf(url_args);
		printf(url_args);
		return -1;
		return -1;
	} else if (argc == 3) {
//	} else if (argc == 3) { // Old version pre 2023-08-01
	} else if (argc >= 3) {
		port = strtol(argv[2], NULL, 10);
		port = strtol(argv[2], NULL, 10);
		if (!port) {
		if (!port) {
			printf("Invalid port number %d\n", port);
			printf("Invalid port number %d\n", port);
@@ -2490,8 +2594,12 @@ int parse_cmd_line(int argc, const char *argv[], struct file_set *fset, int fset
		}
		}
		for (int i = 0; i < fset_sz; i++) {
		for (int i = 0; i < fset_sz; i++) {
			fset[i].port_num = port + i;
			fset[i].port_num = port + i;
			fset[i].ssd_name = NULL;
			fset[i].ssd_fd =   -1;
		}
		}
	} else if (argc > 3) {
		//	} else if (argc > 3) {
		if (argc > 3) {
			/* Old version pre 2023-08-01
		i = 0;
		i = 0;
		// start parsing from first port number which is second positional parameter in cmd line
		// start parsing from first port number which is second positional parameter in cmd line
		while ((i < fset_sz) && (i + 2 < argc)) {
		while ((i < fset_sz) && (i + 2 < argc)) {
@@ -2506,6 +2614,23 @@ int parse_cmd_line(int argc, const char *argv[], struct file_set *fset, int fset
		if (i < fset_sz - 1) {
		if (i < fset_sz - 1) {
			printf("Wrong ports quantity\n");
			printf("Wrong ports quantity\n");
			return -1;
			return -1;
		}
			 */
			if (argc != 5) {
				printf("Wrong number of arguments: %d\n", argc);
				return -1;
			}
			if  (strcasecmp(argv[3], "-s")){ // ssd partition - whole disk?
				printf("Expecting '-s' for argv[3], got '%s'\n", argv[3]);
				return -1;
			}
			strcpy(ssd_name, argv[4]);
//			printf("Got ssd name= '%s'\n", argv[4]);
//			printf("Got ssd name= '%s'\n", ssd_name);
			//ssd_name
			for (int i = 0; i < fset_sz; i++) {
				fset[i].ssd_name = ssd_name;
			}
		}
		}
	}
	}