Commit a54d1b82 authored by Andrey Filippov's avatar Andrey Filippov

Some bugs fixed, better buffer overrun handling

parent 6cffcbe2
......@@ -387,6 +387,7 @@ int camogm_start(camogm_state *state)
D3(fprintf(debug_file, "1a: compressed frame number = %li\n", lseek(state->fd_circ[chn], LSEEK_CIRC_GETFRAME, SEEK_END)));
if ((state->cirbuf_rp[chn] < 0) || (lseek(state->fd_circ[chn], state->cirbuf_rp[chn], SEEK_SET) < 0) || (lseek(state->fd_circ[chn], LSEEK_CIRC_VALID, SEEK_END) < 0 )) {
D3(fprintf(debug_file, "2: state->cirbuf_rp=0x%x\n", state->cirbuf_rp[chn]));
D3(fprintf(debug_file, "2.1: state->greedy=%d\n", state->greedy));
/* In "greedy" mode try to save as many frames from the circbuf as possible */
state->cirbuf_rp[chn] = lseek(state->fd_circ[chn], state->greedy ? LSEEK_CIRC_SCND : LSEEK_CIRC_LAST, SEEK_END);
if (!state->ignore_fps) { // don't even try in ignore mode
......@@ -502,11 +503,12 @@ int camogm_start(camogm_state *state)
}
// and read it
lseek(state->fd_head[chn], 0, 0);
read(state->fd_head[chn], state->jpegHeader[chn], state->head_size[chn]);
// Restore read pointer to the original (now there may be no frame ready there yet)
lseek(state->fd_circ[chn], state->cirbuf_rp[chn], SEEK_SET);
}
}
read(state->fd_head[chn], state->jpegHeader[chn], state->head_size[chn]);
// Restore read pointer to the original (now there may be no frame ready there yet)
lseek(state->fd_circ[chn], state->cirbuf_rp[chn], SEEK_SET);
state->image_size[chn] = 0;
}
}
// here we are ready to initialize Ogm (or other) file
switch (state->format) {
......@@ -544,11 +546,14 @@ int sendImageFrame(camogm_state *state)
int timestamp_start;
int * ifp_this = (int*)&(state->this_frame_params[state->port_num]);
int fp;
int fd_exif_tiff; // switching between TIFF and Exif depending on coloe mode
int port = state->port_num;
struct timeval start_time, end_time;
// start_time = get_fpga_time(state->fd_fparmsall[port], port);
int fd_exif_tiff; // switching between TIFF and Exif depending on coloe mode
int port = state->port_num;
struct timeval start_time, end_time;
if (port < 0) {
D3(fprintf(debug_file, "sendImageFrame:0: port < 0!\n"));
return -CAMOGM_FRAME_BROKEN;
}
// start_time = get_fpga_time(state->fd_fparmsall[port], port);
// This is probably needed only for Quicktime (not to exceed already allocated frame index)
if (!state->rawdev_op && (state->frameno >= (state->max_frames))) {
......@@ -579,7 +584,8 @@ int sendImageFrame(camogm_state *state)
}
// is the frame ready?
if (lseek(state->fd_circ[port], LSEEK_CIRC_READY, SEEK_END) < 0) {
D3(fprintf(debug_file, "?6,fp=0x%x\n", fp)); //frame not ready, frame pointer seems valid, but not ready
D4(fprintf(debug_file, "?6")) ; //frame not ready, frame pointer seems valid, but not ready
D6(fprintf(debug_file, ",fp=0x%x\n", fp)); //frame not ready, frame pointer seems valid, but not ready
return -CAMOGM_FRAME_NOT_READY; // frame pointer valid, but no frames yet
}
......@@ -594,7 +600,7 @@ int sendImageFrame(camogm_state *state)
}
// Get metadata
D3(fprintf(debug_file, "_1_"));
D4(fprintf(debug_file, "_1_"));
state->metadata_start = state->cirbuf_rp[port] - 32;
if (state->metadata_start < 0) state->metadata_start += state->circ_buff_size[port];
memcpy(&(state->this_frame_params[port]), (unsigned long* )&ccam_dma_buf[state->port_num][state->metadata_start >> 2], 32);
......@@ -607,11 +613,11 @@ int sendImageFrame(camogm_state *state)
D3(fprintf(debug_file, "sendImageFrame:8: frame broken\n"));
return -CAMOGM_FRAME_BROKEN;
}
D3(fprintf(debug_file, "_2_"));
D4(fprintf(debug_file, "_2_"));
// find location of the timestamp and copy it to the frame_params structure
timestamp_start = state->cirbuf_rp[port] + ((state->jpeg_len + CCAM_MMAP_META + 3) & (~0x1f)) + 32 - CCAM_MMAP_META_SEC; // magic shift - should index first byte of the time stamp
if (timestamp_start >= state->circ_buff_size[port]) timestamp_start -= state->circ_buff_size[port];
D3(fprintf(debug_file, "_3_"));
D4(fprintf(debug_file, "_3_"));
memcpy(&(state->this_frame_params[port].timestamp_sec), (unsigned long* )&ccam_dma_buf[state->port_num][timestamp_start >> 2], 8);
// verify that the essential current frame params did not change, if they did - return an error (need new file header)
if (!state->ignore_fps && ((state->frame_params[port].width != state->this_frame_params[port].width) ||
......@@ -635,9 +641,9 @@ int sendImageFrame(camogm_state *state)
}
D3(fprintf(debug_file, "_4_"));
D4(fprintf(debug_file, "_4_"));
if (state->exif) {
D3(fprintf(debug_file, "_5_"));
D4(fprintf(debug_file, "_5_"));
fd_exif_tiff = (state->this_frame_params[port].color == COLORMODE_RAW)? state->fd_tiff[port] : state->fd_exif[port];
// update the Exif header with the current frame metadata
state->exifSize[port] = lseek(fd_exif_tiff, 1, SEEK_END); // at the beginning of page 1 - position == page length
......@@ -650,21 +656,21 @@ int sendImageFrame(camogm_state *state)
} else state->exifSize[port] = 0;
} else state->exifSize[port] = 0;
D3(fprintf(debug_file, "_6_"));
D4(fprintf(debug_file, "_6_"));
// prepare a packet to be sent (a lst of memory chunks)
state->chunk_index = 0;
state->packetchunks[state->chunk_index ].bytes = 1;
state->packetchunks[state->chunk_index++].chunk = &frame_packet_type;
if (state->exif > 0) { // insert Exif/Tiff
D3(fprintf(debug_file, "_7_"));
D4(fprintf(debug_file, "_7_"));
if (state->this_frame_params[port].color == COLORMODE_RAW) { // Tiff
D3(fprintf(debug_file, "_8a_"));
D4(fprintf(debug_file, "_8a_"));
state->packetchunks[state->chunk_index ].bytes = state->exifSize[port]; // Tiff Header length
state->packetchunks[state->chunk_index++].chunk = state->ed[port]; // Tiff Header
} else { // JPEG/JP4
D3(fprintf(debug_file, "_8b_"));
D4(fprintf(debug_file, "_8b_"));
state->packetchunks[state->chunk_index ].bytes = 2;
state->packetchunks[state->chunk_index++].chunk = state->jpegHeader[port];
state->packetchunks[state->chunk_index ].bytes = state->exifSize[port];
......@@ -673,16 +679,16 @@ int sendImageFrame(camogm_state *state)
state->packetchunks[state->chunk_index++].chunk = &(state->jpegHeader[port][2]);
}
} else {
D3(fprintf(debug_file, "_8_"));
D4(fprintf(debug_file, "_8_"));
state->packetchunks[state->chunk_index ].bytes = state->head_size[port];
state->packetchunks[state->chunk_index++].chunk = state->jpegHeader[port];
}
D3(fprintf(debug_file, "_9_"));
D4(fprintf(debug_file, "_9_"));
/* Jpeg/Tiff image data may be split in two segments (rolled over buffer end) - process both variants */
if ((state->cirbuf_rp[port] + state->jpeg_len) > state->circ_buff_size[port]) { // two segments
/* copy from the beginning of the frame to the end of the buffer */
D3(fprintf(debug_file, "_10_"));
D4(fprintf(debug_file, "_10_"));
state->packetchunks[state->chunk_index ].bytes = state->circ_buff_size[port] - state->cirbuf_rp[port];
state->packetchunks[state->chunk_index++].chunk = (unsigned char*)&ccam_dma_buf[state->port_num][state->cirbuf_rp[port] >> 2];
/* copy from the beginning of the buffer to the end of the frame */
......@@ -690,15 +696,15 @@ int sendImageFrame(camogm_state *state)
state->packetchunks[state->chunk_index++].chunk = (unsigned char*)&ccam_dma_buf[state->port_num][0];
state->writer_params.segments = 2;
} else { // single segment
D3(fprintf(debug_file, "_11_"));
D4(fprintf(debug_file, "_11_"));
/* copy from the beginning of the frame to the end of the frame (no buffer rollovers) */
state->packetchunks[state->chunk_index ].bytes = state->jpeg_len;
state->packetchunks[state->chunk_index++].chunk = (unsigned char*)&ccam_dma_buf[state->port_num][state->cirbuf_rp[port] >> 2];
state->writer_params.segments = 1;
}
D3(fprintf(debug_file, "\tcirbuf_rp[%d] = 0x%x\t", port, state->cirbuf_rp[port]));
D3(fprintf(debug_file, "_12_\n"));
D4(fprintf(debug_file, "\tcirbuf_rp[%d] = 0x%x\t", port, state->cirbuf_rp[port]));
D5(fprintf(debug_file, "_12_\n"));
if (state->this_frame_params[port].color != COLORMODE_RAW) { // Tiff
state->packetchunks[state->chunk_index ].bytes = 2;
state->packetchunks[state->chunk_index++].chunk = (unsigned char*)trailer;
......@@ -717,20 +723,25 @@ int sendImageFrame(camogm_state *state)
default: rslt = 0; // do nothing
}
if (rslt) {
D3(fprintf(debug_file, "sendImageFrame:12: camogm_frame_***() returned %d\n", rslt));
D4(fprintf(debug_file, "sendImageFrame:12: camogm_frame_***() returned %d\n", rslt));
return rslt;
}
if (state->kml_used) rslt = camogm_frame_kml(state); // will turn on state->kml_used if it can
if (rslt) return rslt;
D3(fprintf(debug_file, "_14_"));
D4(fprintf(debug_file, "_14_"));
// advance frame pointer
state->frameno++;
state->cirbuf_rp[port] = lseek(state->fd_circ[port], LSEEK_CIRC_NEXT, SEEK_END);
D3(fprintf(debug_file, "\tcompressed frame number: %li\t", lseek(state->fd_circ[port], LSEEK_CIRC_GETFRAME, SEEK_END)));
if (state->cirbuf_rp[port] < 0) {
D2(fprintf(debug_file, "_14a_: sendImageFrame(): frame broken - buffer overrun\n"));
return -CAMOGM_FRAME_BROKEN;
}
D3(fprintf(debug_file, "\tcompressed frame number: %li\n", lseek(state->fd_circ[port], LSEEK_CIRC_GETFRAME, SEEK_END)));
// optionally save it to global read pointer (i.e. for debugging with imgsrv "/pointers")
if (state->save_gp) lseek(state->fd_circ[port], LSEEK_CIRC_SETP, SEEK_END);
D3(fprintf(debug_file, "_15_\n"));
D4(fprintf(debug_file, "_15_\n"));
if (state->frames_skip > 0) {
state->frames_skip_left[port] = state->frames_skip;
} else if (state->frames_skip < 0) {
......@@ -749,6 +760,7 @@ int sendImageFrame(camogm_state *state)
// state->rawdev.last_jpeg_size));
// D6(fprintf(debug_file, "Write speed: %d MB/s\n", mbps));
D6(fprintf(debug_file, "Exit sendImageFrame() OK @ %06d\n",get_fpga_usec(state->fd_fparmsall[0], 0)));
return 0;
}
......@@ -1386,21 +1398,23 @@ int parse_cmd(camogm_state *state, FILE* npipe)
{
char * cmd;
char * args;
char * argse;
int d;
double dd;
char * argse;
int d;
double dd;
int port = state->port_num;
//skip empty commands
while (((cmd = getLineFromPipe(npipe))) && !cmd[0]) ;
if (!cmd) return 0; // nothing in the pipe
D2(fprintf(debug_file, "Got command: '%s' @ %06d -> ", cmd, get_fpga_usec(state->fd_fparmsall[0], 0)));
while (((cmd = getLineFromPipe(npipe))) && !cmd[0]) ;
if (!cmd) return 0; // nothing in the pipe
D2(fprintf(debug_file, "Got command (state->port_num=%d): '%s' @ %06d -> ", port, cmd, get_fpga_usec(state->fd_fparmsall[0], 0)));
if (port >= 0) {
D3(fprintf(debug_file, "Acknowledge received command for port=%d\n", port));
// Acknowledge received command by copying frame number to per-daemon parameter
// GLOBALPARS(state->port_num, G_DAEMON_ERR + lastDaemonBit[state->port_num]) = GLOBALPARS(state->port_num, G_THIS_FRAME);
setGValue(state->port_num, G_DAEMON_ERR + lastDaemonBit[state->port_num], getGPValue(state->port_num, G_THIS_FRAME));
args = strpbrk(cmd, "= \t");
// GLOBALPARS(state->port_num, G_DAEMON_ERR + lastDaemonBit[state->port_num]) = GLOBALPARS(state->port_num, G_THIS_FRAME);
setGValue(port, G_DAEMON_ERR + lastDaemonBit[port], getGPValue(port, G_THIS_FRAME));
}
args = strpbrk(cmd, "= \t");
// is it just a single word command or does it have parameters?
if (args) {
if (args) {
args[0] = 0;
args++;
while (strchr("= \t", args[0])) args++;
......@@ -1687,7 +1701,11 @@ int listener_loop(camogm_state *state)
}
if (state->prog_state == STATE_RUNNING) { // no commands in queue, started
D6(fprintf(debug_file, "state->prog_state == STATE_RUNNING "));
rslt = -sendImageFrame(state);
if (curr_port < 0) {
rslt = CAMOGM_FRAME_BROKEN;
} else {
rslt = -sendImageFrame(state);
}
D6(fprintf(debug_file, " ==> %d @ %06d\n",rslt,get_fpga_usec(state->fd_fparmsall[0], 0)));
switch (rslt) {
case 0:
......@@ -1995,32 +2013,40 @@ unsigned int select_port(camogm_state *state)
}
if (state->prog_state == STATE_STARTING || state->prog_state == STATE_RUNNING)
D6(fprintf(debug_file, "Selecting sensor port, buffer free size: "));
D3(fprintf(debug_file, "Selecting sensor port, buffer free size: "));
for (int i = 0; i < SENSOR_PORTS; i++) {
if (is_chn_active(state, i)) {
file_pos = lseek(state->fd_circ[i], 0, SEEK_CUR);
if (file_pos != -1) {
free_sz = lseek(state->fd_circ[i], LSEEK_CIRC_FREE, SEEK_END);
lseek(state->fd_circ[i], file_pos, SEEK_SET);
if (state->prog_state == STATE_STARTING || state->prog_state == STATE_RUNNING)
D6(fprintf(debug_file, "port %i = %li, ", i, free_sz));
if ((free_sz < min_sz && free_sz >= 0) || min_sz == -1) {
min_sz = free_sz;
chn = i;
lseek(state->fd_circ[i], file_pos, SEEK_SET);
if (state->prog_state == STATE_STARTING || state->prog_state == STATE_RUNNING)
D3(fprintf(debug_file, "port %i = %li, ", i, free_sz));
if (free_sz < CAMOGM_MIN_BUF_FRAMES * state->image_size[i]) {
D3(fprintf(debug_file, "port %i has too little free space (<CAMOGM_MIN_BUF_FRAMES * %d), considered BROKEN", i, state->image_size[i]));
chn = -1; // i;
break;
}
if ((free_sz < min_sz && free_sz >= 0) || min_sz == -1) {
min_sz = free_sz;
chn = i;
}
} else {
if (state->prog_state == STATE_STARTING || state->prog_state == STATE_RUNNING)
D3(fprintf(debug_file, "port %i BROKEN FRAME, ", i));
// current frame pointer is possibly overwritten (buffer overflow), select
// this channel which will force sendImageFrame() to take recovery actions
chn = i;
// Did not work - mMaybe use -1? Make susre at least one extra frame is OK?
chn = -1; // i;
break;
}
} else {
if (state->prog_state == STATE_STARTING || state->prog_state == STATE_RUNNING)
D6(fprintf(debug_file, "port %i is inactive, ", i));
D3(fprintf(debug_file, "port %i is inactive, ", i));
}
}
if (state->prog_state == STATE_STARTING || state->prog_state == STATE_RUNNING)
D6(fprintf(debug_file, "selected port: %i\n", chn));
D3(fprintf(debug_file, "selected port: %i @ %06d\n", chn, get_fpga_usec(state->fd_fparmsall[0], 0)));
return chn;
}
......
......@@ -43,6 +43,7 @@
#define CAMOGM_FORMAT_OGM 1 ///< output as Ogg Media file
#define CAMOGM_FORMAT_JPEG 2 ///< output as individual JPEG files
#define CAMOGM_FORMAT_MOV 3 ///< output as Apple Quicktime
#define CAMOGM_MIN_BUF_FRAMES 2 ///< buffer should accomodate at list this number of frames
#define D(x) { if (debug_file && debug_level) { x; fflush(debug_file); } }
#define D0(x) { if (debug_file) { pthread_mutex_lock(&print_mutex); x; fflush(debug_file); pthread_mutex_unlock(&print_mutex); } }
......@@ -191,12 +192,13 @@ typedef struct {
int fd_circ[SENSOR_PORTS]; ///< file descriptor for circbuf
int fd_head[SENSOR_PORTS]; ///< file descriptor for JPEG header
int fd_fparmsall[SENSOR_PORTS]; ///< file descriptor for sensor/compressor parameters
int fd_exif[SENSOR_PORTS]; ///< file descriptor for Exif data
int fd_tiff[SENSOR_PORTS]; ///< file descriptor for Tiff data
int head_size[SENSOR_PORTS]; ///< JPEG header size
unsigned char jpegHeader[SENSOR_PORTS][JPEG_HEADER_MAXSIZE];
int metadata_start;
struct interframe_params_t frame_params[SENSOR_PORTS];
int fd_exif[SENSOR_PORTS]; ///< file descriptor for Exif data
int fd_tiff[SENSOR_PORTS]; ///< file descriptor for Tiff data
int head_size[SENSOR_PORTS]; ///< JPEG header size
int image_size[SENSOR_PORTS]; ///< last image size in bytes
unsigned char jpegHeader[SENSOR_PORTS][JPEG_HEADER_MAXSIZE];
int metadata_start;
struct interframe_params_t frame_params[SENSOR_PORTS];
struct interframe_params_t this_frame_params[SENSOR_PORTS];
int jpeg_len;
int frame_period[SENSOR_PORTS]; ///< in microseconds (1/10 of what is needed for the Ogm header)
......
......@@ -366,11 +366,13 @@ void align_frame(camogm_state *state)
remap_vectors(state, chunks); // now multi-page
// total_sz = get_size_from(chunks, 0, 0, INCLUDE_REM) + rbuff->iov_len;
total_sz = get_size_from_paged(state, 0, INCLUDE_REM) + rbuff->iov_len;
D4(fprintf(debug_file, "total_sz = %d, rbuff->iov_len=%d\n", total_sz, (int)(rbuff->iov_len)));
if (total_sz < PHY_BLOCK_SIZE) {
/* the frame length is less than sector size, delay this frame */
// total_sz = get_size_from(chunks, 0, 0, INCLUDE_REM) + rbuff->iov_len;
total_sz = get_size_from_paged(state, 0, INCLUDE_REM) + rbuff->iov_len;
//port_num
state->image_size[state->port_num] = total_sz;
D4(fprintf(debug_file, "total_sz = %d, rbuff->iov_len=%d\n", total_sz, (int)(rbuff->iov_len)));
if (total_sz < PHY_BLOCK_SIZE) {
/* the frame length is less than sector size, delay this frame */
if (rbuff->iov_len != 0) {
/* some data may be left from previous frame */
// vectcpy(&chunks[CHUNK_REM], rbuff->iov_base, rbuff->iov_len);
......
......@@ -316,12 +316,13 @@ int camogm_frame_jpeg(camogm_state *state)
close(state->ivf);
return -CAMOGM_FRAME_FILE_ERR;
}
state->rawdev.last_jpeg_size = l;
close(state->ivf);
} else {
D6(fprintf(debug_file, "\ndump iovect array for port %u\n", state->port_num));
for (int i = 0; i < state->chunk_index - 1; i++) {
D6(fprintf(debug_file, "ptr: %p, length: %ld\n", state->packetchunks[i + 1].chunk, state->packetchunks[i + 1].bytes));
state->rawdev.last_jpeg_size = l;
close(state->ivf);
} else {
D3(fprintf(debug_file, "_12.7_ camogm_frame_jpeg(): %ld.%06ld:%d @ %06d\n",state->this_frame_params[port].timestamp_sec, state->this_frame_params[port].timestamp_usec, port, get_fpga_usec(state->fd_fparmsall[0], 0)));
D6(fprintf(debug_file, "\ndump iovect array for port %u\n", state->port_num));
for (int i = 0; i < state->chunk_index - 1; i++) {
D6(fprintf(debug_file, "ptr: %p, length: %ld\n", state->packetchunks[i + 1].chunk, state->packetchunks[i + 1].bytes));
}
// next frame is ready for recording, signal this to the writer thread
D6(fprintf(debug_file, "_13__: Yielding thread before getting a lock (%d, %d):%d @ %06d\n", state->writer_params.chunk_page_prep, state->writer_params.chunk_page_write, state->writer_params.writev_run, get_fpga_usec(state->fd_fparmsall[0], 0)));
......@@ -492,19 +493,20 @@ void *jpeg_writer(void *thread_args)
l += chunks_iovec[wpage][i].iov_len;
// Moved rollover here
blocks_write = l / PHY_BLOCK_SIZE;
if (state->writer_params.lba_current + blocks_write <= state->writer_params.lba_end) { // almost always - single write
state->writer_params.lba_current += blocks_write;
// D6(fprintf(debug_file, "_w02_: starting writev @ %06d\n",get_fpga_usec(state->fd_fparmsall[0], 0)));
D6(dbg_us1=get_fpga_usec(state->fd_fparmsall[0], 0); fprintf(debug_file, "_w02_: starting writev @ %06d\n",dbg_us1));
state->writer_params.writev_run = true;
pthread_cond_signal(&params->main_cond);
iovlen = writev(state->writer_params.blockdev_fd, chunks_iovec[wpage], chunk_index);
state->writer_params.writev_run = false;
// D6(fprintf(debug_file, "_w03_: finished writev @ %06d\n",get_fpga_usec(state->fd_fparmsall[0], 0)));
D6(dbg_us=get_fpga_usec(state->fd_fparmsall[0], 0); fprintf(debug_file, "_w03_: finished writev @ %06d (+ %06d, +%06d)\n",dbg_us, (dbg_us-dbg_us1), (dbg_us-dbg_us2)); dbg_us2=dbg_us);
if (iovlen < l) {
D0(fprintf(debug_file, "writev error: %s (returned %i, expected %i)\n", strerror(errno), iovlen, l));
state->writer_params.last_ret_val = -CAMOGM_FRAME_FILE_ERR;
if (state->writer_params.lba_current + blocks_write <= state->writer_params.lba_end) { // almost always - single write
state->writer_params.lba_current += blocks_write;
// D6(fprintf(debug_file, "_w02_: starting writev @ %06d\n",get_fpga_usec(state->fd_fparmsall[0], 0)));
D3(dbg_us1=get_fpga_usec(state->fd_fparmsall[0], 0));
D6(fprintf(debug_file, "_w02_: starting writev @ %06d\n",dbg_us1));
state->writer_params.writev_run = true;
pthread_cond_signal(&params->main_cond);
iovlen = writev(state->writer_params.blockdev_fd, chunks_iovec[wpage], chunk_index);
state->writer_params.writev_run = false;
// D6(fprintf(debug_file, "_w03_: finished writev @ %06d\n",get_fpga_usec(state->fd_fparmsall[0], 0)));
D3(dbg_us=get_fpga_usec(state->fd_fparmsall[0], 0); fprintf(debug_file, "_w03_: finished writev @ %06d (+ %06d, +%06d)\n",dbg_us, (dbg_us-dbg_us1), (dbg_us-dbg_us2)); dbg_us2=dbg_us);
if (iovlen < l) {
D0(fprintf(debug_file, "writev error: %s (returned %i, expected %i)\n", strerror(errno), iovlen, l));
state->writer_params.last_ret_val = -CAMOGM_FRAME_FILE_ERR;
reset_rem = 1;
lseek(state->writer_params.blockdev_fd,
(state->writer_params.lba_current - state->writer_params.lba_start) * PHY_BLOCK_SIZE,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment