/*********************************************************** * Name: vc_dispmanx_resource_write_data * * Arguments: * DISPMANX_RESOURCE_HANDLE_T res * int src_pitch * void * src_address * const VC_RECT_T * rect * * Description: Copy the bitmap data to VideoCore memory * * Returns: 0 or failure * ***********************************************************/ VCHPRE_ int VCHPOST_ vc_dispmanx_resource_write_data( DISPMANX_RESOURCE_HANDLE_T handle, VC_IMAGE_TYPE_T src_type /* not used */, int src_pitch, void * src_address, const VC_RECT_T * rect ) { (void)src_type; //Note that x coordinate of the rect is NOT used //Address of data in host uint8_t *host_start = (uint8_t *)src_address + src_pitch * rect->y; int32_t bulk_len = src_pitch * rect->height, success = 0; //Now send the bulk transfer across //command parameters: resource handle, destination y, bulk length uint32_t param[] = {VC_HTOV32(handle), VC_HTOV32(rect->y), VC_HTOV32(bulk_len)}; success = dispmanx_send_command( EDispmanBulkWrite | DISPMANX_NO_REPLY_MASK, param, sizeof(param)); if(success == 0) { lock_obtain(); success = vchi_bulk_queue_transmit( dispmanx_client.client_handle[0], host_start, bulk_len, VCHI_FLAGS_BLOCK_UNTIL_DATA_READ, NULL ); lock_release(); } return (int) success; }
/****************************************************************************** NAME vchostreq_readmem SYNOPSIS VC_RESP_CODE_T vchostreq_readmem( void* host_addr, void *vc_addr, int len ) FUNCTION Allows Videocore to read a block of memory from a memory area on the host specified by host_addr Obsolete now RETURNS Success : 0 - all the data was copied Fail : 1 - error in input parameters or operation ******************************************************************************/ int vchostreq_readmem( void* host_addr, void *vc_addr, int len ) { uint32_t param[] = {(uint32_t) host_addr, len, (uint32_t) vc_addr}; int success = hostreq_send_command(VC_HOSTREQ_READMEM, param, sizeof(param)); assert(success == 0); assert(0); // should not be calling this function success = vchi_bulk_queue_transmit(hostreq_client.client_handle[0], host_addr, len, VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE, NULL); assert(success == 0); return (success == 0) ? 0 : 1; }
// if handle==MEM_HANDLE_INVALID: data is a pointer, size is length // if handle!=MEM_HANDLE_INVALID: data is int, offset from handle base, size is length void vchiq_queue_bulk_transmit(VCHIQ_STATE_T *state, int fourcc, VCHI_MEM_HANDLE_T handle, const void *data, int size, void *userdata) { VCHIQ_WRAPPER_T *st = wrapper_list; while(st != NULL && (st->state != state || st->fourcc != fourcc)) st = st->next; vcos_demand(st != NULL); if(handle == MEM_HANDLE_INVALID) vchi_bulk_queue_transmit(st->vchi_handle, data, size, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); else vchi_bulk_queue_transmit_reloc(st->vchi_handle, handle, (uint32_t) data, size, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); }
/*********************************************************** * Name: vc_dispmanx_resource_set_palette * * Arguments: * DISPMANX_RESOURCE_HANDLE_T res * void * src_address * int offset * int size * * Description: Set the resource palette (for VC_IMAGE_4BPP and VC_IMAGE_8BPP) * offset should be 0 * size is 16*2 for 4BPP and 256*2 for 8BPP * Returns: 0 or failure * ***********************************************************/ VCHPRE_ int VCHPOST_ vc_dispmanx_resource_set_palette( DISPMANX_RESOURCE_HANDLE_T handle, void * src_address, int offset, int size) { //Note that x coordinate of the rect is NOT used //Address of data in host uint8_t *host_start = src_address; int32_t bulk_len = size, success = 0; //Now send the bulk transfer across //command parameters: resource size uint32_t param[] = {VC_HTOV32(handle), VC_HTOV32(offset), VC_HTOV32(bulk_len) }; success = dispmanx_send_command( EDispmanSetPalette | DISPMANX_NO_REPLY_MASK, param, sizeof(param)); if(success == 0) { lock_obtain(); success = vchi_bulk_queue_transmit( dispmanx_client.client_handle[0], host_start, bulk_len, VCHI_FLAGS_BLOCK_UNTIL_DATA_READ, NULL ); lock_release(); } return (int) success; }
int bcm2835_audio_write_worker(bcm2835_alsa_stream_t *alsa_stream, uint32_t count, void *src) { VC_AUDIO_MSG_T m; AUDIO_INSTANCE_T *instance = alsa_stream->instance; int32_t success; int ret; LOG_DBG(" .. IN\n"); LOG_INFO(" Writing %d bytes from %p\n", count, src); if(mutex_lock_interruptible(&instance->vchi_mutex)) { LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); return -EINTR; } vchi_service_use(instance->vchi_handle[0]); if ( instance->peer_version==0 && vchi_get_peer_version(instance->vchi_handle[0], &instance->peer_version) == 0 ) { LOG_DBG("%s: client version %d connected\n", __func__, instance->peer_version); } m.type = VC_AUDIO_MSG_TYPE_WRITE; m.u.write.count = count; // old version uses bulk, new version uses control m.u.write.max_packet = instance->peer_version < 2 || force_bulk ? 0:4000; m.u.write.callback = alsa_stream->fifo_irq_handler; m.u.write.cookie = alsa_stream; m.u.write.silence = src == NULL; /* Send the message to the videocore */ success = vchi_msg_queue(instance->vchi_handle[0], &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); if (success != 0) { LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", __func__, success); ret = -1; goto unlock; } if (!m.u.write.silence) { if (m.u.write.max_packet == 0) { /* Send the message to the videocore */ success = vchi_bulk_queue_transmit(instance->vchi_handle[0], src, count, 0 * VCHI_FLAGS_BLOCK_UNTIL_QUEUED + 1 * VCHI_FLAGS_BLOCK_UNTIL_DATA_READ, NULL); } else { while (count > 0) { int bytes = min((int)m.u.write.max_packet, (int)count); success = vchi_msg_queue(instance->vchi_handle[0], src, bytes, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); src = (char *)src + bytes; count -= bytes; } } if (success != 0) { LOG_ERR ("%s: failed on vchi_bulk_queue_transmit (status=%d)\n", __func__, success); ret = -1; goto unlock; } } ret = 0; unlock: vchi_service_release(instance->vchi_handle[0]); mutex_unlock(&instance->vchi_mutex); LOG_DBG(" .. OUT\n"); return ret; }
// send a buffer to VideoCore either by writing the buffer data in a control // message or by sending an aligned bulk transfer with fixup information in the // control message. OMX_ERRORTYPE vc_ilcs_pass_buffer(IL_FUNCTION_T func, void *reference, OMX_BUFFERHEADERTYPE *pBuffer) { IL_PASS_BUFFER_EXECUTE_T exe; IL_BUFFER_BULK_T fixup; IL_RESPONSE_HEADER_T resp; void *data2 = NULL; int len2 = 0; if ((func == IL_EMPTY_THIS_BUFFER && pBuffer->pInputPortPrivate == NULL) || (func == IL_FILL_THIS_BUFFER && pBuffer->pOutputPortPrivate == NULL)) { // return this to pass conformance // the actual error is using a buffer that hasn't be registered with usebuffer/allocatebuffer return OMX_ErrorIncorrectStateOperation; } exe.reference = reference; memcpy(&exe.bufferHeader, pBuffer, sizeof(OMX_BUFFERHEADERTYPE)); if(pBuffer->nFilledLen) { if(pBuffer->nFilledLen + sizeof(IL_PASS_BUFFER_EXECUTE_T) <= VC_ILCS_MAX_INLINE) { exe.method = IL_BUFFER_INLINE; data2 = pBuffer->pBuffer + pBuffer->nOffset; len2 = pBuffer->nFilledLen; } else { const uint8_t *start = pBuffer->pBuffer + pBuffer->nOffset; const uint8_t *end = start + pBuffer->nFilledLen; const uint8_t *round_start = (const OMX_U8*)VCHI_BULK_ROUND_UP(start); const uint8_t *round_end = (const OMX_U8*)VCHI_BULK_ROUND_DOWN(end); int bulk_len = round_end-round_start; int32_t result; exe.method = IL_BUFFER_BULK; result = vchi_bulk_queue_transmit( vc_ilcsg.vchi_handle, round_start, round_end-round_start, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL ); // when IL_EMPTY_THIS_BUFFER executes on videocore, there is a // corresponding vchi_bulk_queue_receive, which blocks until // complete (at which point, the above vchi_bulk_queue_transmit // must by definition have completed) vc_assert(result == 0); if((fixup.headerlen = round_start - start) > 0) memcpy(fixup.header, start, fixup.headerlen); if((fixup.trailerlen = end - round_end) > 0) memcpy(fixup.trailer, round_end, fixup.trailerlen); data2 = &fixup; len2 = sizeof(IL_BUFFER_BULK_T); } } else { exe.method = IL_BUFFER_NONE; } vc_ilcs_execute_function(func, &exe, sizeof(IL_PASS_BUFFER_EXECUTE_T), data2, len2, &resp, sizeof(resp)); return resp.err; }
static void omx_buffer_fixup(IL_PASS_BUFFER_EXECUTE_T* data, void**data2, int*len2,IL_BUFFER_BULK_T *fixup) { OMX_BUFFERHEADERTYPE* headptr; void* uaddr; vc_malloc_t desc; OMX_BUFFERHEADERTYPE *pBuffer; IL_RESPONSE_HEADER_T *resp; *data2 = NULL; *len2 = 0; if(NULL==data || NULL == data->bufferHeader.pBuffer) { OMX_DEBUG("error: invalid / corrupt memory\n"); return; } headptr = &data->bufferHeader; uaddr = (void*)headptr->pBuffer; uaddr -= sizeof(desc); memcpy(&desc, uaddr, sizeof(desc)); // if(desc.key != VC_MAGIC || NULL == desc.kaddr || NULL == desc.addr) { OMX_DEBUG("error: invalid / corrupt memory\n"); return; } headptr->pPlatformPrivate = headptr->pBuffer; headptr->pBuffer = desc.kaddr + sizeof(desc); #if 0 vchi_bulk_queue_transmit(vc_ilcs_vchi_handle(), headptr->pBuffer + headptr->nOffset, headptr->nFilledLen, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL ); #else pBuffer = headptr; if(pBuffer->nFilledLen) { if(pBuffer->nFilledLen + sizeof(IL_PASS_BUFFER_EXECUTE_T) <= VC_ILCS_MAX_INLINE) { data->method = IL_BUFFER_INLINE; *data2 = pBuffer->pBuffer + pBuffer->nOffset; *len2 = pBuffer->nFilledLen; } else { const uint8_t *start = pBuffer->pBuffer + pBuffer->nOffset; const uint8_t *end = start + pBuffer->nFilledLen; const uint8_t *round_start = (const OMX_U8*)VCHI_BULK_ROUND_UP(start); const uint8_t *round_end = (const OMX_U8*)VCHI_BULK_ROUND_DOWN(end); int bulk_len = round_end-round_start; int32_t result; data->method = IL_BUFFER_BULK; result = vchi_bulk_queue_transmit( vc_ilcsg.vchi_handle, round_start, round_end-round_start, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL ); // when IL_EMPTY_THIS_BUFFER executes on videocore, there is a // corresponding vchi_bulk_queue_receive, which blocks until // complete (at which point, the above vchi_bulk_queue_transmit // must by definition have completed) vc_assert(result == 0); if((fixup->headerlen = round_start - start) > 0) memcpy(fixup->header, start, fixup->headerlen); if((fixup->trailerlen = end - round_end) > 0) memcpy(fixup->trailer, round_end, fixup->trailerlen); *data2 = fixup; *len2 = sizeof(IL_BUFFER_BULK_T); } } else { data->method = IL_BUFFER_NONE; } #endif }
/* ---------------------------------------------------------------------- * send a string to the host side IL component service. if resp is NULL * then there is no response to this call, so we should not wait for one. * * returns 0 on successful call made, -1 on failure to send call. * on success, the response is written to 'resp' pointer * -------------------------------------------------------------------- */ int vc_ilcs_execute_function( IL_FUNCTION_T func, void *data, int len, void *data2, int len2, void *bulk, int bulk_len, void *resp ) { VC_ILCS_WAIT_T *wait; int i, num; // the host MUST receive a response vc_assert( resp ); // need to atomically find free ->wait entry os_semaphore_obtain( &vc_ilcsg.wait_sem ); // we try a number of times then give up with an error message // rather than just deadlocking for (i=0; i<VC_ILCS_WAIT_TIMEOUT; i++) { num = 0; while( num < VC_ILCS_MAX_WAITING && vc_ilcsg.wait[num].resp ) num++; if ( num < VC_ILCS_MAX_WAITING || i == VC_ILCS_WAIT_TIMEOUT-1) break; // might be a fatal error if another thread is relying // on this call completing before it can complete // we'll pause until we can carry on and hope that's sufficient. os_semaphore_release( &vc_ilcsg.wait_sem ); os_sleep( 10 ); // 10 msec // if we're the vcilcs thread, then the waiters might need // us to handle their response, so try and clear those now if(os_thread_is_running(&vc_ilcsg.thread)) while(vc_ilcs_process_message(0)); os_logging_message( "%s: wait for sem", __FUNCTION__); os_semaphore_obtain( &vc_ilcsg.wait_sem ); } if(num == VC_ILCS_MAX_WAITING) { // failed to send message. vc_assert(0); os_semaphore_release( &vc_ilcsg.wait_sem ); return -1; } wait = &vc_ilcsg.wait[num]; wait->resp = resp; wait->xid = vc_ilcsg.next_xid++; os_semaphore_create( &wait->sem, OS_SEMAPHORE_TYPE_SUSPEND ); os_semaphore_obtain( &wait->sem ); // at this point, ->wait is exclusively ours () os_semaphore_release( &vc_ilcsg.wait_sem ); if(bulk) os_semaphore_obtain( &vc_ilcsg.send_sem); // write the command header. vc_ilcs_transmit( func, wait->xid, data, len, data2, len2 ); if(bulk) { int result; result = vchi_bulk_queue_transmit( vc_ilcsg.vchi_handle, // call to VCHI bulk, bulk_len, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL ); vc_assert(result == 0); os_semaphore_release( &vc_ilcsg.send_sem); } if ( !os_thread_is_running(&vc_ilcsg.thread) ) { os_semaphore_obtain( &wait->sem ); } else { // we're the vcilcs task, so wait for, and handle, incoming // messages while we're not completed for (;;) { // wait->sem will not be released until we process the response message vc_ilcs_process_message(1); // did the last message release wait->sem ? if ( !os_semaphore_obtained(&wait->sem) ) break; } } // safe to do the following - the assignment of NULL is effectively atomic os_semaphore_destroy( &wait->sem ); wait->resp = NULL; return 0; }
//Request handler static void hostreq_request_handler(int inum, HOSTREQ_SERVICE_T *state){ assert(state->response_length); uint32_t *data = (uint32_t*)state->response_buffer; uint32_t command = *data; int32_t success = 0; data ++; state->response_length -= sizeof(uint32_t); switch(command) { case VC_HOSTREQ_TIME: { //VC asks for time //HOST specific implementation //We just return zero for time for now time_t dummytime = vchi_hostreq_time(); int success = hostreq_send_command(VC_HOSTREQ_TIME, &dummytime, sizeof(dummytime)); assert(success == 0); break; } case VC_HOSTREQ_NOTIFY: { //Host notify assert(state->response_length == 2*sizeof(uint32_t)); VC_HRNOTIFY_T reason = (VC_HRNOTIFY_T) *data; data ++; uint32_t param = *data; if(hostreq_client.notify_callbacks[reason]) { (*hostreq_client.notify_callbacks[reason])(reason, param); } break; } case VC_HOSTREQ_READMEM: { //params are hostaddr, len, vc addr uint32_t *param = data; assert(param[0]); success = vchi_bulk_queue_transmit(hostreq_client.client_handle[0], (void *)param[0], param[1], VCHI_FLAGS_NONE, NULL); assert(success == 0); break; } case VC_HOSTREQ_WRITEMEM: { //params are hostaddr, len, vc addr uint32_t *param = data; assert(param[0]); success = vchi_bulk_queue_receive(hostreq_client.client_handle[0], (void *)param[0], param[1], VCHI_FLAGS_NONE, NULL); assert(success == 0); break; } default: assert(!"unhandled request"); } }