| /* Copyright 2016 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| /* Session procedure |
| * |
| * This is TCP session main procedure. |
| */ |
| |
| #include "chameleon_driver.h" |
| #include "log.h" |
| #include "packet_format.h" |
| #include "session.h" |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <netinet/in.h> |
| #include <poll.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <strings.h> |
| #include <sys/mman.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| /* Used to measure the duration of copying shared memory */ |
| #ifdef MEASURE_DUMP_DURATION |
| #include <sys/time.h> |
| #endif |
| |
| /* |
| * Use define here because of array declaration. |
| */ |
| #define MAX_SOCKETBUFFER_SIZE 2048 |
| #define MAX_VIDEO_DUMP_CHANNEL 2 |
| |
| /* |
| * Collect all sessions' log under sessions directory. |
| */ |
| static const char *kSessionLogfilePattern_ = "session_%d.log"; |
| |
| static const int kRetOK_ = 0; |
| static const int kRetFail_ = -1; |
| static const int kHW_CountWrap_ = 0x10000; |
| static const int kBytePerPixel_ = 3; |
| static const int kAudioPageSize_ = 4096; |
| |
| static const char *kErrorMessageMMap = "Memory map fail"; |
| static const char *kErrorMessageMemoryAlloc = "Memory allocate fail"; |
| static const char *kErrorMessageRealtimeMode = "Realtime mode is wrong"; |
| static const char *kErrorMessageRealtimeStream = |
| "There is an existing realtime stream"; |
| static const char *kErrorMessageRealtimeNonSame = |
| "Width or height or limit is not the same"; |
| static const char *kErrorMessageFrameNumberZero = "Frame number is 0"; |
| static const char *kErrorMessage2ndChannelNotRun = "2nd channel is not running"; |
| static const char *kErrorMessageNotRun = "Capture HW is not running"; |
| static const char *kErrorMessageDumpMemoryNotEnough = |
| "Dump memory is not enough"; |
| static const char *kErrorMessageDropVideoFrame = "Drop realtime video frame %d"; |
| static const char *kErrorMessageDropAudioPage = "Drop realtime audio page %d"; |
| static const char *kErrorMessageMemoryOverflow = |
| "Stop dump realtime audio/video due to memory overflow"; |
| |
| |
| /* |
| * We only support one realtime dump per session. |
| * So some of the members will be shared between audio and video. |
| */ |
| typedef struct { |
| /* |
| * The socket descriptor of this session. |
| */ |
| int socket; |
| |
| /* |
| * The buffer used to receive from or send to socket. |
| */ |
| char socketbuffer[MAX_SOCKETBUFFER_SIZE]; |
| |
| LogHandle log; |
| int dev_mem_fd; |
| |
| /* |
| * The processing message type. |
| */ |
| enum MessageType message_type; |
| |
| /* |
| * Temporary buffer for audio/video dumping. |
| * We will copy data from shared memory to this buffer first. |
| * If we deal with the shared memory directly, the performance will be very |
| * bad. |
| */ |
| char *p_dump_buffer; |
| |
| /* |
| * The indicator to stop dumping realtime video/audio stream. |
| */ |
| uint8_t stop_dump; |
| |
| /* To indicate the current realtime stream is audio or video */ |
| uint8_t is_dump_audio; |
| |
| /* |
| * Store the width and height pixels of the no-realtime video dump. |
| */ |
| uint16_t screen_width; |
| uint16_t screen_height; |
| |
| /* |
| * To indicate if we need to shrink the video frame during the dumping. |
| * If we don't need to shrink the video frame, we can just copy the data from |
| * shared memory. |
| */ |
| uint8_t is_shrink; |
| uint8_t shrink_width; |
| uint8_t shrink_height; |
| |
| /* |
| * For realtime video dump. |
| * We have 2 video dump controllers. And we may have data from any of them. |
| * We will auto detect the dump controller by the Run bit and store the info |
| * in realtime_check_channel. |
| */ |
| uint8_t realtime_check_channel; |
| |
| /* |
| * The max video frames/audio pages in the dump area. |
| * The dump controller will reset dump pointer to Dump Start Address after |
| * dump_limit is reached. |
| * For video dump, we read from the Dump Limit register of video dump |
| * controller. |
| * For audio dump, we calculate it from Dump Start Address and |
| * Dump End Address of audio Dump Controller. |
| */ |
| uint32_t dump_limit; |
| |
| /* |
| * Store the dump start addresses of audio/video. |
| * For audio dump, only the first element is used. |
| */ |
| uint32_t dump_addresses[MAX_VIDEO_DUMP_CHANNEL]; |
| |
| /* |
| * Paged size aligned of audio page size or video frame size. |
| * We can use it to calculate each audio/video start address. |
| */ |
| int unit_aligned_size; |
| |
| /* |
| * Store the size of mmap. So we can do munmap later. |
| */ |
| int mmap_size; |
| |
| /* |
| * The pointer of the shared memory by mmap. |
| */ |
| char *p_mmap_sources[MAX_VIDEO_DUMP_CHANNEL]; |
| |
| RealtimeMode realtime_mode; |
| } Session; |
| |
| typedef int (*MessageHandler)(Session *); |
| |
| static int _ReadFromSocket(Session *p_session, int size); |
| static int _SendToSocket(Session *p_session, char *p_buffer, int size); |
| static int _SendWholePacketToSocket(Session *p_session); |
| static int _ProcessReset(Session *p_session); |
| static int _ProcessGetVersion(Session *p_session); |
| static int _ProcessConfigVideoStream(Session *p_session); |
| static int _ProcessConfigShrinkVideoStream(Session *p_session); |
| static int _ProcessDumpVideoFrame(Session *p_session); |
| static int _ProcessDumpRealtimeVideoFrame(Session *p_session); |
| static int _ProcessStopDump(Session *p_session); |
| static int _ProcessDumpRealtimeAudioPage(Session *p_session); |
| static int _ProcessMessage(Session *p_session); |
| |
| /* |
| * Message handler table for each message type. |
| * The position of the handler must be the same as the message type. |
| */ |
| static const MessageHandler _g_handlers[] = { |
| _ProcessReset, |
| _ProcessGetVersion, |
| _ProcessConfigVideoStream, |
| _ProcessConfigShrinkVideoStream, |
| _ProcessDumpVideoFrame, |
| _ProcessDumpRealtimeVideoFrame, |
| _ProcessStopDump, |
| _ProcessDumpRealtimeAudioPage, |
| _ProcessStopDump, |
| }; |
| |
| static inline PacketHead *_get_packet_head(Session *p_session) |
| { |
| return (PacketHead *)p_session->socketbuffer; |
| } |
| |
| static unsigned _calculate_page_aligned_size(int size) |
| { |
| int pagesize = getpagesize(); |
| |
| if (size % pagesize) { |
| size += pagesize; |
| size -= size % pagesize; |
| } |
| |
| return size; |
| } |
| |
| /** |
| * @brief _CleanDumpVariable |
| * Clean the variables of the dump process. |
| * |
| * @param p_session Session instance. |
| */ |
| static void _CleanDumpVariable(Session *p_session) |
| { |
| int i; |
| |
| if (p_session->p_dump_buffer) { |
| free(p_session->p_dump_buffer); |
| p_session->p_dump_buffer = NULL; |
| } |
| |
| for (i = 0; i < MAX_VIDEO_DUMP_CHANNEL; i++) { |
| if (!p_session->dump_addresses[i]) { |
| continue; |
| } |
| p_session->dump_addresses[i] = 0; |
| if (p_session->p_mmap_sources[i]) { |
| munmap(p_session->p_mmap_sources[i], p_session->mmap_size); |
| p_session->p_mmap_sources[i] = NULL; |
| } |
| } |
| p_session->mmap_size = 0; |
| p_session->realtime_mode = kNonRealtime; |
| p_session->is_dump_audio = 0; |
| } |
| |
| /** |
| * @brief _CleanSession |
| * Clean all the resources of the session. |
| * |
| * @param p_session Session instance |
| */ |
| static void _CleanSession(Session *p_session) |
| { |
| LogPrint(&p_session->log, kInfo, "Cleaning Session..."); |
| |
| _CleanDumpVariable(p_session); |
| if (p_session->dev_mem_fd && p_session->dev_mem_fd != -1) { |
| close(p_session->dev_mem_fd); |
| p_session->dev_mem_fd = 0; |
| } |
| |
| LogPrint(&p_session->log, kInfo, "Cleaned session."); |
| LogDestroy(&p_session->log); |
| } |
| |
| /** |
| * @brief _ReadFromSocket |
| * Read size of data from socket. The data will be stored in the socketbuffer. |
| * |
| * @param p_session Session instance. |
| * @param size The size of the read data. |
| * |
| * @return kRetFail_, kRetOK_ |
| */ |
| static int _ReadFromSocket(Session *p_session, int size) |
| { |
| int read_bytes; |
| |
| if (size > MAX_SOCKETBUFFER_SIZE) { |
| LogPrint(&p_session->log, kWarn, "Reading size %d > buffer size %d", size, |
| MAX_SOCKETBUFFER_SIZE); |
| return kRetFail_; |
| } |
| |
| read_bytes = read(p_session->socket, p_session->socketbuffer, size); |
| |
| if (read_bytes < 0) { |
| LogPrint(&p_session->log, kWarn, "Error reading from socket"); |
| return kRetFail_; |
| } |
| if (read_bytes == 0) { |
| LogPrint(&p_session->log, kInfo, "Client disconnected"); |
| return kRetFail_; |
| } |
| if (read_bytes != size) { |
| LogPrint(&p_session->log, kWarn, |
| "Read error: read bytes %d, expected bytes %d", read_bytes, size); |
| return kRetFail_; |
| } |
| |
| return kRetOK_; |
| } |
| |
| /** |
| * @brief _SendToSocket |
| * Send size of data to socket. |
| * |
| * @param p_session Session instance |
| * @param p_buffer The data to be sent. |
| * @param size bytes of the data. |
| * |
| * @return kRetFail_, kRetOK_ |
| */ |
| static int _SendToSocket(Session *p_session, char *p_buffer, int size) |
| { |
| int write_bytes; |
| |
| write_bytes = write(p_session->socket, p_buffer, size); |
| if (write_bytes < 0) { |
| LogPrint(&p_session->log, kWarn, "Write error code %d", errno); |
| return kRetFail_; |
| } |
| if (write_bytes == 0) { |
| LogPrint(&p_session->log, kInfo, "Client disconnected"); |
| return kRetFail_; |
| } |
| if (write_bytes != size) { |
| LogPrint(&p_session->log, kWarn, "Write bytes %d, expected bytes %d", |
| write_bytes, size); |
| return kRetFail_; |
| } |
| |
| return kRetOK_; |
| } |
| |
| /** |
| * @brief _SendWholePacketToSocket |
| * It will read length from the packet head, and send whole packet to socket. |
| * The data is from the socketbuffer in the session. |
| * |
| * @param p_session Session instance. |
| * |
| * @return kRetFail_, kRetOK_ |
| */ |
| static int _SendWholePacketToSocket(Session *p_session) |
| { |
| PacketHead *p_head = _get_packet_head(p_session); |
| int length = htonl(p_head->length); |
| |
| return _SendToSocket(p_session, (char *)p_head, sizeof(PacketHead) + length); |
| } |
| |
| static void _InitResponseHead(Session *p_session, int error_code, int length, |
| char *p_message) |
| { |
| PacketHead *p_head = _get_packet_head(p_session); |
| |
| p_head->type = htons(kResponse << 8 | p_session->message_type); |
| p_head->error_code = htons(error_code); |
| p_head->length = htonl(length); |
| if (p_message) { |
| memcpy(p_head->content, p_message, length); |
| } |
| } |
| |
| static int _SendResponse(Session *p_session, int error_code, int length, |
| char *p_message) { |
| _InitResponseHead(p_session, error_code, length, p_message); |
| return _SendWholePacketToSocket(p_session); |
| } |
| |
| /** |
| * @brief _CheckRealtimeStream |
| * Check if we have realtime streaming in this session. |
| * If we have the streaming, also reply an error response. |
| * |
| * @param p_session Session instance. |
| * |
| * @return kRetFail_, kRetOK_ |
| */ |
| static int _CheckRealtimeStream(Session *p_session) |
| { |
| if (p_session->realtime_mode == kNonRealtime) { |
| return kRetOK_; |
| } |
| |
| LogPrint(&p_session->log, kWarn, (char *)kErrorMessageRealtimeStream); |
| _SendResponse(p_session, kRealtimeStreamExists, |
| strlen(kErrorMessageRealtimeStream), |
| (char *)kErrorMessageRealtimeStream); |
| |
| return kRetFail_; |
| } |
| |
| /** |
| * @brief _DumpVideoFrameToClient |
| * Dump video frame data to client without header. |
| * It will also do shrink process if user requires it. |
| * |
| * @param p_session Session instance. |
| * @param p_source The video frame start address of the shared memory. |
| * |
| * @return kRetFail_, kRetOK_ |
| */ |
| static int _DumpVideoFrameToClient(Session *p_session, char *p_source) |
| { |
| int width, height, pixel, size; |
| int shrink_width, shrink_height; |
| int screen_width, screen_height; |
| char *p_dump_buffer; |
| char *p_width; |
| #ifdef MEASURE_DUMP_DURATION |
| struct timeval start, stop, diff; |
| |
| gettimeofday(&start, NULL); |
| #endif |
| |
| p_dump_buffer = p_session->p_dump_buffer; |
| screen_width = p_session->screen_width; |
| screen_height = p_session->screen_height; |
| |
| if (p_session->is_shrink) { |
| shrink_width = p_session->shrink_width; |
| shrink_height = p_session->shrink_height; |
| |
| /* |
| * For 1920x1080 video frame. memcpy takes 152 ms. |
| * For loop without shirnk takes 3.06 seconds. |
| * For loop with shink width 4 and shrink height 4 takes 121 ms. |
| * This memcpy will copy the whole frame from shared memory to internal |
| * memory. And then we use the internal memory to do the shrink thing. |
| * If we use the shared memory to do the shrink thing, it will take a long |
| * time to do it if we only shrink a little pixels. |
| */ |
| if (shrink_width < 4 || shrink_height < 4) { |
| size = screen_width * screen_height * kBytePerPixel_; |
| memcpy(p_dump_buffer, p_source, size); |
| p_source = p_dump_buffer; |
| } |
| |
| /* |
| * Process data pixel by pixel. |
| */ |
| size = 0; |
| for (height = 0; height < screen_height; height++) { |
| /* |
| * assign start width to p_width. |
| */ |
| p_width = p_source + screen_width * kBytePerPixel_ * height; |
| for (width = 0; width < screen_width; width++) { |
| // Copy pixel |
| for (pixel = 0; pixel < kBytePerPixel_; pixel++) { |
| p_dump_buffer[size++] = *p_width++; |
| } |
| // skip shrinked width |
| p_width += shrink_width * kBytePerPixel_; |
| width += shrink_width; |
| } |
| // skip shrinked height. |
| height += shrink_height; |
| } |
| } else { |
| /* Non shrink case, only copy whole video frame.*/ |
| size = screen_width * screen_height * kBytePerPixel_; |
| memcpy(p_dump_buffer, p_source, size); |
| } |
| |
| #ifdef MEASURE_DUMP_DURATION |
| gettimeofday(&stop, NULL); |
| timersub(&stop, &start, &diff); |
| LogPrint(&p_session->log, kInfo, "copy memory took %ld.%06ld", |
| (long int)diff.tv_sec, (long int)diff.tv_usec); |
| #endif |
| |
| return _SendToSocket(p_session, p_dump_buffer, size); |
| } |
| |
| /** |
| * @brief _DoMMAP |
| * Do mmap by the address and size. |
| * |
| * @param p_session Session instance. |
| * @param address address of the mmap. |
| * @param size size of the mmap. |
| * |
| * @return NULL - mmap failed. |
| * Pointer to the mapped area. |
| */ |
| static char *_DoMMAP(Session *p_session, uint32_t address, int size) |
| { |
| char *p_source; |
| |
| p_source = |
| mmap(0, size, PROT_READ, MAP_SHARED, p_session->dev_mem_fd, address); |
| if (p_source != MAP_FAILED) { |
| LogPrint(&p_session->log, kInfo, "MMAP address 0x%x, size %d bytes", |
| address, size); |
| return p_source; |
| } |
| |
| perror("cannot mmap source\n"); |
| LogPrint(&p_session->log, kError, "Cannot mmap source 0x%x", address); |
| _SendResponse(p_session, kArgument, strlen(kErrorMessageMMap), |
| (char *)kErrorMessageMMap); |
| return NULL; |
| } |
| |
| /** |
| * @brief _PrepareMMAP |
| * Prepare mmap by the session's dump_addresses, dump_limit and |
| * unit_aligned_size state variables. |
| * The mapped pointer will be stored in p_mmap_sources. |
| * |
| * @param p_session Session instance. |
| * |
| * @return kRetFail_, kRetOK_ |
| */ |
| static int _PrepareMMAP(Session *p_session) |
| { |
| int i; |
| int mmap_size; |
| |
| mmap_size = p_session->dump_limit * p_session->unit_aligned_size; |
| p_session->mmap_size = mmap_size; |
| |
| for (i = 0; i < MAX_VIDEO_DUMP_CHANNEL; i++) { |
| if (!p_session->dump_addresses[i]) { |
| continue; |
| } |
| p_session->p_mmap_sources[i] = |
| _DoMMAP(p_session, p_session->dump_addresses[i], mmap_size); |
| if (p_session->p_mmap_sources[i] == NULL) { |
| return kRetFail_; |
| } |
| } |
| |
| return kRetOK_; |
| } |
| |
| /** |
| * @brief _PrepareDumpBuffer |
| * Allocate memory to the p_dump_buffer. |
| * The size will be equal to the unit_aligned_size. |
| * |
| * @param p_session Session instance. |
| * |
| * @return kRetFail_, kRetOK_ |
| */ |
| int _PrepareDumpBuffer(Session *p_session) |
| { |
| p_session->p_dump_buffer = malloc(p_session->unit_aligned_size); |
| |
| LogPrint(&p_session->log, kInfo, "Allocate frame buffer %d bytes", |
| p_session->unit_aligned_size); |
| if (!p_session->p_dump_buffer) { |
| _SendResponse(p_session, kMemoryAllocFail, strlen(kErrorMessageMemoryAlloc), |
| (char *)kErrorMessageMemoryAlloc); |
| return kRetFail_; |
| } |
| |
| return kRetOK_; |
| } |
| |
| /** |
| * @brief _InitDumpVideoHead |
| * Init the video dump head and packet head's content. |
| * |
| * @param p_session Session instance. |
| * @param p_stream_head The video stream head to be inited. |
| */ |
| static void _InitDumpVideoHead(Session *p_session, |
| VideoDataStreamHead *p_stream_head) |
| { |
| PacketHead *p_head = &p_stream_head->head; |
| VideoDataStream *p_data_head = &p_stream_head->data_head; |
| unsigned width, height; |
| int dump_frame_size; |
| |
| p_head->type = htons(kData << 8 | p_session->message_type); |
| p_head->error_code = 0; |
| // Calculate length after shrinked frame |
| width = p_session->screen_width / (p_session->shrink_width + 1); |
| height = p_session->screen_height / (p_session->shrink_height + 1); |
| dump_frame_size = width * height * kBytePerPixel_; |
| p_head->length = htonl(sizeof(VideoDataStream) + dump_frame_size); |
| LogPrint(&p_session->log, kInfo, |
| "Start Dump, screen(%d, %d), dump(%d, %d), dump length %d", |
| p_session->screen_width, p_session->screen_height, width, height, |
| dump_frame_size); |
| p_data_head->width = htons(width); |
| p_data_head->height = htons(height); |
| } |
| |
| /** |
| * @brief _InitDumpAudioHead |
| * Init the audio dump head's and packet head's content. |
| * |
| * @param p_session Session instance. |
| * @param p_stream_head The audio stream head to be inited. |
| */ |
| static void _InitDumpAudioHead(Session *p_session, |
| AudioDataStreamHead *p_stream_head) |
| { |
| PacketHead *p_head = &p_stream_head->head; |
| |
| p_head->type = htons(kData << 8 | p_session->message_type); |
| p_head->error_code = 0; |
| p_head->length = htonl(sizeof(AudioDataStream) + kAudioPageSize_); |
| } |
| |
| /** |
| * @brief _DumpAllChannelVideoFrame |
| * Used to dump video frame from both dump controllers. |
| * |
| * @param p_session Session instance. |
| * @param p_stream_head The pre-inited video stream header. |
| * @param offset memory offset of the video frame. |
| * |
| * @return kRetFail_, kRetOK_ |
| */ |
| static int _DumpAllChannelVideoFrame(Session *p_session, |
| VideoDataStreamHead *p_stream_head, |
| int offset) |
| { |
| int i; |
| char *p_source; |
| VideoDataStream *p_data_head = &p_stream_head->data_head; |
| |
| for (i = 0; i < MAX_VIDEO_DUMP_CHANNEL; i++) { |
| p_source = p_session->p_mmap_sources[i]; |
| // Check if we need to dump data from this channel. |
| if (!p_source) { |
| continue; |
| } |
| p_data_head->channel = i; |
| /* Send video data header first */ |
| if (_SendToSocket(p_session, (char *)p_stream_head, |
| sizeof(VideoDataStreamHead))) { |
| return kRetFail_; |
| } |
| /* Send remain video frame data */ |
| if (_DumpVideoFrameToClient(p_session, p_source + offset)) { |
| return kRetFail_; |
| } |
| } |
| |
| return kRetOK_; |
| } |
| |
| /** |
| * @brief _DoDumpVideoFrame |
| * Dump non-realtime video frames. It only dump specified number of video |
| * frames to client. |
| * |
| * @param p_session Session instance |
| * @param number_of_frames Number of the video frames. |
| * |
| * @return kRetFail_, kRetOK_ |
| */ |
| static int _DoDumpVideoFrame(Session *p_session, int number_of_frames) |
| { |
| VideoDataStreamHead head; |
| VideoDataStream *p_data_head = &head.data_head; |
| unsigned long unit_aligned_size; |
| int i; |
| |
| unit_aligned_size = p_session->unit_aligned_size; |
| |
| _InitDumpVideoHead(p_session, &head); |
| LogPrint(&p_session->log, kDebug, "Dump number of frame %d", |
| number_of_frames); |
| |
| for (i = 0; i < number_of_frames; i++) { |
| p_data_head->frame_number = htonl(i); |
| if (_DumpAllChannelVideoFrame(p_session, &head, i * unit_aligned_size)) { |
| return kRetFail_; |
| } |
| } |
| |
| return kRetOK_; |
| } |
| |
| /** |
| * @brief _GetRealtimeVideoParameters |
| * Get the realtime video stream parameters from the chameleon and p_request. |
| * This function will also check the chameleon status. |
| * |
| * @param p_session Session instance. |
| * @param p_request DumpRealtimeVideoRequest |
| * |
| * @return kRetFail_, kRetOK_ |
| */ |
| static int _GetRealtimeVideoParameters(Session *p_session, |
| DumpRealtimeVideoRequest *p_request) |
| { |
| int positions[4]; |
| int width, height; |
| uint32_t dump_end_address; |
| int check_channel; |
| |
| /* Auto detect the video dump channel */ |
| if (ChameleonVideoGetRun(0)) { |
| p_session->dump_addresses[0] = ChameleonVideoGetDumpStartAddress(0); |
| check_channel = 0; |
| } else if (ChameleonVideoGetRun(1)) { |
| p_session->dump_addresses[0] = ChameleonVideoGetDumpStartAddress(1); |
| check_channel = 1; |
| } else { |
| LogPrint(&p_session->log, kWarn, (char *)kErrorMessageNotRun); |
| _SendResponse(p_session, kArgument, strlen(kErrorMessageNotRun), |
| (char *)kErrorMessageNotRun); |
| return kRetFail_; |
| } |
| |
| /* Get the width and height of the video frame */ |
| if (ChameleonVideoGetCropEnable(check_channel)) { |
| ChameleonVideoGetCrop(check_channel, positions); |
| width = positions[kCropRightIndex] - positions[kCropLeftIndex]; |
| height = positions[kCropBottomIndex] - positions[kCropTopIndex]; |
| } else { |
| width = ChameleonVideoGetFrameWidth(check_channel); |
| height = ChameleonVideoGetFrameHeight(check_channel); |
| } |
| |
| p_session->dump_limit = ChameleonVideoGetDumpLimit(check_channel); |
| p_session->screen_width = width; |
| p_session->screen_height = height; |
| p_session->realtime_check_channel = check_channel; |
| p_session->unit_aligned_size = |
| _calculate_page_aligned_size(width * height * kBytePerPixel_); |
| p_session->realtime_mode = p_request->mode; |
| |
| /* |
| * Check memory spaces first. to prevent memory overflow due to |
| * wrong chameleon config. |
| */ |
| dump_end_address = ChameleonVideoGetDumpEndAddress(check_channel); |
| LogPrint(&p_session->log, kInfo, |
| "Realtime Video address[0] = 0x%x, end address = 0x%x, " |
| "minimum memory space %d bytes", |
| p_session->dump_addresses[0], dump_end_address, |
| p_session->unit_aligned_size * p_session->dump_limit); |
| if (dump_end_address - p_session->dump_addresses[0] <= |
| p_session->unit_aligned_size * p_session->dump_limit) { |
| LogPrint(&p_session->log, kWarn, (char *)kErrorMessageDumpMemoryNotEnough); |
| _SendResponse(p_session, kArgument, |
| strlen(kErrorMessageDumpMemoryNotEnough), |
| (char *)kErrorMessageDumpMemoryNotEnough); |
| return kRetFail_; |
| } |
| |
| if (!p_request->is_dual) { |
| // Setup 2nd channel address to 0 to indicate we only dump from one channel. |
| p_session->dump_addresses[1] = 0; |
| goto function_exit; |
| } |
| |
| /* |
| * Reply error if we want to dump from 2nd channel but the channel is |
| * not running. |
| */ |
| if (!ChameleonVideoGetRun(!check_channel)) { |
| LogPrint(&p_session->log, kWarn, (char *)kErrorMessage2ndChannelNotRun); |
| _SendResponse(p_session, kArgument, strlen(kErrorMessage2ndChannelNotRun), |
| (char *)kErrorMessage2ndChannelNotRun); |
| return kRetFail_; |
| } |
| |
| /* |
| * For dual channel mode. |
| * We only support same parameters on both channels now. |
| * It doesn't make sense to use different parameters of 2 channels. |
| */ |
| if (ChameleonVideoGetCropEnable(!check_channel)) { |
| ChameleonVideoGetCrop(!check_channel, positions); |
| width = positions[kCropRightIndex] - positions[kCropLeftIndex]; |
| height = positions[kCropBottomIndex] - positions[kCropTopIndex]; |
| } else { |
| width = ChameleonVideoGetFrameWidth(!check_channel); |
| height = ChameleonVideoGetFrameHeight(!check_channel); |
| } |
| if (p_session->screen_width != width || p_session->screen_height != height || |
| p_session->dump_limit != ChameleonVideoGetDumpLimit(!check_channel)) { |
| LogPrint(&p_session->log, kWarn, (char *)kErrorMessageRealtimeNonSame); |
| _SendResponse(p_session, kArgument, strlen(kErrorMessageRealtimeNonSame), |
| (char *)kErrorMessageRealtimeNonSame); |
| return kRetFail_; |
| } |
| |
| p_session->dump_addresses[1] = |
| ChameleonVideoGetDumpStartAddress(!check_channel); |
| dump_end_address = ChameleonVideoGetDumpEndAddress(!check_channel); |
| LogPrint(&p_session->log, kInfo, |
| "Realtime Video address[1] = 0x%x, end address = 0x%x, " |
| "minimum memory space %d bytes", |
| p_session->dump_addresses[1], dump_end_address, |
| p_session->unit_aligned_size * p_session->dump_limit); |
| if (dump_end_address - p_session->dump_addresses[1] <= |
| p_session->unit_aligned_size * p_session->dump_limit) { |
| LogPrint(&p_session->log, kWarn, (char *)kErrorMessageDumpMemoryNotEnough); |
| _SendResponse(p_session, kArgument, |
| strlen(kErrorMessageDumpMemoryNotEnough), |
| (char *)kErrorMessageDumpMemoryNotEnough); |
| return kRetFail_; |
| } |
| |
| function_exit: |
| |
| LogPrint(&p_session->log, kInfo, "Screen width %d, height %d, dump limit %d", |
| p_session->screen_width, p_session->screen_height, |
| p_session->dump_limit); |
| |
| return kRetOK_; |
| } |
| |
| /** |
| * @brief _GetRealtimeAudioParameters |
| * Get the realtime audio stream parameters from the chameleon and p_request. |
| * This function will also check the chameleon status. |
| * |
| * @param p_session Session instance. |
| * @param p_request DumpRealtimeAudioRequest |
| * |
| * @return kRetFail_, kRetOK_ |
| */ |
| static int _GetRealtimeAudioParameters(Session *p_session, |
| DumpRealtimeAudioRequest *p_request) |
| { |
| uint32_t dump_end_address; |
| |
| if (!ChameleonAudioGetRun()) { |
| LogPrint(&p_session->log, kWarn, (char *)kErrorMessageNotRun); |
| _SendResponse(p_session, kArgument, strlen(kErrorMessageNotRun), |
| (char *)kErrorMessageNotRun); |
| return kRetFail_; |
| } |
| p_session->dump_addresses[0] = ChameleonAudioGetDumpStartAddress(); |
| dump_end_address = ChameleonAudioGetDumpEndAddress(); |
| /* |
| * There is no dump limit register of audio dump controller. We calculate it |
| * from the total memory spaces and audio page size. |
| */ |
| p_session->dump_limit = |
| (dump_end_address - p_session->dump_addresses[0]) / kAudioPageSize_; |
| p_session->unit_aligned_size = kAudioPageSize_; |
| p_session->realtime_mode = p_request->mode; |
| |
| LogPrint(&p_session->log, kInfo, |
| "Realtime audio start_address = 0x%x, stop_address = 0x%x, " |
| "limit %d", |
| p_session->dump_addresses[0], dump_end_address, |
| p_session->dump_limit); |
| |
| return kRetOK_; |
| } |
| |
| /** |
| * @brief _CheckRequestRealtimeMode |
| * Check if the requested realtime mode is acceptiable. |
| * |
| * @param p_session Session instance. |
| * @param mode The requested realtime mode. |
| * |
| * @return kRetFail_, kRetOK_ |
| */ |
| static int _CheckRequestRealtimeMode(Session *p_session, RealtimeMode mode) |
| { |
| if (kStopWhenOverflow <= mode && mode <= kBestEffort) { |
| return kRetOK_; |
| } |
| |
| LogPrint(&p_session->log, kWarn, "Realtime mode %d is not acceptable", mode); |
| |
| _SendResponse(p_session, kArgument, strlen(kErrorMessageRealtimeMode), |
| (char *)kErrorMessageRealtimeMode); |
| |
| return kRetFail_; |
| } |
| |
| /** |
| * @brief _GetCountDifference |
| * This funciton calculate the difference between current count with the |
| * hardware count of chameleon. |
| * The hardware count always goes in advance. |
| * So we use hw_count to subtract count to get difference. |
| * |
| * @param hw_count The count from chameleon. |
| * @param count The count in this session. |
| * |
| * @return Difference between hardware count and software count. |
| */ |
| static int _GetCountDifference(int hw_count, int count) |
| { |
| int difference; |
| |
| difference = hw_count - (count % kHW_CountWrap_); |
| if (difference < 0) { |
| difference += kHW_CountWrap_; |
| } |
| |
| return difference; |
| } |
| |
| /** |
| * @brief _GetNextDumpCount |
| * Get the next count of the next dump. |
| * This function will check if we can dump in time. |
| * If not, it will decide to drop data or stop dumping by the realtime_mode. |
| * |
| * @param p_session Session instance. |
| * @param current_count The session's current count. |
| * @param hw_count The count from chameleon. |
| * |
| * @return Next count, kRetFail_ |
| * If next count is 0, means we will stop dumping process. |
| */ |
| static uint32_t _GetNextDumpCount(Session *p_session, uint32_t current_count, |
| uint32_t hw_count) |
| { |
| int difference; |
| char buffer[128]; |
| char *p_error; |
| |
| difference = _GetCountDifference(hw_count, current_count); |
| if (difference == 0) { |
| return current_count; |
| } |
| // Overflow happens |
| if (difference > p_session->dump_limit) { |
| switch (p_session->realtime_mode) { |
| case kStopWhenOverflow: |
| LogPrint(&p_session->log, kWarn, (char *)kErrorMessageMemoryOverflow); |
| if (_SendResponse(p_session, |
| p_session->is_dump_audio ? kAudioMemoryOverflowStop |
| : kVideoMemoryOverflowStop, |
| strlen(kErrorMessageMemoryOverflow), |
| (char *)kErrorMessageMemoryOverflow)) { |
| return kRetFail_; |
| } |
| return 0; |
| |
| case kBestEffort: |
| p_error = |
| (char *)(p_session->is_dump_audio ? kErrorMessageDropAudioPage |
| : kErrorMessageDropVideoFrame); |
| sprintf(buffer, p_error, difference); |
| LogPrint(&p_session->log, kWarn, buffer); |
| if (_SendResponse(p_session, |
| p_session->is_dump_audio ? kAudioMemoryOverflowDrop |
| : kVideoMemoryOverflowDrop, |
| strlen(buffer), buffer)) { |
| return kRetFail_; |
| } |
| // Drop frames, jump to the latest one. |
| current_count += difference; |
| break; |
| |
| default: |
| LogPrint(&p_session->log, kError, "Can't reach here"); |
| return kRetFail_; |
| } |
| } else { |
| current_count++; |
| } |
| |
| return current_count; |
| } |
| |
| /** |
| * @brief _DoDumpRealtimeVideoFrame |
| * Dump realtime video frame function. |
| * This function will also receive the request message from client. |
| * |
| * @param p_session Session instance. |
| * |
| * @return kRetFail_, kRetOK_ |
| */ |
| static int _DoDumpRealtimeVideoFrame(Session *p_session) |
| { |
| /* |
| * Use dedicated stack memory for the head. |
| * So we can use socketbuffer to receive new messages. |
| */ |
| VideoDataStreamHead head; |
| VideoDataStream *p_data_head = &head.data_head; |
| unsigned long unit_aligned_size; |
| uint32_t frame_number, next_frame_number, hw_frame_count; |
| int i; |
| int poll_ret; |
| struct pollfd ufds[1]; |
| |
| ufds[0].fd = p_session->socket; |
| ufds[0].events = POLLIN | POLLPRI; |
| |
| unit_aligned_size = p_session->unit_aligned_size; |
| _InitDumpVideoHead(p_session, &head); |
| |
| frame_number = 0; |
| while (1) { |
| poll_ret = poll(ufds, 1, 0); |
| if (poll_ret == -1) { |
| perror("poll"); |
| return kRetFail_; |
| } else if (poll_ret) { |
| if (_ProcessMessage(p_session)) { |
| LogPrint(&p_session->log, kError, |
| "Process message fail during dump realtime video"); |
| return kRetFail_; |
| } |
| if (!p_session->stop_dump) { |
| /* |
| * we may change the state variables during the _ProcessMessage, |
| * So we reinit the video head here. |
| */ |
| _InitDumpVideoHead(p_session, &head); |
| } |
| } |
| |
| if (p_session->stop_dump) { |
| p_session->stop_dump = 0; |
| return kRetOK_; |
| } |
| |
| /* |
| * We assume chameleon board can get new frame on both channels at the same |
| * time. So only check the frame count of one channel. |
| */ |
| hw_frame_count = |
| ChameleonVideoGetFrameCount(p_session->realtime_check_channel); |
| next_frame_number = |
| _GetNextDumpCount(p_session, frame_number, hw_frame_count); |
| if (next_frame_number == frame_number) { |
| continue; |
| } |
| if (next_frame_number <= 0) { |
| return next_frame_number; |
| } |
| |
| p_data_head->frame_number = htonl(frame_number); |
| i = frame_number % p_session->dump_limit; |
| if (_DumpAllChannelVideoFrame(p_session, &head, i * unit_aligned_size)) { |
| return kRetFail_; |
| } |
| |
| frame_number = next_frame_number; |
| } |
| |
| return kRetOK_; |
| } |
| |
| /** |
| * @brief _DoDumpRealtimeAudioPage |
| * Dump realtime audio page function. |
| * This function will also receive the request message from client. |
| * |
| * @param p_session Session instance. |
| * |
| * @return kRetFail_, kRetOK_ |
| */ |
| static int _DoDumpRealtimeAudioPage(Session *p_session) |
| { |
| /* |
| * Use dedicated stack memory for the head. |
| * So we can use socketbuffer to receive new messages. |
| */ |
| AudioDataStreamHead head; |
| AudioDataStream *p_data_head = &head.data_head; |
| uint32_t page_count, next_page_count, hw_page_count; |
| char *p_source, *p_dump_buffer; |
| int i; |
| int poll_ret; |
| struct pollfd ufds[1]; |
| enum MessageType message_type; |
| |
| ufds[0].fd = p_session->socket; |
| ufds[0].events = POLLIN | POLLPRI; |
| |
| _InitDumpAudioHead(p_session, &head); |
| |
| page_count = 0; |
| p_source = p_session->p_mmap_sources[0]; |
| p_dump_buffer = p_session->p_dump_buffer; |
| while (1) { |
| poll_ret = poll(ufds, 1, 0); |
| if (poll_ret == -1) { |
| perror("poll"); |
| return kRetFail_; |
| } else if (poll_ret) { |
| /* backup message type */ |
| message_type = p_session->message_type; |
| if (_ProcessMessage(p_session)) { |
| LogPrint(&p_session->log, kError, |
| "Process message fail during dump realtime audio"); |
| return kRetFail_; |
| } |
| /* restore message type */ |
| p_session->message_type = message_type; |
| } |
| |
| if (p_session->stop_dump) { |
| p_session->stop_dump = 0; |
| return kRetOK_; |
| } |
| |
| hw_page_count = ChameleonAudioGetPageCount(); |
| next_page_count = _GetNextDumpCount(p_session, page_count, hw_page_count); |
| if (next_page_count == page_count) { |
| continue; |
| } |
| if (next_page_count <= 0) { |
| return next_page_count; |
| } |
| |
| p_data_head->page_count = htonl(page_count); |
| i = page_count % p_session->dump_limit; |
| if (_SendToSocket(p_session, (char *)&head, sizeof(AudioDataStreamHead))) { |
| return kRetFail_; |
| } |
| memcpy(p_dump_buffer, p_source + i * kAudioPageSize_, kAudioPageSize_); |
| if (_SendToSocket(p_session, p_dump_buffer, kAudioPageSize_)) { |
| return kRetFail_; |
| } |
| page_count = next_page_count; |
| } |
| |
| return kRetOK_; |
| } |
| |
| static void _ResetSession(Session *p_session) |
| { |
| p_session->screen_width = 0; |
| p_session->screen_height = 0; |
| p_session->is_shrink = 0; |
| p_session->shrink_width = 0; |
| p_session->shrink_height = 0; |
| |
| p_session->stop_dump = 0; |
| p_session->is_dump_audio = 0; |
| p_session->dump_limit = 0; |
| |
| p_session->realtime_mode = kNonRealtime; |
| } |
| |
| /** |
| * @brief _ProcessReset |
| * Message handler of kReset. |
| * |
| * @param p_session Session instance. |
| * |
| * @return kRetFail_, kRetOK_ |
| */ |
| static int _ProcessReset(Session *p_session) |
| { |
| LogPrint(&p_session->log, kInfo, "Process Reset"); |
| |
| /* |
| * If we have realtime stream, we can't do reset thing. |
| */ |
| if (_CheckRealtimeStream(p_session)) { |
| return kRetFail_; |
| } |
| |
| _ResetSession(p_session); |
| |
| return _SendResponse(p_session, kOK, 0, NULL); |
| } |
| |
| /** |
| * @brief _ProcessGetVersion |
| * Message handler of kGetVersion. |
| * |
| * @param p_session Session instance. |
| * |
| * @return kRetFail_, kRetOK_ |
| */ |
| static int _ProcessGetVersion(Session *p_session) |
| { |
| GetVersionResponse response; |
| |
| LogPrint(&p_session->log, kInfo, "GetVersion %d.%d", kMajor, kMinor); |
| |
| // Send response back. |
| response.major = kMajor; |
| response.minor = kMinor; |
| _InitResponseHead(p_session, kOK, sizeof(GetVersionResponse), |
| (char *)&response); |
| return _SendWholePacketToSocket(p_session); |
| } |
| |
| /** |
| * @brief _ProcessConfigVideoStream |
| * Message handler of kConfigVideoStream. |
| * |
| * @param p_session Session instance. |
| * |
| * @return kRetFail_, kRetOK_ |
| */ |
| static int _ProcessConfigVideoStream(Session *p_session) |
| { |
| ConfigVideoStreamRequest *p_request = |
| (ConfigVideoStreamRequest *)p_session->socketbuffer; |
| |
| p_session->screen_width = ntohs(p_request->screen_width); |
| p_session->screen_height = ntohs(p_request->screen_height); |
| |
| LogPrint(&p_session->log, kInfo, |
| "ConfigVideoStreamRequest width %d, height %d", |
| p_session->screen_width, p_session->screen_height); |
| |
| return _SendResponse(p_session, kOK, 0, NULL); |
| } |
| |
| /** |
| * @brief _ProcessConfigShrinkVideoStream |
| * Message handler of kConfigShrinkVideoStream. |
| * |
| * @param p_session Session instance. |
| * |
| * @return kRetFail_, kRetOK_ |
| */ |
| static int _ProcessConfigShrinkVideoStream(Session *p_session) |
| { |
| ConfigShrinkVideoStreamRequest *p_request = |
| (ConfigShrinkVideoStreamRequest *)p_session->socketbuffer; |
| |
| p_session->shrink_width = p_request->shrink_width; |
| p_session->shrink_height = p_request->shrink_height; |
| |
| p_session->is_shrink = p_session->shrink_width || p_session->shrink_height; |
| LogPrint(&p_session->log, kInfo, |
| "ConfigShrinkVideoStreamRequest shrink_width %u, shrink_height %u", |
| p_session->shrink_width, p_session->shrink_height); |
| |
| return _SendResponse(p_session, kOK, 0, NULL); |
| } |
| |
| /** |
| * @brief _ProcessDumpVideoFrame |
| * Message handler of kDumpVideoFrame. |
| * |
| * @param p_session Session instance. |
| * |
| * @return kRetFail_, kRetOK_ |
| */ |
| static int _ProcessDumpVideoFrame(Session *p_session) |
| { |
| DumpVideoFrameRequest *p_request = |
| (DumpVideoFrameRequest *)p_session->socketbuffer; |
| uint16_t number_of_frames; |
| uint32_t memory_addresses[MAX_VIDEO_DUMP_CHANNEL]; |
| unsigned long frame_size, screen_width, screen_height; |
| |
| number_of_frames = ntohs(p_request->number_of_frames); |
| memory_addresses[0] = ntohl(p_request->memory_address1); |
| memory_addresses[1] = ntohl(p_request->memory_address2); |
| |
| LogPrint(&p_session->log, kInfo, |
| "DumpVideoFrameRequest frames %d, memory1: 0x%x, memory2: 0x%x", |
| number_of_frames, memory_addresses[0], memory_addresses[1]); |
| |
| screen_width = p_session->screen_width; |
| screen_height = p_session->screen_height; |
| frame_size = screen_width * screen_height * kBytePerPixel_; |
| p_session->unit_aligned_size = _calculate_page_aligned_size(frame_size); |
| |
| p_session->dump_addresses[0] = memory_addresses[0]; |
| p_session->dump_addresses[1] = memory_addresses[1]; |
| |
| if (number_of_frames == 0) { |
| _SendResponse(p_session, kArgument, strlen(kErrorMessageFrameNumberZero), |
| (char *)kErrorMessageFrameNumberZero); |
| return kRetFail_; |
| } |
| |
| if (_PrepareDumpBuffer(p_session)) { |
| return kRetFail_; |
| } |
| |
| p_session->dump_limit = number_of_frames; |
| if (_PrepareMMAP(p_session)) { |
| return kRetFail_; |
| } |
| |
| if (_SendResponse(p_session, kOK, 0, NULL)) { |
| return kRetFail_; |
| } |
| |
| if (_DoDumpVideoFrame(p_session, number_of_frames)) { |
| return kRetFail_; |
| } |
| |
| _CleanDumpVariable(p_session); |
| return kRetOK_; |
| } |
| |
| /** |
| * @brief _ProcessDumpRealtimeVideoFrame |
| * Message handler of kDumpRealtimeVideoFrame. |
| * |
| * @param p_session Session instance. |
| * |
| * @return kRetFail_, kRetOK_ |
| */ |
| static int _ProcessDumpRealtimeVideoFrame(Session *p_session) |
| { |
| DumpRealtimeVideoRequest *p_request = |
| (DumpRealtimeVideoRequest *)p_session->socketbuffer; |
| |
| LogPrint(&p_session->log, kInfo, "DumpRealtimeVideo is_dual %d, mode %d", |
| p_request->is_dual, p_request->mode); |
| |
| if (_CheckRealtimeStream(p_session)) { |
| return kRetFail_; |
| } |
| |
| if (_CheckRequestRealtimeMode(p_session, p_request->mode)) { |
| return kRetFail_; |
| } |
| |
| if (_GetRealtimeVideoParameters(p_session, p_request)) { |
| return kRetFail_; |
| } |
| |
| if (_PrepareDumpBuffer(p_session)) { |
| return kRetFail_; |
| } |
| |
| if (_PrepareMMAP(p_session)) { |
| return kRetFail_; |
| } |
| |
| if (_SendResponse(p_session, kOK, 0, NULL)) { |
| return kRetFail_; |
| } |
| |
| if (_DoDumpRealtimeVideoFrame(p_session)) { |
| return kRetFail_; |
| } |
| |
| _CleanDumpVariable(p_session); |
| return kRetOK_; |
| } |
| |
| /** |
| * @brief _ProcessStopDump |
| * Message handler of kStopDump. |
| * |
| * @param p_session Session instance. |
| * |
| * @return kRetFail_, kRetOK_ |
| */ |
| static int _ProcessStopDump(Session *p_session) |
| { |
| LogPrint(&p_session->log, kInfo, "Process stop dump, current mode %d", |
| p_session->realtime_mode); |
| |
| if (p_session->realtime_mode != kNonRealtime) { |
| p_session->stop_dump = 1; |
| } |
| |
| return _SendResponse(p_session, kOK, 0, NULL); |
| } |
| |
| /** |
| * @brief _ProcessDumpRealtimeAudioPage |
| * Message handler of kDumpRealtimeAudioPage |
| * |
| * @param p_session Session instance. |
| * |
| * @return kRetFail_, kRetOK_ |
| */ |
| static int _ProcessDumpRealtimeAudioPage(Session *p_session) |
| { |
| DumpRealtimeAudioRequest *p_request = |
| (DumpRealtimeAudioRequest *)p_session->socketbuffer; |
| |
| LogPrint(&p_session->log, kInfo, "DumpRealtimeAudio"); |
| p_session->is_dump_audio = 1; |
| |
| if (_CheckRealtimeStream(p_session)) { |
| return kRetFail_; |
| } |
| |
| if (_CheckRequestRealtimeMode(p_session, p_request->mode)) { |
| return kRetFail_; |
| } |
| |
| if (_GetRealtimeAudioParameters(p_session, p_request)) { |
| return kRetFail_; |
| } |
| |
| if (_PrepareDumpBuffer(p_session)) { |
| return kRetFail_; |
| } |
| |
| if (_PrepareMMAP(p_session)) { |
| return kRetFail_; |
| } |
| |
| if (_SendResponse(p_session, kOK, 0, NULL)) { |
| return kRetFail_; |
| } |
| |
| if (_DoDumpRealtimeAudioPage(p_session)) { |
| return kRetFail_; |
| } |
| |
| _CleanDumpVariable(p_session); |
| return kRetOK_; |
| } |
| |
| /** |
| * @brief _ProcessMessage |
| * Main message process handler. It will read whole packet and dispatch to |
| * related message handler by the message type. |
| * |
| * @param p_session Session instance |
| * |
| * @return kRetFail_, kRetOK_ |
| */ |
| static int _ProcessMessage(Session *p_session) |
| { |
| PacketHead *p_head = _get_packet_head(p_session); |
| int type; |
| int length; |
| |
| // Read packet common header |
| if (_ReadFromSocket(p_session, sizeof(PacketHead))) { |
| return kRetFail_; |
| } |
| type = ntohs(p_head->type); |
| |
| // Check main type, it only can has request type since it is a server. |
| if ((type >> 8) != kRequest) { |
| LogPrint(&p_session->log, kError, "Type Error 0x%x != 0x%x ", type >> 8, |
| kRequest); |
| return kRetFail_; |
| } |
| |
| type = type & 0xFF; |
| if (type >= kMaxMessageType) { |
| LogPrint(&p_session->log, kError, "Type Error %d >= %d ", type, |
| kMaxMessageType); |
| return kRetFail_; |
| } |
| |
| // read remain content. |
| length = ntohl(p_head->length); |
| if (length && _ReadFromSocket(p_session, length)) { |
| return kRetFail_; |
| } |
| |
| p_session->message_type = type; |
| LogPrint(&p_session->log, kInfo, "Receive Type %d, length %d ", type, length); |
| |
| return _g_handlers[type](p_session); |
| } |
| |
| void SessionEntry(int socket) |
| { |
| Session *p_session = NULL; |
| char log_path[kPathBufferSize]; |
| |
| p_session = malloc(sizeof(Session)); |
| if (p_session == NULL) { |
| perror("Can't allocate memory for session\n"); |
| goto function_exit; |
| } |
| memset(p_session, 0, sizeof(Session)); |
| |
| p_session->socket = socket; |
| |
| /* Prepare log of this session */ |
| sprintf(log_path, kSessionLogfilePattern_, socket); |
| if (LogInit(&p_session->log, log_path)) { |
| goto function_exit; |
| } |
| |
| /* Prepare /dev/mem */ |
| p_session->dev_mem_fd = open("/dev/mem", O_RDWR | O_SYNC); |
| if (p_session->dev_mem_fd == -1) { |
| perror("can't open /dev/mem\n"); |
| LogPrint(&p_session->log, kError, "Can't open /dev/mem"); |
| goto function_exit; |
| } |
| |
| _ResetSession(p_session); |
| |
| LogPrint(&p_session->log, kDebug, "Session %d start", socket); |
| |
| /* The main loop of the session. */ |
| while (1) { |
| if (_ProcessMessage(p_session)) { |
| LogPrint(&p_session->log, kError, "Process message %d fail", |
| p_session->message_type); |
| break; |
| } |
| } |
| |
| function_exit: |
| |
| if (p_session) { |
| _CleanSession(p_session); |
| free(p_session); |
| } |
| close(socket); |
| } |