Big serial- and disk-redirection update from

Andreas Flick <Andreas.Flick@unicon-ka.de>


git-svn-id: svn://svn.code.sf.net/p/rdesktop/code/trunk/rdesktop@794 423420c4-83ab-492f-b58f-81f9feb106b5
This commit is contained in:
Michael Gernoth 2004-11-03 13:56:52 +00:00
parent 4ad6d8d4e0
commit 53eabea36f
7 changed files with 730 additions and 102 deletions

View File

@ -340,6 +340,7 @@ enum RDP_INPUT_DEVICE
/* NT status codes for RDPDR */
#define STATUS_SUCCESS 0x00000000
#define STATUS_NOT_IMPLEMENTED 0x00000001
#define STATUS_PENDING 0x00000103
#define STATUS_NO_MORE_FILES 0x80000006
@ -358,6 +359,7 @@ enum RDP_INPUT_DEVICE
#define STATUS_FILE_IS_A_DIRECTORY 0xc00000ba
#define STATUS_NOT_SUPPORTED 0xc00000bb
#define STATUS_TIMEOUT 0xc0000102
#define STATUS_NOTIFY_ENUM_DIR 0xc000010c
#define STATUS_CANCELLED 0xc0000120
@ -371,6 +373,8 @@ enum RDP_INPUT_DEVICE
#define FILE_DIRECTORY_FILE 0x00000001
#define FILE_NON_DIRECTORY_FILE 0x00000040
#define FILE_COMPLETE_IF_OPLOCKED 0x00000100
#define FILE_DELETE_ON_CLOSE 0x00001000
#define FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000
/* RDP5 disconnect PDU */

194
disk.c
View File

