23#include <freerdp/config.h>
39#include <linux/ppdev.h>
40#include <linux/parport.h>
44#include <winpr/assert.h>
45#include <winpr/synch.h>
46#include <winpr/thread.h>
47#include <winpr/stream.h>
48#include <winpr/collections.h>
49#include <winpr/interlocked.h>
51#include <freerdp/types.h>
52#include <freerdp/freerdp.h>
53#include <freerdp/constants.h>
54#include <freerdp/channels/rdpdr.h>
55#include <freerdp/channels/log.h>
56#include <freerdp/utils/rdpdr_utils.h>
58#define TAG CHANNELS_TAG("drive.client")
70 rdpContext* rdpcontext;
79static UINT parallel_process_irp_create(PARALLEL_DEVICE* parallel, IRP* irp)
82 UINT32 PathLength = 0;
84 WINPR_ASSERT(parallel);
87 if (!Stream_SafeSeek(irp->input, 28))
88 return ERROR_INVALID_DATA;
91 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 4))
92 return ERROR_INVALID_DATA;
93 Stream_Read_UINT32(irp->input, PathLength);
94 if (PathLength <
sizeof(WCHAR))
95 return ERROR_INVALID_DATA;
96 const WCHAR* ptr = Stream_ConstPointer(irp->input);
97 if (!Stream_SafeSeek(irp->input, PathLength))
98 return ERROR_INVALID_DATA;
99 path = ConvertWCharNToUtf8Alloc(ptr, PathLength /
sizeof(WCHAR), NULL);
101 return CHANNEL_RC_NO_MEMORY;
103 parallel->id = irp->devman->id_sequence++;
104 parallel->file = open(parallel->path, O_RDWR);
106 if (parallel->file < 0)
108 irp->IoStatus = STATUS_ACCESS_DENIED;
114 if (fcntl(parallel->file, F_SETFL, O_NONBLOCK) == -1)
119 Stream_Write_UINT32(irp->output, parallel->id);
120 Stream_Write_UINT8(irp->output, 0);
122 return irp->Complete(irp);
130static UINT parallel_process_irp_close(PARALLEL_DEVICE* parallel, IRP* irp)
132 WINPR_ASSERT(parallel);
135 (void)close(parallel->file);
137 Stream_Zero(irp->output, 5);
138 return irp->Complete(irp);
146static UINT parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp)
153 WINPR_ASSERT(parallel);
156 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 12))
157 return ERROR_INVALID_DATA;
158 Stream_Read_UINT32(irp->input, Length);
159 Stream_Read_UINT64(irp->input, Offset);
162 buffer = (BYTE*)calloc(Length,
sizeof(BYTE));
166 WLog_Print(parallel->log, WLOG_ERROR,
"malloc failed!");
167 return CHANNEL_RC_NO_MEMORY;
170 status = read(parallel->file, buffer, Length);
172 if ((status < 0) || (status > UINT32_MAX))
174 irp->IoStatus = STATUS_UNSUCCESSFUL;
181 Length = (UINT32)status;
184 Stream_Write_UINT32(irp->output, Length);
188 if (!Stream_EnsureRemainingCapacity(irp->output, Length))
190 WLog_Print(parallel->log, WLOG_ERROR,
"Stream_EnsureRemainingCapacity failed!");
192 return CHANNEL_RC_NO_MEMORY;
195 Stream_Write(irp->output, buffer, Length);
199 return irp->Complete(irp);
207static UINT parallel_process_irp_write(PARALLEL_DEVICE* parallel, IRP* irp)
213 WINPR_ASSERT(parallel);
216 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 12))
217 return ERROR_INVALID_DATA;
219 Stream_Read_UINT32(irp->input, Length);
220 Stream_Read_UINT64(irp->input, Offset);
223 if (!Stream_SafeSeek(irp->input, 20))
224 return ERROR_INVALID_DATA;
225 const void* ptr = Stream_ConstPointer(irp->input);
226 if (!Stream_SafeSeek(irp->input, Length))
227 return ERROR_INVALID_DATA;
232 const ssize_t status = write(parallel->file, ptr, len);
234 if ((status < 0) || (status > len))
236 irp->IoStatus = STATUS_UNSUCCESSFUL;
241 Stream_Seek(irp->input, WINPR_ASSERTING_INT_CAST(
size_t, status));
245 Stream_Write_UINT32(irp->output, Length);
246 Stream_Write_UINT8(irp->output, 0);
247 return irp->Complete(irp);
255static UINT parallel_process_irp_device_control(WINPR_ATTR_UNUSED PARALLEL_DEVICE* parallel,
258 WINPR_ASSERT(parallel);
261 Stream_Write_UINT32(irp->output, 0);
262 WINPR_ASSERT(irp->Complete);
263 return irp->Complete(irp);
271static UINT parallel_process_irp(PARALLEL_DEVICE* parallel, IRP* irp)
273 UINT error = ERROR_INTERNAL_ERROR;
275 WINPR_ASSERT(parallel);
278 switch (irp->MajorFunction)
281 error = parallel_process_irp_create(parallel, irp);
285 error = parallel_process_irp_close(parallel, irp);
289 error = parallel_process_irp_read(parallel, irp);
293 error = parallel_process_irp_write(parallel, irp);
296 case IRP_MJ_DEVICE_CONTROL:
297 error = parallel_process_irp_device_control(parallel, irp);
301 irp->IoStatus = STATUS_NOT_SUPPORTED;
302 error = irp->Complete(irp);
306 DWORD level = WLOG_TRACE;
310 WLog_Print(parallel->log, level,
311 "[%s|0x%08" PRIx32
"] completed with %s [0x%08" PRIx32
"] (IoStatus %s [0x%08" PRIx32
313 rdpdr_irp_string(irp->MajorFunction), irp->MajorFunction, WTSErrorToString(error),
314 error, NtStatus2Tag(irp->IoStatus), irp->IoStatus);
319static DWORD WINAPI parallel_thread_func(LPVOID arg)
321 PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)arg;
322 UINT error = CHANNEL_RC_OK;
324 WINPR_ASSERT(parallel);
327 if (!MessageQueue_Wait(parallel->queue))
329 WLog_Print(parallel->log, WLOG_ERROR,
"MessageQueue_Wait failed!");
330 error = ERROR_INTERNAL_ERROR;
334 wMessage message = { 0 };
335 if (!MessageQueue_Peek(parallel->queue, &message, TRUE))
337 WLog_Print(parallel->log, WLOG_ERROR,
"MessageQueue_Peek failed!");
338 error = ERROR_INTERNAL_ERROR;
342 if (message.id == WMQ_QUIT)
345 IRP* irp = (IRP*)message.wParam;
347 error = parallel_process_irp(parallel, irp);
350 WLog_Print(parallel->log, WLOG_ERROR,
351 "parallel_process_irp failed with error %" PRIu32
"!", error);
356 if (error && parallel->rdpcontext)
357 setChannelError(parallel->rdpcontext, error,
"parallel_thread_func reported an error");
368static UINT parallel_irp_request(DEVICE* device, IRP* irp)
370 PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)device;
372 WINPR_ASSERT(parallel);
374 if (!MessageQueue_Post(parallel->queue, NULL, 0, (
void*)irp, NULL))
376 WLog_Print(parallel->log, WLOG_ERROR,
"MessageQueue_Post failed!");
377 return ERROR_INTERNAL_ERROR;
380 return CHANNEL_RC_OK;
388static UINT parallel_free_int(PARALLEL_DEVICE* parallel)
392 if (!MessageQueue_PostQuit(parallel->queue, 0) ||
393 (WaitForSingleObject(parallel->thread, INFINITE) == WAIT_FAILED))
395 const UINT error = GetLastError();
396 WLog_Print(parallel->log, WLOG_ERROR,
397 "WaitForSingleObject failed with error %" PRIu32
"!", error);
400 (void)CloseHandle(parallel->thread);
401 Stream_Free(parallel->device.data, TRUE);
402 MessageQueue_Free(parallel->queue);
405 return CHANNEL_RC_OK;
408static UINT parallel_free(DEVICE* device)
411 return parallel_free_int((PARALLEL_DEVICE*)device);
412 return CHANNEL_RC_OK;
415static void parallel_message_free(
void* obj)
423 IRP* irp = (IRP*)msg->wParam;
426 WINPR_ASSERT(irp->Discard);
438 PARALLEL_DEVICE* parallel = NULL;
441 WINPR_ASSERT(pEntryPoints);
444 WINPR_ASSERT(device);
446 wLog* log = WLog_Get(TAG);
449 char* name = device->device.Name;
450 char* path = device->Path;
452 if (!name || (name[0] ==
'*') || !path)
455 WLog_Print(log, WLOG_WARN,
"Autodetection not implemented, no ports will be redirected");
456 return CHANNEL_RC_INITIALIZATION_ERROR;
459 if (name[0] && path[0])
461 parallel = (PARALLEL_DEVICE*)calloc(1,
sizeof(PARALLEL_DEVICE));
465 WLog_Print(log, WLOG_ERROR,
"calloc failed!");
466 return CHANNEL_RC_NO_MEMORY;
470 parallel->device.type = RDPDR_DTYP_PARALLEL;
471 parallel->device.name = name;
472 parallel->device.IRPRequest = parallel_irp_request;
473 parallel->device.Free = parallel_free;
474 parallel->rdpcontext = pEntryPoints->rdpcontext;
475 const size_t length = strlen(name);
476 parallel->device.data = Stream_New(NULL, length + 1);
478 if (!parallel->device.data)
480 WLog_Print(parallel->log, WLOG_ERROR,
"Stream_New failed!");
481 error = CHANNEL_RC_NO_MEMORY;
485 for (
size_t i = 0; i <= length; i++)
486 Stream_Write_INT8(parallel->device.data, name[i] < 0 ?
'_' : name[i]);
488 parallel->path = path;
489 parallel->queue = MessageQueue_New(NULL);
491 if (!parallel->queue)
493 WLog_Print(parallel->log, WLOG_ERROR,
"MessageQueue_New failed!");
494 error = CHANNEL_RC_NO_MEMORY;
498 wObject* obj = MessageQueue_Object(parallel->queue);
500 obj->fnObjectFree = parallel_message_free;
502 error = pEntryPoints->RegisterDevice(pEntryPoints->devman, ¶llel->device);
505 WLog_Print(parallel->log, WLOG_ERROR,
"RegisterDevice failed with error %" PRIu32
"!",
510 parallel->thread = CreateThread(NULL, 0, parallel_thread_func, parallel, 0, NULL);
511 if (!parallel->thread)
513 WLog_Print(parallel->log, WLOG_ERROR,
"CreateThread failed!");
514 error = ERROR_INTERNAL_ERROR;
519 return CHANNEL_RC_OK;
521 parallel_free_int(parallel);
This struct contains function pointer to initialize/free objects.