//
// Copyright 2013 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
#include
#include
#define NIRIO_IOCTL_MAP_MEMORY CTL_CODE(FILE_DEVICE_UNKNOWN, 0xF00, METHOD_BUFFERED, (FILE_READ_ACCESS | FILE_WRITE_ACCESS))
#define NIRIO_IOCTL_UNMAP_MEMORY CTL_CODE(FILE_DEVICE_UNKNOWN, 0xF01, METHOD_BUFFERED, (FILE_READ_ACCESS | FILE_WRITE_ACCESS))
namespace nirio_driver_iface {
nirio_status rio_open(
const std::string& device_path,
rio_dev_handle_t& device_handle)
{
device_handle = CreateFileA(device_path.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, /* default security */
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL /* template file */);
return (device_handle == INVALID_HANDLE_VALUE) ? NiRio_Status_InvalidParameter : NiRio_Status_Success;
}
void rio_close(rio_dev_handle_t& device_handle)
{
::CloseHandle(device_handle);
device_handle = INVALID_HANDLE_VALUE;
}
bool rio_isopen(rio_dev_handle_t device_handle)
{
return (device_handle != INVALID_HANDLE_VALUE);
}
nirio_status rio_ioctl(
rio_dev_handle_t device_handle,
uint32_t ioctl_code,
const void *write_buf,
size_t write_buf_len,
void *read_buf,
size_t read_buf_len)
{
if (!rio_isopen(device_handle)) return NiRio_Status_ResourceNotInitialized;
/* Note, if the file handle was opened with the OVERLAPPED flag, you must
* supply an OVERLAPPED structure to ReadFile, WriteFile, and
* DeviceIoControl, even when doing synchronous IO. */
OVERLAPPED zeroedOverlapped = {0};
DWORD outLen = 0;
int_fast32_t lastError = 0;
if (!(DeviceIoControl(device_handle, ioctl_code,
const_cast(write_buf), static_cast(write_buf_len),
read_buf, static_cast(read_buf_len),
&outLen, &zeroedOverlapped )))
{
lastError = GetLastError();
return NiRio_Status_SoftwareFault;
}
return NiRio_Status_Success;
}
unsigned int __stdcall memory_map_thread_routine(void *context)
{
rio_mmap_threadargs_t *args = (rio_mmap_threadargs_t*)context;
args->status = rio_ioctl(args->device_handle, NIRIO_IOCTL_MAP_MEMORY, &(args->params), sizeof(args->params), NULL, 0);
if (nirio_status_fatal(args->status))
{
SetEvent(reinterpret_cast(args->params.map_ready_event_handle));
}
return 0;
}
nirio_status rio_mmap(
rio_dev_handle_t device_handle,
uint16_t memory_type,
size_t size,
bool writable,
rio_mmap_t &map)
{
if (!rio_isopen(device_handle)) return NiRio_Status_ResourceNotInitialized;
access_mode_t access_mode = writable ? ACCESS_MODE_WRITE : ACCESS_MODE_READ;
uint64_t mapped_addr = 0;
map.map_thread_args.device_handle = device_handle;
map.map_thread_args.status = NiRio_Status_Success;
map.map_thread_args.params.memoryType = memory_type;
map.map_thread_args.params.size = (uint32_t)size;
map.map_thread_args.params.mapped_va_ptr = reinterpret_cast(&mapped_addr);
map.map_thread_args.params.access_mode = (uint8_t)access_mode;
HANDLE map_ready_event_handle = CreateEventA(NULL, TRUE, FALSE, NULL);
if (map_ready_event_handle == NULL) {
map.addr = NULL;
return NiRio_Status_SoftwareFault;
}
map.map_thread_args.params.map_ready_event_handle = reinterpret_cast(map_ready_event_handle);
map.map_thread_handle = (HANDLE) _beginthreadex(NULL, 0, memory_map_thread_routine, &(map.map_thread_args), 0, NULL);
nirio_status status = NiRio_Status_Success;
if (map.map_thread_handle == NULL) {
map.addr = NULL;
return NiRio_Status_SoftwareFault;
} else {
WaitForSingleObject(map_ready_event_handle, INFINITE);
map.addr = reinterpret_cast(mapped_addr);
if (map.addr == NULL) {
WaitForSingleObject(map.map_thread_handle, INFINITE);
CloseHandle(map.map_thread_handle);
nirio_status_chain(map.map_thread_args.status, status);
}
}
CloseHandle(map_ready_event_handle);
return status;
}
nirio_status rio_munmap(rio_mmap_t &map)
{
if (!rio_isopen(map.map_thread_args.device_handle)) return NiRio_Status_ResourceNotInitialized;
nirio_status status = NiRio_Status_Success;
if (map.addr != NULL) {
uint64_t mapped_addr = reinterpret_cast(map.addr);
status = rio_ioctl(map.map_thread_args.device_handle, NIRIO_IOCTL_UNMAP_MEMORY, &mapped_addr, sizeof(mapped_addr), NULL, 0);
if (nirio_status_not_fatal(status)) {
WaitForSingleObject(map.map_thread_handle, INFINITE);
}
CloseHandle(map.map_thread_handle);
map.addr = NULL;
}
return status;
}
}