@ -81,6 +81,7 @@
extern RDPDR_DEVICE g_rdpdr_device[];
FILEINFO g_fileinfo[MAX_OPEN_FILES];
BOOL g_notify_stamp = False;
typedef struct
{
@ -90,6 +91,7 @@ typedef struct
char type[256];
} FsInfoType;
static NTSTATUS NotifyInfo(NTHANDLE handle, uint32 info_class, NOTIFY * p);
static time_t
get_create_time(struct stat *st)
@ -328,7 +330,7 @@ disk_create(uint32 device_id, uint32 accessmask, uint32 sharemode, uint32 create
break;
}
//printf("Open: \"%s\" flags: %u, accessmask: %u sharemode: %u create disp: %u\n", path, flags_and_attributes, accessmask, sharemode, create_disposition);
//printf("Open: \"%s\" flags: %X, accessmask: %X sharemode: %X create disp: %X\n", path, flags_and_attributes, accessmask, sharemode, create_disposition);
// Get information about file and set that flag ourselfs
if ((stat(path, &filestat) == 0) && (S_ISDIR(filestat.st_mode)))
@ -424,9 +426,15 @@ disk_create(uint32 device_id, uint32 accessmask, uint32 sharemode, uint32 create
if (dirp)
g_fileinfo[handle].pdir = dirp;
else
g_fileinfo[handle].pdir = NULL;
g_fileinfo[handle].device_id = device_id;
g_fileinfo[handle].flags_and_attributes = flags_and_attributes;
g_fileinfo[handle].accessmask = accessmask;
strncpy(g_fileinfo[handle].path, path, 255);
g_fileinfo[handle].delete_on_close = False;
g_notify_stamp = True;
*phandle = handle;
return STATUS_SUCCESS;
@ -439,14 +447,41 @@ disk_close(NTHANDLE handle)
pfinfo = &(g_fileinfo[handle]);
if (pfinfo->flags_and_attributes & FILE_DIRECTORY_FILE)
g_notify_stamp = True;
rdpdr_abort_io(handle, 0, STATUS_CANCELLED);
if (pfinfo->pdir)
{
closedir(pfinfo->pdir);
//FIXME: Should check exit code
if (closedir(pfinfo->pdir) < 0)
{
perror("closedir");
return STATUS_INVALID_HANDLE;
}
if (pfinfo->delete_on_close)
if (rmdir(pfinfo->path) < 0)
{
perror(pfinfo->path);
return STATUS_ACCESS_DENIED;
}
pfinfo->delete_on_close = False;
}
else
{
close(handle);
if (close(handle) < 0)
{
perror("close");
return STATUS_INVALID_HANDLE;
}
if (pfinfo->delete_on_close)
if (unlink(pfinfo->path) < 0)
{
perror(pfinfo->path);
return STATUS_ACCESS_DENIED;
}
pfinfo->delete_on_close = False;
}
return STATUS_SUCCESS;
@ -477,7 +512,10 @@ disk_read(NTHANDLE handle, uint8 * data, uint32 length, uint32 offset, uint32 *
switch (errno)
{
case EISDIR:
return STATUS_FILE_IS_A_DIRECTORY;
/* Implement 24 Byte directory read ??
with STATUS_NOT_IMPLEMENTED server doesn't read again */
/* return STATUS_FILE_IS_A_DIRECTORY; */
return STATUS_NOT_IMPLEMENTED;
default:
perror("read");
return STATUS_INVALID_PARAMETER;
@ -600,10 +638,9 @@ disk_query_information(NTHANDLE handle, uint32 info_class, STREAM out)
NTSTATUS
disk_set_information(NTHANDLE handle, uint32 info_class, STREAM in, STREAM out)
{
uint32 length, file_attributes, ft_high, ft_low;
uint32 length, file_attributes, ft_high, ft_low, delete_on_close;
char newname[256], fullpath[256];
struct fileinfo *pfinfo;
int mode;
struct stat filestat;
time_t write_time, change_time, access_time, mod_time;
@ -611,6 +648,7 @@ disk_set_information(NTHANDLE handle, uint32 info_class, STREAM in, STREAM out)
struct STATFS_T stat_fs;
pfinfo = &(g_fileinfo[handle]);
g_notify_stamp = True;
switch (info_class)
{
@ -670,7 +708,7 @@ disk_set_information(NTHANDLE handle, uint32 info_class, STREAM in, STREAM out)
printf("FileBasicInformation modification time %s",
ctime(&tvs.modtime));
#endif
if (utime(pfinfo->path, &tvs))
if (utime(pfinfo->path, &tvs) && errno != EPERM)
return STATUS_ACCESS_DENIED;
}
@ -728,25 +766,16 @@ disk_set_information(NTHANDLE handle, uint32 info_class, STREAM in, STREAM out)
FileDispositionInformation requests with
DeleteFile set to FALSE should unschedule
the delete. See
http://www.osronline.com/article.cfm?article=245. Currently,
we are deleting the file immediately. I
guess this is a FIXME. */
http://www.osronline.com/article.cfm?article=245. */
//in_uint32_le(in, delete_on_close);
in_uint32_le(in, delete_on_close);
/* Make sure we close the file before
unlinking it. Not doing so would trigger
silly-delete if using NFS, which might fail
on FAT floppies, for example. */
disk_close(handle);
if ((pfinfo->flags_and_attributes & FILE_DIRECTORY_FILE)) // remove a directory
if (delete_on_close ||
(pfinfo->
accessmask & (FILE_DELETE_ON_CLOSE | FILE_COMPLETE_IF_OPLOCKED)))
{
if (rmdir(pfinfo->path) < 0)
return STATUS_ACCESS_DENIED;
pfinfo->delete_on_close = True;
}
else if (unlink(pfinfo->path) < 0) // unlink a file
return STATUS_ACCESS_DENIED;
break;
@ -763,7 +792,7 @@ disk_set_information(NTHANDLE handle, uint32 info_class, STREAM in, STREAM out)
/* prevents start of writing if not enough space left on device */
if (STATFS_FN(g_rdpdr_device[pfinfo->device_id].local_path, &stat_fs) == 0)
if (stat_fs.f_bsize * stat_fs.f_bfree < length)
if (stat_fs.f_bfree * stat_fs.f_bsize < length)
return STATUS_DISK_FULL;
if (ftruncate_growable(handle, length) != 0)
@ -780,20 +809,129 @@ disk_set_information(NTHANDLE handle, uint32 info_class, STREAM in, STREAM out)
return STATUS_SUCCESS;
}
NTSTATUS
disk_check_notify(NTHANDLE handle)
{
struct fileinfo *pfinfo;
NTSTATUS status = STATUS_PENDING;
NOTIFY notify;
pfinfo = &(g_fileinfo[handle]);
if (!pfinfo->pdir)
return STATUS_INVALID_DEVICE_REQUEST;
status = NotifyInfo(handle, pfinfo->info_class, &notify);
if (status != STATUS_PENDING)
return status;
if (memcmp(&pfinfo->notify, &notify, sizeof(NOTIFY)))
{
//printf("disk_check_notify found changed event\n");
memcpy(&pfinfo->notify, &notify, sizeof(NOTIFY));
status = STATUS_NOTIFY_ENUM_DIR;
}
return status;
}
NTSTATUS
disk_create_notify(NTHANDLE handle, uint32 info_class)
{
struct fileinfo *pfinfo;
NTSTATUS ret = STATUS_PENDING;
/* printf("start disk_create_notify info_class %X\n", info_class); */
pfinfo = &(g_fileinfo[handle]);
pfinfo->info_class = info_class;
ret = NotifyInfo(handle, info_class, &pfinfo->notify);
if (info_class & 0x1000)
{ /* ???? */
if (ret == STATUS_PENDING)
return STATUS_SUCCESS;
}
/* printf("disk_create_notify: num_entries %d\n", pfinfo->notify.num_entries); */
return ret;
}
static NTSTATUS
NotifyInfo(NTHANDLE handle, uint32 info_class, NOTIFY * p)
{
struct fileinfo *pfinfo;
struct stat buf;
struct dirent *dp;
char *fullname;
DIR *dpr;
pfinfo = &(g_fileinfo[handle]);
if (fstat(handle, &buf) < 0)
{
perror("NotifyInfo");
return STATUS_ACCESS_DENIED;
}
p->modify_time = buf.st_mtime;
p->status_time = buf.st_ctime;
p->num_entries = 0;
p->total_time = 0;
dpr = opendir(pfinfo->path);
if (!dpr)
{
perror("NotifyInfo");
return STATUS_ACCESS_DENIED;
}
while ((dp = readdir(dpr)))
{
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
continue;
p->num_entries++;
fullname = xmalloc(strlen(pfinfo->path) + strlen(dp->d_name) + 2);
sprintf(fullname, "%s/%s", pfinfo->path, dp->d_name);
if (!stat(fullname, &buf))
{
p->total_time += (buf.st_mtime + buf.st_ctime);
}
xfree(fullname);
}
closedir(dpr);
return STATUS_PENDING;
}
static FsInfoType *
FsVolumeInfo(char *fpath)
{
#ifdef HAVE_MNTENT_H
FILE *fdfs;
struct mntent *e;
static FsInfoType info;
#ifdef HAVE_MNTENT_H
struct mntent *e;
#endif
/* initialize */
memset(&info, 0, sizeof(info));
strcpy(info.label, "RDESKTOP");
strcpy(info.type, "RDPFS");
#ifdef HAVE_MNTENT_H
fdfs = setmntent(MNTENT_PATH, "r");
if (!fdfs)
return &info;
@ -836,8 +974,6 @@ FsVolumeInfo(char *fpath)
}
endmntent(fdfs);
#else
static FsInfoType info;
/* initialize */
memset(&info, 0, sizeof(info));
strcpy(info.label, "RDESKTOP");

View File

@ -32,6 +32,8 @@ NTSTATUS disk_query_information(NTHANDLE handle, uint32 info_class, STREAM out);
NTSTATUS disk_set_information(NTHANDLE handle, uint32 info_class, STREAM in, STREAM out);
NTSTATUS disk_query_volume_information(NTHANDLE handle, uint32 info_class, STREAM out);
NTSTATUS disk_query_directory(NTHANDLE handle, uint32 info_class, char *pattern, STREAM out);
NTSTATUS disk_create_notify(NTHANDLE handle, uint32 info_class);
NTSTATUS disk_check_notify(NTHANDLE handle);
/* mppc.c */
int mppc_expand(uint8 * data, uint32 clen, uint8 ctype, uint32 * roff, uint32 * rlen);
/* ewmhints.c */
@ -144,6 +146,7 @@ void sec_disconnect(void);
/* serial.c */
int serial_enum_devices(uint32 * id, char *optarg);
BOOL serial_get_timeout(NTHANDLE handle, uint32 length, uint32 * timeout, uint32 * itv_timeout);
BOOL serial_get_event(NTHANDLE handle, uint32 * result);
/* tcp.c */
STREAM tcp_init(uint32 maxlen);
void tcp_send(STREAM s);

193
rdpdr.c
View File

@ -61,6 +61,7 @@ extern DEVICE_FNS printer_fns;
extern DEVICE_FNS parallel_fns;
extern DEVICE_FNS disk_fns;
extern FILEINFO g_fileinfo[];
extern BOOL g_notify_stamp;
static VCHANNEL *rdpdr_channel;
@ -323,8 +324,11 @@ rdpdr_send_completion(uint32 device, uint32 id, uint32 status, uint32 result, ui
out_uint32_le(s, result);
out_uint8p(s, buffer, length);
s_mark_end(s);
/* JIF
hexdump(s->channel_hdr + 8, s->end - s->channel_hdr - 8); */
/* JIF */
#ifdef WITH_DEBUG_RDP5
printf("--> rdpdr_send_completion\n");
//hexdump(s->channel_hdr + 8, s->end - s->channel_hdr - 8);
#endif
channel_send(s, rdpdr_channel);
}
@ -623,15 +627,25 @@ rdpdr_process_irp(STREAM s)
case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
/* JIF
unimpl("IRP major=0x%x minor=0x%x: IRP_MN_NOTIFY_CHANGE_DIRECTORY\n", major, minor); */
status = STATUS_PENDING; // Don't send completion packet
unimpl("IRP major=0x%x minor=0x%x: IRP_MN_NOTIFY_CHANGE_DIRECTORY\n", major, minor); */
in_uint32_le(s, info_level); // notify mask
g_notify_stamp = True;
status = disk_create_notify(file, info_level);
result = 0;
if (status == STATUS_PENDING)
add_async_iorequest(device, file, id, major, length,
fns, 0, 0, NULL, 0);
break;
default:
status = STATUS_INVALID_PARAMETER;
/* JIF
unimpl("IRP major=0x%x minor=0x%x\n", major, minor); */
/* JIF */
unimpl("IRP major=0x%x minor=0x%x\n", major, minor);
}
break;
@ -659,6 +673,17 @@ rdpdr_process_irp(STREAM s)
out.size = sizeof(buffer);
status = fns->device_control(file, request, s, &out);
result = buffer_len = out.p - out.data;
/* Serial SERIAL_WAIT_ON_MASK */
if (status == STATUS_PENDING)
{
if (add_async_iorequest
(device, file, id, major, length, fns, 0, 0, NULL, 0))
{
status = STATUS_PENDING;
break;
}
}
break;
@ -827,25 +852,34 @@ rdpdr_add_fds(int *n, fd_set * rfds, fd_set * wfds, struct timeval *tv, BOOL * t
reconnecting. FIXME: Real
support for reconnects. */
if ((read(iorq->fd, &c, 0) != 0) && (errno == EBADF))
break;
FD_SET(iorq->fd, rfds);
*n = MAX(*n, iorq->fd);
// Check if io request timeout is smaller than current (but not 0).
/* Check if io request timeout is smaller than current (but not 0). */
if (iorq->timeout
&& (select_timeout == 0
|| iorq->timeout < select_timeout))
{
// Set new timeout
/* Set new timeout */
select_timeout = iorq->timeout;
g_min_timeout_fd = iorq->fd; /* Remember fd */
tv->tv_sec = select_timeout / 1000;
tv->tv_usec = (select_timeout % 1000) * 1000;
*timeout = True;
break;
}
if (iorq->itv_timeout && iorq->partial_len > 0
&& (select_timeout == 0
|| iorq->itv_timeout < select_timeout))
{
/* Set new timeout */
select_timeout = iorq->itv_timeout;
g_min_timeout_fd = iorq->fd; /* Remember fd */
tv->tv_sec = select_timeout / 1000;
tv->tv_usec = (select_timeout % 1000) * 1000;
*timeout = True;
break;
}
break;
case IRP_MJ_WRITE:
@ -857,6 +891,11 @@ rdpdr_add_fds(int *n, fd_set * rfds, fd_set * wfds, struct timeval *tv, BOOL * t
*n = MAX(*n, iorq->fd);
break;
case IRP_MJ_DEVICE_CONTROL:
if (select_timeout > 5)
select_timeout = 5; /* serial event queue */
break;
}
}
@ -891,7 +930,7 @@ rdpdr_remove_iorequest(struct async_iorequest *prev, struct async_iorequest *ior
/* Check if select() returned with one of the rdpdr file descriptors, and complete io if it did */
void
rdpdr_check_fds(fd_set * rfds, fd_set * wfds, BOOL timed_out)
_rdpdr_check_fds(fd_set * rfds, fd_set * wfds, BOOL timed_out)
{
NTSTATUS status;
uint32 result = 0;
@ -899,9 +938,53 @@ rdpdr_check_fds(fd_set * rfds, fd_set * wfds, BOOL timed_out)
struct async_iorequest *iorq;
struct async_iorequest *prev;
uint32 req_size = 0;
uint32 buffer_len;
struct stream out;
uint8 *buffer = NULL;
if (timed_out)
{
/* check serial iv_timeout */
iorq = g_iorequest;
prev = NULL;
while (iorq != NULL)
{
if (iorq->fd == g_min_timeout_fd)
{
if ((iorq->partial_len > 0) &&
(g_rdpdr_device[iorq->device].device_type ==
DEVICE_TYPE_SERIAL))
{
/* iv_timeout between 2 chars, send partial_len */
//printf("RDPDR: IVT total %u bytes read of %u\n", iorq->partial_len, iorq->length);
rdpdr_send_completion(iorq->device,
iorq->id, STATUS_SUCCESS,
iorq->partial_len,
iorq->buffer, iorq->partial_len);
iorq = rdpdr_remove_iorequest(prev, iorq);
return;
}
else
{
break;
}
}
else
{
break;
}
prev = iorq;
if (iorq)
iorq = iorq->next;
}
rdpdr_abort_io(g_min_timeout_fd, 0, STATUS_TIMEOUT);
return;
}
@ -929,7 +1012,7 @@ rdpdr_check_fds(fd_set * rfds, fd_set * wfds, BOOL timed_out)
iorq->buffer + iorq->partial_len,
req_size, iorq->offset, &result);
if (result > 0)
if ((long) result > 0)
{
iorq->partial_len += result;
iorq->offset += result;
@ -940,8 +1023,7 @@ rdpdr_check_fds(fd_set * rfds, fd_set * wfds, BOOL timed_out)
/* only delete link if all data has been transfered */
/* or if result was 0 and status success - EOF */
if ((iorq->partial_len == iorq->length) ||
(g_rdpdr_device[iorq->device].device_type ==
DEVICE_TYPE_SERIAL) || (result == 0))
(result == 0))
{
#if WITH_DEBUG_RDP5
DEBUG(("RDPDR: AIO total %u bytes read of %u\n", iorq->partial_len, iorq->length));
@ -972,7 +1054,7 @@ rdpdr_check_fds(fd_set * rfds, fd_set * wfds, BOOL timed_out)
iorq->partial_len, req_size,
iorq->offset, &result);
if (result > 0)
if ((long) result > 0)
{
iorq->partial_len += result;
iorq->offset += result;
@ -998,6 +1080,23 @@ rdpdr_check_fds(fd_set * rfds, fd_set * wfds, BOOL timed_out)
iorq = rdpdr_remove_iorequest(prev, iorq);
}
}
break;
case IRP_MJ_DEVICE_CONTROL:
if (serial_get_event(iorq->fd, &result))
{
buffer = (uint8 *) xrealloc((void *) buffer, 0x14);
out.data = out.p = buffer;
out.size = sizeof(buffer);
out_uint32_le(&out, result);
result = buffer_len = out.p - out.data;
status = STATUS_SUCCESS;
rdpdr_send_completion(iorq->device, iorq->id,
status, result, buffer,
buffer_len);
xfree(buffer);
iorq = rdpdr_remove_iorequest(prev, iorq);
}
break;
}
@ -1007,8 +1106,68 @@ rdpdr_check_fds(fd_set * rfds, fd_set * wfds, BOOL timed_out)
iorq = iorq->next;
}
/* Check notify */
iorq = g_iorequest;
prev = NULL;
while (iorq != NULL)
{
if (iorq->fd != 0)
{
switch (iorq->major)
{
case IRP_MJ_DIRECTORY_CONTROL:
if (g_rdpdr_device[iorq->device].device_type ==
DEVICE_TYPE_DISK)
{
if (g_notify_stamp)
{
g_notify_stamp = False;
status = disk_check_notify(iorq->fd);
if (status != STATUS_PENDING)
{
rdpdr_send_completion(iorq->device,
iorq->id,
status, 0,
NULL, 0);
iorq = rdpdr_remove_iorequest(prev,
iorq);
}
}
}
break;
}
}
prev = iorq;
if (iorq)
iorq = iorq->next;
}
}
void
rdpdr_check_fds(fd_set * rfds, fd_set * wfds, BOOL timed_out)
{
fd_set dummy;
FD_ZERO(&dummy);
/* fist check event queue only,
any serial wait event must be done before read block will be sent
*/
_rdpdr_check_fds(&dummy, &dummy, False);
_rdpdr_check_fds(rfds, wfds, timed_out);
}
/* Abort a pending io request for a given handle and major */
BOOL
rdpdr_abort_io(uint32 fd, uint32 major, NTSTATUS status)

413
serial.c
View File

@ -5,6 +5,12 @@
#include <sys/ioctl.h>
#include "rdesktop.h"
#ifdef WITH_DEBUG_SERIAL
#define DEBUG_SERIAL(args) printf args;
#else
#define DEBUG_SERIAL(args)
#endif
#define FILE_DEVICE_SERIAL_PORT 0x1b
#define SERIAL_SET_BAUD_RATE 1
@ -75,15 +81,39 @@
#define SERIAL_EV_EVENT2 0x1000 // Provider specific event 2
/* Modem Status */
#define SERIAL_MS_DTR 0x01
#define SERIAL_MS_RTS 0x02
#define SERIAL_MS_CTS 0x10
#define SERIAL_MS_DSR 0x20
#define SERIAL_MS_RNG 0x40
#define SERIAL_MS_CAR 0x80
/* Handflow */
#define SERIAL_DTR_CONTROL 0x01
#define SERIAL_CTS_HANDSHAKE 0x08
#define SERIAL_ERROR_ABORT 0x80000000
#define SERIAL_XON_HANDSHAKE 0x01
#define SERIAL_XOFF_HANDSHAKE 0x02
#define SERIAL_DSR_SENSITIVITY 0x40
#define SERIAL_CHAR_EOF 0
#define SERIAL_CHAR_ERROR 1
#define SERIAL_CHAR_BREAK 2
#define SERIAL_CHAR_EVENT 3
#define SERIAL_CHAR_XON 4
#define SERIAL_CHAR_XOFF 5
#ifndef CRTSCTS
#define CRTSCTS 0
#endif
/* FIONREAD should really do the same thing as TIOCINQ, where it is
* not available */
#ifndef TIOCINQ
#include <sys/filio.h>
#define TIOCINQ FIONREAD
#endif
extern RDPDR_DEVICE g_rdpdr_device[];
@ -188,9 +218,19 @@ get_termios(SERIAL_DEVICE * pser_inf, NTHANDLE serial_fd)
case B115200:
pser_inf->baud_rate = 115200;
break;
#endif
#ifdef B230400
case B230400:
pser_inf->baud_rate = 230400;
break;
#endif
#ifdef B460800
case B460800:
pser_inf->baud_rate = 460800;
break;
#endif
default:
pser_inf->baud_rate = 0;
pser_inf->baud_rate = 9600;
break;
}
@ -218,7 +258,27 @@ get_termios(SERIAL_DEVICE * pser_inf, NTHANDLE serial_fd)
break;
}
pser_inf->rts = (ptermios->c_cflag & CRTSCTS) ? 1 : 0;
if (ptermios->c_cflag & CRTSCTS)
{
pser_inf->control = SERIAL_DTR_CONTROL | SERIAL_CTS_HANDSHAKE | SERIAL_ERROR_ABORT;
}
else
{
pser_inf->control = SERIAL_DTR_CONTROL | SERIAL_ERROR_ABORT;
}
pser_inf->xonoff = SERIAL_DSR_SENSITIVITY;
if (ptermios->c_iflag & IXON)
pser_inf->xonoff |= SERIAL_XON_HANDSHAKE;
if (ptermios->c_iflag & IXOFF)
pser_inf->xonoff |= SERIAL_XOFF_HANDSHAKE;
pser_inf->chars[SERIAL_CHAR_XON] = ptermios->c_cc[VSTART];
pser_inf->chars[SERIAL_CHAR_XOFF] = ptermios->c_cc[VSTOP];
pser_inf->chars[SERIAL_CHAR_EOF] = ptermios->c_cc[VEOF];
pser_inf->chars[SERIAL_CHAR_BREAK] = ptermios->c_cc[VINTR];
pser_inf->chars[SERIAL_CHAR_ERROR] = ptermios->c_cc[VKILL];
return True;
}
@ -309,16 +369,31 @@ set_termios(SERIAL_DEVICE * pser_inf, NTHANDLE serial_fd)
case 115200:
speed = B115200;
break;
#endif
#ifdef B230400
case 230400:
speed = B115200;
break;
#endif
#ifdef B460800
case 460800:
speed = B115200;
break;
#endif
default:
speed = B0;
speed = B9600;
break;
}
#if 0
/* on systems with separate ispeed and ospeed, we can remember the speed
in ispeed while changing DTR with ospeed */
cfsetispeed(pser_inf->ptermios, speed);
cfsetospeed(pser_inf->ptermios, pser_inf->dtr ? speed : 0);
#endif
ptermios->c_cflag &= ~CBAUD;
ptermios->c_cflag |= speed;
ptermios->c_cflag &= ~(CSTOPB | PARENB | PARODD | CSIZE | CRTSCTS);
switch (pser_inf->stop_bits)
@ -326,6 +401,9 @@ set_termios(SERIAL_DEVICE * pser_inf, NTHANDLE serial_fd)
case STOP_BITS_2:
ptermios->c_cflag |= CSTOPB;
break;
default:
ptermios->c_cflag &= ~CSTOPB;
break;
}
switch (pser_inf->parity)
@ -336,6 +414,9 @@ set_termios(SERIAL_DEVICE * pser_inf, NTHANDLE serial_fd)
case ODD_PARITY:
ptermios->c_cflag |= PARENB | PARODD;
break;
case NO_PARITY:
ptermios->c_cflag &= ~(PARENB | PARODD);
break;
}
switch (pser_inf->word_length)
@ -354,8 +435,43 @@ set_termios(SERIAL_DEVICE * pser_inf, NTHANDLE serial_fd)
break;
}
#if 0
if (pser_inf->rts)
ptermios->c_cflag |= CRTSCTS;
else
ptermios->c_cflag &= ~CRTSCTS;
#endif
if (pser_inf->control & SERIAL_CTS_HANDSHAKE)
{
ptermios->c_cflag |= CRTSCTS;
}
else
{
ptermios->c_cflag &= ~CRTSCTS;
}
if (pser_inf->xonoff & SERIAL_XON_HANDSHAKE)
{
ptermios->c_iflag |= IXON | IMAXBEL;
}
if (pser_inf->xonoff & SERIAL_XOFF_HANDSHAKE)
{
ptermios->c_iflag |= IXOFF | IMAXBEL;
}
if ((pser_inf->xonoff & (SERIAL_XOFF_HANDSHAKE | SERIAL_XON_HANDSHAKE)) == 0)
{
ptermios->c_iflag &= ~IXON;
ptermios->c_iflag &= ~IXOFF;
}
ptermios->c_cc[VSTART] = pser_inf->chars[SERIAL_CHAR_XON];
ptermios->c_cc[VSTOP] = pser_inf->chars[SERIAL_CHAR_XOFF];
ptermios->c_cc[VEOF] = pser_inf->chars[SERIAL_CHAR_EOF];
ptermios->c_cc[VINTR] = pser_inf->chars[SERIAL_CHAR_BREAK];
ptermios->c_cc[VKILL] = pser_inf->chars[SERIAL_CHAR_ERROR];
tcsetattr(serial_fd, TCSANOW, ptermios);
}
@ -434,31 +550,31 @@ serial_create(uint32 device_id, uint32 access, uint32 share_mode, uint32 disposi
g_rdpdr_device[device_id].handle = serial_fd;
/* some sane information */
printf("INFO: SERIAL %s to %s\nINFO: speed %u baud, stop bits %u, parity %u, word length %u bits, dtr %u, rts %u\n", g_rdpdr_device[device_id].name, g_rdpdr_device[device_id].local_path, pser_inf->baud_rate, pser_inf->stop_bits, pser_inf->parity, pser_inf->word_length, pser_inf->dtr, pser_inf->rts);
DEBUG_SERIAL(("INFO: SERIAL %s to %s\nINFO: speed %u baud, stop bits %u, parity %u, word length %u bits, dtr %u, rts %u\n", g_rdpdr_device[device_id].name, g_rdpdr_device[device_id].local_path, pser_inf->baud_rate, pser_inf->stop_bits, pser_inf->parity, pser_inf->word_length, pser_inf->dtr, pser_inf->rts));
printf("INFO: use stty to change settings\n");
/* ptermios->c_cflag = B115200 | CRTSCTS | CS8 | CLOCAL | CREAD;
ptermios->c_cflag |= CREAD;
ptermios->c_lflag |= ICANON;
ptermios->c_iflag = IGNPAR | ICRNL;
tcsetattr(serial_fd, TCSANOW, ptermios);
*/
pser_inf->ptermios->c_iflag &=
~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
pser_inf->ptermios->c_oflag &= ~OPOST;
pser_inf->ptermios->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
pser_inf->ptermios->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN | XCASE);
pser_inf->ptermios->c_cflag &= ~(CSIZE | PARENB);
pser_inf->ptermios->c_cflag |= CS8;
tcsetattr(serial_fd, TCSANOW, pser_inf->ptermios);
pser_inf->event_txempty = 0;
pser_inf->event_cts = 0;
pser_inf->event_dsr = 0;
pser_inf->event_rlsd = 0;
pser_inf->event_pending = 0;
*handle = serial_fd;
/* all read and writes should be non blocking */
if (fcntl(*handle, F_SETFL, O_NONBLOCK) == -1)
perror("fcntl");
pser_inf->read_total_timeout_constant = 5;
return STATUS_SUCCESS;
}
@ -468,6 +584,8 @@ serial_close(NTHANDLE handle)
int i = get_device_index(handle);
if (i >= 0)
g_rdpdr_device[i].handle = 0;
rdpdr_abort_io(handle, 0, STATUS_TIMEOUT);
close(handle);
return STATUS_SUCCESS;
}
@ -478,6 +596,10 @@ serial_read(NTHANDLE handle, uint8 * data, uint32 length, uint32 offset, uint32
long timeout;
SERIAL_DEVICE *pser_inf;
struct termios *ptermios;
#ifdef WITH_DEBUG_SERIAL
int bytes_inqueue;
#endif
timeout = 90;
pser_inf = get_serial_info(handle);
@ -511,10 +633,18 @@ serial_read(NTHANDLE handle, uint8 * data, uint32 length, uint32 offset, uint32
}
tcsetattr(handle, TCSANOW, ptermios);
#ifdef WITH_DEBUG_SERIAL
ioctl(handle, TIOCINQ, &bytes_inqueue);
DEBUG_SERIAL(("serial_read inqueue: %d expected %d\n", bytes_inqueue, length));
#endif
*result = read(handle, data, length);
//hexdump(data, *read);
#ifdef WITH_DEBUG_SERIAL
DEBUG_SERIAL(("serial_read Bytes %d\n", *result));
if (*result > 0)
hexdump(data, *result);
#endif
return STATUS_SUCCESS;
}
@ -522,16 +652,24 @@ serial_read(NTHANDLE handle, uint8 * data, uint32 length, uint32 offset, uint32
static NTSTATUS
serial_write(NTHANDLE handle, uint8 * data, uint32 length, uint32 offset, uint32 * result)
{
SERIAL_DEVICE *pser_inf;
pser_inf = get_serial_info(handle);
*result = write(handle, data, length);
if (*result > 0)
pser_inf->event_txempty = *result;
DEBUG_SERIAL(("serial_write length %d, offset %d result %d\n", length, offset, *result));
return STATUS_SUCCESS;
}
static NTSTATUS
serial_device_control(NTHANDLE handle, uint32 request, STREAM in, STREAM out)
{
#if 0
int flush_mask, purge_mask;
#endif
uint32 result, modemstate;
uint8 immediate;
SERIAL_DEVICE *pser_inf;
@ -547,81 +685,134 @@ serial_device_control(NTHANDLE handle, uint32 request, STREAM in, STREAM out)
request >>= 2;
request &= 0xfff;
printf("SERIAL IOCTL %d\n", request);
switch (request)
{
case SERIAL_SET_BAUD_RATE:
in_uint32_le(in, pser_inf->baud_rate);
set_termios(pser_inf, handle);
DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_BAUD_RATE %d\n", pser_inf->baud_rate));
break;
case SERIAL_GET_BAUD_RATE:
out_uint32_le(out, pser_inf->baud_rate);
DEBUG_SERIAL(("serial_ioctl -> SERIAL_GET_BAUD_RATE %d\n", pser_inf->baud_rate));
break;
case SERIAL_SET_QUEUE_SIZE:
in_uint32_le(in, pser_inf->queue_in_size);
in_uint32_le(in, pser_inf->queue_out_size);
DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_QUEUE_SIZE in %d out %d\n",
pser_inf->queue_in_size, pser_inf->queue_out_size));
break;
case SERIAL_SET_LINE_CONTROL:
in_uint8(in, pser_inf->stop_bits);
in_uint8(in, pser_inf->parity);
in_uint8(in, pser_inf->word_length);
set_termios(pser_inf, handle);
DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_LINE_CONTROL stop %d parity %d word %d\n",
pser_inf->stop_bits, pser_inf->parity, pser_inf->word_length));
break;
case SERIAL_GET_LINE_CONTROL:
DEBUG_SERIAL(("serial_ioctl -> SERIAL_GET_LINE_CONTROL\n"));
out_uint8(out, pser_inf->stop_bits);
out_uint8(out, pser_inf->parity);
out_uint8(out, pser_inf->word_length);
break;
case SERIAL_IMMEDIATE_CHAR:
DEBUG_SERIAL(("serial_ioctl -> SERIAL_IMMEDIATE_CHAR\n"));
in_uint8(in, immediate);
serial_write(handle, &immediate, 1, 0, &result);
break;
case SERIAL_CONFIG_SIZE:
DEBUG_SERIAL(("serial_ioctl -> SERIAL_CONFIG_SIZE\n"));
out_uint32_le(out, 0);
break;
case SERIAL_GET_CHARS:
out_uint8s(out, 6);
DEBUG_SERIAL(("serial_ioctl -> SERIAL_GET_CHARS\n"));
out_uint8a(out, pser_inf->chars, 6);
break;
case SERIAL_SET_CHARS:
in_uint8s(in, 6);
DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_CHARS\n"));
in_uint8a(in, pser_inf->chars, 6);
#ifdef WITH_DEBUG_SERIAL
hexdump(pser_inf->chars, 6);
#endif
set_termios(pser_inf, handle);
break;
case SERIAL_GET_HANDFLOW:
out_uint32_le(out, 0);
out_uint32_le(out, 3); /* Xon/Xoff */
out_uint32_le(out, 0);
out_uint32_le(out, 0);
DEBUG_SERIAL(("serial_ioctl -> SERIAL_GET_HANDFLOW\n"));
get_termios(pser_inf, handle);
out_uint32_le(out, pser_inf->control);
out_uint32_le(out, pser_inf->xonoff); /* Xon/Xoff */
out_uint32_le(out, pser_inf->onlimit);
out_uint32_le(out, pser_inf->offlimit);
break;
case SERIAL_SET_HANDFLOW:
in_uint8s(in, 16);
in_uint32_le(in, pser_inf->control);
in_uint32_le(in, pser_inf->xonoff);
in_uint32_le(in, pser_inf->onlimit);
in_uint32_le(in, pser_inf->offlimit);
DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_HANDFLOW %x %x %x %x\n",
pser_inf->control, pser_inf->xonoff, pser_inf->onlimit,
pser_inf->onlimit));
set_termios(pser_inf, handle);
break;
case SERIAL_SET_TIMEOUTS:
in_uint8s(in, 20);
in_uint32(in, pser_inf->read_interval_timeout);
in_uint32(in, pser_inf->read_total_timeout_multiplier);
in_uint32(in, pser_inf->read_total_timeout_constant);
in_uint32(in, pser_inf->write_total_timeout_multiplier);
in_uint32(in, pser_inf->write_total_timeout_constant);
DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_TIMEOUTS read timeout %d %d %d\n",
pser_inf->read_interval_timeout,
pser_inf->read_total_timeout_multiplier,
pser_inf->read_total_timeout_constant));
break;
case SERIAL_GET_TIMEOUTS:
out_uint8s(out, 20);
DEBUG_SERIAL(("serial_ioctl -> SERIAL_GET_TIMEOUTS read timeout %d %d %d\n",
pser_inf->read_interval_timeout,
pser_inf->read_total_timeout_multiplier,
pser_inf->read_total_timeout_constant));
out_uint32(out, pser_inf->read_interval_timeout);
out_uint32(out, pser_inf->read_total_timeout_multiplier);
out_uint32(out, pser_inf->read_total_timeout_constant);
out_uint32(out, pser_inf->write_total_timeout_multiplier);
out_uint32(out, pser_inf->write_total_timeout_constant);
break;
case SERIAL_GET_WAIT_MASK:
out_uint32(out, pser_inf->wait_mask);
DEBUG_SERIAL(("serial_ioctl -> SERIAL_GET_WAIT_MASK %X\n", pser_inf->wait_mask);
out_uint32(out, pser_inf->wait_mask));
break;
case SERIAL_SET_WAIT_MASK:
in_uint32(in, pser_inf->wait_mask);
DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_WAIT_MASK %X\n", pser_inf->wait_mask));
break;
case SERIAL_SET_DTR:
DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_DTR\n"));
ioctl(handle, TIOCMGET, &result);
result |= TIOCM_DTR;
ioctl(handle, TIOCMSET, &result);
pser_inf->dtr = 1;
set_termios(pser_inf, handle);
break;
case SERIAL_CLR_DTR:
DEBUG_SERIAL(("serial_ioctl -> SERIAL_CLR_DTR\n"));
ioctl(handle, TIOCMGET, &result);
result &= ~TIOCM_DTR;
ioctl(handle, TIOCMSET, &result);
pser_inf->dtr = 0;
set_termios(pser_inf, handle);
break;
case SERIAL_SET_RTS:
DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_RTS\n"));
ioctl(handle, TIOCMGET, &result);
result |= TIOCM_RTS;
ioctl(handle, TIOCMSET, &result);
pser_inf->rts = 1;
set_termios(pser_inf, handle);
break;
case SERIAL_CLR_RTS:
DEBUG_SERIAL(("serial_ioctl -> SERIAL_CLR_RTS\n"));
ioctl(handle, TIOCMGET, &result);
result &= ~TIOCM_RTS;
ioctl(handle, TIOCMSET, &result);
pser_inf->rts = 0;
set_termios(pser_inf, handle);
break;
case SERIAL_GET_MODEMSTATUS:
modemstate = 0;
@ -635,46 +826,76 @@ serial_device_control(NTHANDLE handle, uint32 request, STREAM in, STREAM out)
modemstate |= SERIAL_MS_RNG;
if (result & TIOCM_CAR)
modemstate |= SERIAL_MS_CAR;
if (result & TIOCM_DTR)
modemstate |= SERIAL_MS_DTR;
if (result & TIOCM_RTS)
modemstate |= SERIAL_MS_RTS;
#endif
DEBUG_SERIAL(("serial_ioctl -> SERIAL_GET_MODEMSTATUS %X\n", modemstate));
out_uint32_le(out, modemstate);
break;
case SERIAL_GET_COMMSTATUS:
out_uint32_le(out, 0); /* Errors */
out_uint32_le(out, 0); /* Hold reasons */
out_uint32_le(out, 0); /* Amount in in queue */
out_uint32_le(out, 0); /* Amount in out queue */
result = 0;
ioctl(handle, TIOCINQ, &result);
out_uint32_le(out, result); /* Amount in in queue */
if (result)
DEBUG_SERIAL(("serial_ioctl -> SERIAL_GET_COMMSTATUS in queue %d\n", result));
result = 0;
ioctl(handle, TIOCOUTQ, &result);
out_uint32_le(out, result); /* Amount in out queue */
if (result)
DEBUG_SERIAL(("serial_ioctl -> SERIAL_GET_COMMSTATUS out queue %d\n", result));
out_uint8(out, 0); /* EofReceived */
out_uint8(out, 0); /* WaitForImmediate */
break;
#if 0
case SERIAL_PURGE:
printf("SERIAL_PURGE\n");
in_uint32(in, purge_mask);
if (purge_mask & 0x04)
DEBUG_SERIAL(("serial_ioctl -> SERIAL_PURGE purge_mask %X\n", purge_mask));
flush_mask = 0;
if (purge_mask & SERIAL_PURGE_TXCLEAR)
flush_mask |= TCOFLUSH;
if (purge_mask & 0x08)
if (purge_mask & SERIAL_PURGE_RXCLEAR)
flush_mask |= TCIFLUSH;
if (flush_mask != 0)
tcflush(handle, flush_mask);
if (purge_mask & 0x01)
if (purge_mask & SERIAL_PURGE_TXABORT)
rdpdr_abort_io(handle, 4, STATUS_CANCELLED);
if (purge_mask & 0x02)
if (purge_mask & SERIAL_PURGE_RXABORT)
rdpdr_abort_io(handle, 3, STATUS_CANCELLED);
break;
case SERIAL_WAIT_ON_MASK:
/* XXX implement me */
out_uint32_le(out, pser_inf->wait_mask);
DEBUG_SERIAL(("serial_ioctl -> SERIAL_WAIT_ON_MASK %X\n", pser_inf->wait_mask));
pser_inf->event_pending = 1;
if (serial_get_event(handle, &result))
{
DEBUG_SERIAL(("WAIT end event = %x\n", result));
out_uint32_le(out, result);
break;
}
return STATUS_PENDING;
break;
case SERIAL_SET_BREAK_ON:
tcsendbreak(serial_fd, 0);
DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_BREAK_ON\n"));
tcsendbreak(handle, 0);
break;
case SERIAL_RESET_DEVICE:
case SERIAL_SET_BREAK_OFF:
case SERIAL_SET_XOFF:
case SERIAL_SET_XON:
/* ignore */
DEBUG_SERIAL(("serial_ioctl -> SERIAL_RESET_DEVICE\n"));
break;
case SERIAL_SET_BREAK_OFF:
DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_BREAK_OFF\n"));
break;
case SERIAL_SET_XOFF:
DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_XOFF\n"));
break;
case SERIAL_SET_XON:
DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_XON\n"));
tcflow(handle, TCION);
break;
#endif
default:
unimpl("SERIAL IOCTL %d\n", request);
return STATUS_INVALID_PARAMETER;
@ -683,6 +904,100 @@ serial_device_control(NTHANDLE handle, uint32 request, STREAM in, STREAM out)
return STATUS_SUCCESS;
}
BOOL
serial_get_event(NTHANDLE handle, uint32 * result)
{
int index;
SERIAL_DEVICE *pser_inf;
int bytes;
BOOL ret = False;
*result = 0;
index = get_device_index(handle);
if (index < 0)
return False;
pser_inf = (SERIAL_DEVICE *) g_rdpdr_device[index].pdevice_data;
ioctl(handle, TIOCINQ, &bytes);
if (bytes > 0)
{
DEBUG_SERIAL(("serial_get_event Bytes %d\n", bytes));
if (bytes > pser_inf->event_rlsd)
{
pser_inf->event_rlsd = bytes;
if (pser_inf->wait_mask & SERIAL_EV_RLSD)
{
DEBUG_SERIAL(("Event -> SERIAL_EV_RLSD \n"));
*result |= SERIAL_EV_RLSD;
ret = True;
}
}
if ((bytes > 1) && (pser_inf->wait_mask & SERIAL_EV_RXFLAG))
{
DEBUG_SERIAL(("Event -> SERIAL_EV_RXFLAG Bytes %d\n", bytes));
*result |= SERIAL_EV_RXFLAG;
ret = True;
}
if ((pser_inf->wait_mask & SERIAL_EV_RXCHAR))
{
DEBUG_SERIAL(("Event -> SERIAL_EV_RXCHAR Bytes %d\n", bytes));
*result |= SERIAL_EV_RXCHAR;
ret = True;
}
}
else
{
pser_inf->event_rlsd = 0;
}
ioctl(handle, TIOCOUTQ, &bytes);
if ((bytes == 0)
&& (pser_inf->event_txempty > 0) && (pser_inf->wait_mask & SERIAL_EV_TXEMPTY))
{
DEBUG_SERIAL(("Event -> SERIAL_EV_TXEMPTY\n"));
*result |= SERIAL_EV_TXEMPTY;
ret = True;
}
pser_inf->event_txempty = bytes;
ioctl(handle, TIOCMGET, &bytes);
if ((bytes & TIOCM_DSR) != pser_inf->event_dsr)
{
pser_inf->event_dsr = bytes & TIOCM_DSR;
if (pser_inf->wait_mask & SERIAL_EV_DSR)
{
DEBUG_SERIAL(("event -> SERIAL_EV_DSR %s\n", (bytes & TIOCM_DSR) ? "ON" : "OFF"));
*result |= SERIAL_EV_DSR;
ret = True;
}
}
if ((bytes & TIOCM_CTS) != pser_inf->event_cts)
{
pser_inf->event_cts = bytes & TIOCM_CTS;
if (pser_inf->wait_mask & SERIAL_EV_CTS)
{
DEBUG_SERIAL((" EVENT-> SERIAL_EV_CTS %s\n", (bytes & TIOCM_CTS) ? "ON" : "OFF"));
*result |= SERIAL_EV_CTS;
ret = True;
}
}
if (ret)
pser_inf->event_pending = 0;
return ret;
}
/* Read timeout for a given file descripter (device) when adding fd's to select() */
BOOL
serial_get_timeout(NTHANDLE handle, uint32 length, uint32 * timeout, uint32 * itv_timeout)

16
types.h
View File

@ -197,6 +197,7 @@ typedef struct rdpdr_serial_device_info
{
int dtr;
int rts;
uint32 control, xonoff, onlimit, offlimit;
uint32 baud_rate,
queue_in_size,
queue_out_size,
@ -206,7 +207,9 @@ typedef struct rdpdr_serial_device_info
read_total_timeout_constant,
write_total_timeout_multiplier, write_total_timeout_constant, posix_wait_mask;
uint8 stop_bits, parity, word_length;
uint8 chars[6];
struct termios *ptermios, *pold_termios;
int event_txempty, event_cts, event_dsr, event_rlsd, event_pending;
}
SERIAL_DEVICE;
@ -235,13 +238,24 @@ typedef struct rdpdr_printer_info
}
PRINTER;
typedef struct notify_data
{
time_t modify_time;
time_t status_time;
time_t total_time;
unsigned int num_entries;
}
NOTIFY;
typedef struct fileinfo
{
uint32 device_id, flags_and_attributes;
uint32 device_id, flags_and_attributes, accessmask;
char path[256];
DIR *pdir;
struct dirent *pdirent;
char pattern[64];
BOOL delete_on_close;
NOTIFY notify;
uint32 info_class;
}
FILEINFO;

9
xwin.c
View File

@ -1435,12 +1435,9 @@ ui_select(int rdp_socket)
error("select: %s\n", strerror(errno));
case 0:
/* TODO: if tv.tv_sec just times out
* we will segfault.
* FIXME:
*/
//s_timeout = True;
//rdpdr_check_fds(&rfds, &wfds, (BOOL) True);
/* Abort serial read calls */
if (s_timeout)
rdpdr_check_fds(&rfds, &wfds, (BOOL) True);
continue;
}