altos/test: Adjust CRC error rate after FEC fix
[fw/altos] / libaltos / libaltos_windows.c
1 /*
2  * Copyright © 2016 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #include "libaltos_private.h"
20
21 #include <winsock2.h>
22 #include <windows.h>
23 #include <setupapi.h>
24
25 struct altos_list {
26         HDEVINFO        dev_info;
27         int             index;
28         int             ftdi;
29 };
30
31 #define USB_BUF_SIZE    64
32
33 struct altos_file_windows {
34         struct altos_file               file;
35
36         BOOL                            is_winsock;
37         /* Data used by the regular I/O */
38         HANDLE                          handle;
39         OVERLAPPED                      ov_read;
40         BOOL                            pend_read;
41         OVERLAPPED                      ov_write;
42
43         /* Data used by winsock */
44         SOCKET                          socket;
45 };
46
47 #include <stdarg.h>
48
49 static void
50 log_message(char *fmt, ...)
51 {
52         static FILE *log = NULL;
53         va_list a;
54
55         if (!log)
56                 log = fopen("\\temp\\altos.txt", "w");
57         if (log) {
58                 SYSTEMTIME time;
59                 char    buffer[4096];
60
61                 GetLocalTime(&time);
62                 sprintf (buffer, "%4d-%02d-%02d %2d:%02d:%02d. ",
63                          time.wYear, time.wMonth, time.wDay,
64                          time.wHour, time.wMinute, time.wSecond);
65                 va_start(a, fmt);
66
67                 vsprintf(buffer + strlen(buffer), fmt, a);
68                 va_end(a);
69
70                 fputs(buffer, log);
71                 fflush(log);
72                 fputs(buffer, stdout);
73                 fflush(stdout);
74         }
75 }
76
77 static void
78 _altos_set_last_windows_error(char *file, int line, DWORD error)
79 {
80         TCHAR   message[1024];
81         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
82                       0,
83                       error,
84                       0,
85                       message,
86                       sizeof (message) / sizeof (TCHAR),
87                       NULL);
88         if (error != ERROR_SUCCESS)
89                 log_message ("%s:%d (%d) %s\n", file, line, error, message);
90         altos_set_last_error(error, message);
91 }
92
93 #define altos_set_last_windows_error() _altos_set_last_windows_error(__FILE__, __LINE__, GetLastError())
94 #define altos_set_last_winsock_error() _altos_set_last_windows_error(__FILE__, __LINE__, WSAGetLastError())
95
96 PUBLIC struct altos_list *
97 altos_list_start(void)
98 {
99         struct altos_list       *list = calloc(1, sizeof (struct altos_list));
100
101         if (!list)
102                 return NULL;
103         list->dev_info = SetupDiGetClassDevs(NULL, "USB", NULL,
104                                              DIGCF_ALLCLASSES|DIGCF_PRESENT);
105         if (list->dev_info == INVALID_HANDLE_VALUE) {
106                 altos_set_last_windows_error();
107                 free(list);
108                 return NULL;
109         }
110         list->index = 0;
111         list->ftdi = 0;
112         return list;
113 }
114
115 PUBLIC struct altos_list *
116 altos_ftdi_list_start(void)
117 {
118         struct altos_list       *list = calloc(1, sizeof (struct altos_list));
119
120         if (!list)
121                 return NULL;
122         list->dev_info = SetupDiGetClassDevs(NULL, "FTDIBUS", NULL,
123                                              DIGCF_ALLCLASSES|DIGCF_PRESENT);
124         if (list->dev_info == INVALID_HANDLE_VALUE) {
125                 altos_set_last_windows_error();
126                 free(list);
127                 return NULL;
128         }
129         list->index = 0;
130         list->ftdi = 1;
131         return list;
132 }
133
134 static struct {
135         unsigned int    vid, pid;
136         char    *name;
137 } name_map[] = {
138         { .vid = 0xfffe, .pid = 0x000d, .name = "EasyTimer" },
139         { .vid = 0xfffe, .pid = 0x0028, .name = "EasyMega" },
140         { .vid = 0xfffe, .pid = 0x002c, .name = "EasyMotor" },
141         { .name = NULL },
142 };
143
144 PUBLIC int
145 altos_list_next(struct altos_list *list, struct altos_device *device)
146 {
147         SP_DEVINFO_DATA dev_info_data;
148         BYTE            port[128];
149         DWORD           port_len;
150         char            friendlyname[256];
151         BYTE            symbolic[256];
152         DWORD           symbolic_len;
153         HKEY            dev_key;
154         unsigned int    vid, pid;
155         int             serial;
156         HRESULT         result;
157         DWORD           friendlyname_type;
158         DWORD           friendlyname_len;
159         char            instanceid[1024];
160         DWORD           instanceid_len;
161         int             i;
162
163         dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA);
164         while(SetupDiEnumDeviceInfo(list->dev_info, list->index,
165                                     &dev_info_data))
166         {
167                 list->index++;
168
169                 dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data,
170                                                DICS_FLAG_GLOBAL, 0, DIREG_DEV,
171                                                KEY_READ);
172                 if (dev_key == INVALID_HANDLE_VALUE) {
173                         altos_set_last_windows_error();
174                         continue;
175                 }
176
177                 if (list->ftdi) {
178                         vid = 0x0403;
179                         pid = 0x6015;
180                         serial = 0;
181                 } else {
182                         vid = pid = serial = 0;
183                         /* Fetch symbolic name for this device and parse out
184                          * the vid/pid/serial info */
185                         symbolic_len = sizeof(symbolic);
186                         result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL,
187                                                  symbolic, &symbolic_len);
188                         if (result != 0) {
189                                 altos_set_last_windows_error();
190                         } else {
191                                 sscanf((char *) symbolic + sizeof("\\??\\USB#VID_") - 1,
192                                        "%04X", &vid);
193                                 sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1,
194                                        "%04X", &pid);
195                                 sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1,
196                                        "%d", &serial);
197                         }
198                         if (vid == 0 || pid == 0 || serial == 0) {
199                                 if (SetupDiGetDeviceInstanceId(list->dev_info,
200                                                                &dev_info_data,
201                                                                instanceid,
202                                                                sizeof (instanceid),
203                                                                &instanceid_len)) {
204                                         sscanf((char *) instanceid + sizeof("USB\\VID_") - 1,
205                                                "%04X", &vid);
206                                         sscanf((char *) instanceid + sizeof("USB\\VID_XXXX&PID_") - 1,
207                                                "%04X", &pid);
208                                         sscanf((char *) instanceid + sizeof("USB\\VID_XXXX&PID_XXXX\\") - 1,
209                                                "%d", &serial);
210                                 } else {
211                                         altos_set_last_windows_error();
212                                 }
213                         }
214                         if (vid == 0 || pid == 0 || serial == 0) {
215                                 RegCloseKey(dev_key);
216                                 continue;
217                         }
218                 }
219
220                 /* Fetch the com port name */
221                 port_len = sizeof (port);
222                 result = RegQueryValueEx(dev_key, "PortName", NULL, NULL,
223                                          port, &port_len);
224                 RegCloseKey(dev_key);
225                 if (result != 0) {
226                         altos_set_last_windows_error();
227                         continue;
228                 }
229
230                 /* Fetch the device description which is the device name,
231                  * with firmware that has unique USB ids */
232                 friendlyname_len = sizeof (friendlyname);
233                 if(!SetupDiGetDeviceRegistryProperty(list->dev_info,
234                                                      &dev_info_data,
235                                                      SPDRP_FRIENDLYNAME,
236                                                      &friendlyname_type,
237                                                      (BYTE *)friendlyname,
238                                                      sizeof(friendlyname),
239                                                      &friendlyname_len))
240                 {
241                         altos_set_last_windows_error();
242                         continue;
243                 }
244
245                 char *space = friendlyname;
246                 while (*space) {
247                         if (*space == ' ') {
248                                 *space = '\0';
249                                 break;
250                         }
251                         space++;
252                 }
253
254                 for (i = 0; name_map[i].name; i++) {
255                         if (name_map[i].vid == vid && name_map[i].pid == pid) {
256                                 strcpy(friendlyname, name_map[i].name);
257                                 break;
258                         }
259                 }
260
261                 device->vendor = vid;
262                 device->product = pid;
263                 device->serial = serial;
264                 strcpy(device->name, friendlyname);
265
266                 strcpy(device->path, (char *) port);
267                 return 1;
268         }
269         result = GetLastError();
270         if (result != ERROR_NO_MORE_ITEMS)
271                 altos_set_last_windows_error();
272         return 0;
273 }
274
275 PUBLIC void
276 altos_list_finish(struct altos_list *list)
277 {
278         SetupDiDestroyDeviceInfoList(list->dev_info);
279         free(list);
280 }
281
282 static int
283 altos_queue_read(struct altos_file_windows *file)
284 {
285         DWORD   got;
286         if (file->pend_read)
287                 return LIBALTOS_SUCCESS;
288
289         if (!ReadFile(file->handle, file->file.in_data, USB_BUF_SIZE, &got, &file->ov_read)) {
290                 if (GetLastError() != ERROR_IO_PENDING) {
291                         altos_set_last_windows_error();
292                         return LIBALTOS_ERROR;
293                 }
294                 file->pend_read = TRUE;
295         } else {
296                 file->pend_read = FALSE;
297                 file->file.in_read = 0;
298                 file->file.in_used = got;
299         }
300         return LIBALTOS_SUCCESS;
301 }
302
303 static int
304 altos_wait_read(struct altos_file_windows *file, int timeout)
305 {
306         DWORD   ret;
307         DWORD   got;
308
309         if (!file->pend_read)
310                 return LIBALTOS_SUCCESS;
311
312         if (!timeout)
313                 timeout = INFINITE;
314
315         ret = WaitForSingleObject(file->ov_read.hEvent, timeout);
316         switch (ret) {
317         case WAIT_OBJECT_0:
318                 if (!GetOverlappedResult(file->handle, &file->ov_read, &got, FALSE)) {
319                         if (GetLastError () != ERROR_OPERATION_ABORTED)
320                                 altos_set_last_windows_error();
321                         return LIBALTOS_ERROR;
322                 }
323                 file->pend_read = FALSE;
324                 file->file.in_read = 0;
325                 file->file.in_used = got;
326                 break;
327         case WAIT_TIMEOUT:
328                 return LIBALTOS_TIMEOUT;
329                 break;
330         default:
331                 altos_set_last_windows_error();
332                 return LIBALTOS_ERROR;
333         }
334         return LIBALTOS_SUCCESS;
335 }
336
337 int
338 altos_fill(struct altos_file *file_common, int timeout)
339 {
340         struct altos_file_windows       *file = (struct altos_file_windows *) file_common;
341
342         int     ret;
343
344         if (file->file.in_read < file->file.in_used)
345                 return LIBALTOS_SUCCESS;
346
347         file->file.in_read = file->file.in_used = 0;
348
349         if (file->is_winsock) {
350
351                 for (;;) {
352                         fd_set  readfds;
353                         TIMEVAL timeval;
354                         int     thistimeout;
355
356                         /* Check to see if the socket has been closed */
357                         if (file->socket == INVALID_SOCKET)
358                                 return LIBALTOS_ERROR;
359
360 #define POLL_TIMEOUT    10000
361
362                         /* Poll to see if the socket has been closed
363                          * as select doesn't abort when that happens
364                          */
365                         if (timeout) {
366                                 thistimeout = timeout;
367                                 if (thistimeout > POLL_TIMEOUT)
368                                         thistimeout = POLL_TIMEOUT;
369                         } else {
370                                 thistimeout = POLL_TIMEOUT;
371                         }
372
373                         timeval.tv_sec = thistimeout / 1000;
374                         timeval.tv_usec = (thistimeout % 1000) * 1000;
375
376                         FD_ZERO(&readfds);
377                         FD_SET(file->socket, &readfds);
378
379                         ret = select(1, &readfds, NULL, NULL, &timeval);
380
381                         if (ret == 0) {
382                                 if (timeout) {
383                                         timeout -= thistimeout;
384                                         if (timeout == 0)
385                                                 return LIBALTOS_TIMEOUT;
386                                 }
387                         } else {
388                                 if (ret > 0)
389                                         break;
390
391                                 if (ret < 0) {
392                                         altos_set_last_winsock_error();
393                                         return LIBALTOS_ERROR;
394                                 }
395                         }
396                 }
397
398                 if (file->socket == INVALID_SOCKET) {
399                         altos_set_last_winsock_error();
400                         return LIBALTOS_ERROR;
401                 }
402
403                 ret = recv(file->socket, (char *) file->file.in_data, USB_BUF_SIZE, 0);
404
405                 if (ret <= 0) {
406                         altos_set_last_winsock_error();
407                         return LIBALTOS_ERROR;
408                 }
409                 file->file.in_read = 0;
410                 file->file.in_used = ret;
411         } else {
412                 if (file->handle == INVALID_HANDLE_VALUE) {
413                         altos_set_last_windows_error();
414                         return LIBALTOS_ERROR;
415                 }
416
417                 ret = altos_queue_read(file);
418                 if (ret)
419                         return ret;
420                 ret = altos_wait_read(file, timeout);
421                 if (ret)
422                         return ret;
423         }
424
425         return LIBALTOS_SUCCESS;
426 }
427
428 PUBLIC int
429 altos_flush(struct altos_file *file_common)
430 {
431         struct altos_file_windows       *file = (struct altos_file_windows *) file_common;
432
433         unsigned char   *data = file->file.out_data;
434         int             used = file->file.out_used;
435
436         while (used) {
437                 if (file->is_winsock) {
438                         int     put;
439
440                         put = send(file->socket, (char *) data, used, 0);
441                         if (put <= 0) {
442                                 altos_set_last_winsock_error();
443                                 return LIBALTOS_ERROR;
444                         }
445                         data += put;
446                         used -= put;
447                 } else {
448                         DWORD           put;
449                         DWORD           ret;
450                         if (!WriteFile(file->handle, data, used, &put, &file->ov_write)) {
451                                 if (GetLastError() != ERROR_IO_PENDING) {
452                                         altos_set_last_windows_error();
453                                         return LIBALTOS_ERROR;
454                                 }
455                                 ret = WaitForSingleObject(file->ov_write.hEvent, INFINITE);
456                                 switch (ret) {
457                                 case WAIT_OBJECT_0:
458                                         if (!GetOverlappedResult(file->handle, &file->ov_write, &put, FALSE)) {
459                                                 altos_set_last_windows_error();
460                                                 return LIBALTOS_ERROR;
461                                         }
462                                         break;
463                                 default:
464                                         altos_set_last_windows_error();
465                                         return LIBALTOS_ERROR;
466                                 }
467                         }
468                         data += put;
469                         used -= put;
470                 }
471         }
472         file->file.out_used = 0;
473         return LIBALTOS_SUCCESS;
474 }
475
476 static HANDLE
477 open_serial(char *full_name)
478 {
479         HANDLE  handle;
480         DCB     dcb;
481
482         handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE,
483                             0, NULL, OPEN_EXISTING,
484                             FILE_FLAG_OVERLAPPED, NULL);
485
486         if (handle == INVALID_HANDLE_VALUE) {
487                 altos_set_last_windows_error();
488                 return INVALID_HANDLE_VALUE;
489         }
490
491         if (!GetCommState(handle, &dcb)) {
492                 altos_set_last_windows_error();
493                 CloseHandle(handle);
494                 return INVALID_HANDLE_VALUE;
495         }
496         dcb.BaudRate = CBR_9600;
497         dcb.fBinary = TRUE;
498         dcb.fParity = FALSE;
499         dcb.fOutxCtsFlow = FALSE;
500         dcb.fOutxDsrFlow = FALSE;
501         dcb.fDtrControl = DTR_CONTROL_ENABLE;
502         dcb.fDsrSensitivity = FALSE;
503         dcb.fTXContinueOnXoff = FALSE;
504         dcb.fOutX = FALSE;
505         dcb.fInX = FALSE;
506         dcb.fErrorChar = FALSE;
507         dcb.fNull = FALSE;
508         dcb.fRtsControl = RTS_CONTROL_ENABLE;
509         dcb.fAbortOnError = FALSE;
510         dcb.XonLim = 10;
511         dcb.XoffLim = 10;
512         dcb.ByteSize = 8;
513         dcb.Parity = NOPARITY;
514         dcb.StopBits = ONESTOPBIT;
515         dcb.XonChar = 17;
516         dcb.XoffChar = 19;
517 #if 0
518         dcb.ErrorChar = 0;
519         dcb.EofChar = 0;
520         dcb.EvtChar = 0;
521 #endif
522         if (!SetCommState(handle, &dcb)) {
523                 altos_set_last_windows_error();
524                 CloseHandle(handle);
525                 return INVALID_HANDLE_VALUE;
526         }
527         return handle;
528 }
529
530 PUBLIC struct altos_file *
531 altos_open(struct altos_device *device)
532 {
533         struct altos_file_windows       *file = calloc (1, sizeof (struct altos_file_windows));
534         char    full_name[64];
535         COMMTIMEOUTS timeouts;
536         int i;
537
538         if (!file)
539                 return NULL;
540
541         strcpy(full_name, "\\\\.\\");
542         strcat(full_name, device->path);
543
544         file->handle = INVALID_HANDLE_VALUE;
545
546         for (i = 0; i < 5; i++) {
547                 file->handle = open_serial(full_name);
548                 if (file->handle != INVALID_HANDLE_VALUE)
549                         break;
550                 altos_set_last_windows_error();
551                 Sleep(100);
552         }
553
554         if (file->handle == INVALID_HANDLE_VALUE) {
555                 free(file);
556                 return NULL;
557         }
558
559         /* The FTDI driver doesn't appear to work right unless you open it twice */
560         if (device->vendor == 0x0403) {
561                 CloseHandle(file->handle);
562                 file->handle = open_serial(full_name);
563                 if (file->handle == INVALID_HANDLE_VALUE) {
564                         free(file);
565                         return NULL;
566                 }
567         }
568
569         timeouts.ReadIntervalTimeout = MAXDWORD;
570         timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
571         timeouts.ReadTotalTimeoutConstant = 1 << 30;    /* almost forever */
572         timeouts.WriteTotalTimeoutMultiplier = 0;
573         timeouts.WriteTotalTimeoutConstant = 0;
574         SetCommTimeouts(file->handle, &timeouts);
575
576         file->ov_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
577         file->ov_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
578
579         return &file->file;
580 }
581
582 PUBLIC void
583 altos_close(struct altos_file *file_common)
584 {
585         struct altos_file_windows       *file = (struct altos_file_windows *) file_common;
586
587         if (file->is_winsock) {
588                 SOCKET  socket = file->socket;
589                 if (socket != INVALID_SOCKET) {
590                         file->socket = INVALID_SOCKET;
591                         closesocket(socket);
592                 }
593         } else {
594                 HANDLE  handle = file->handle;
595                 if (handle != INVALID_HANDLE_VALUE) {
596                         HANDLE  ov_read = file->ov_read.hEvent;
597                         HANDLE  ov_write = file->ov_write.hEvent;
598                         file->handle = INVALID_HANDLE_VALUE;
599                         file->ov_read.hEvent = INVALID_HANDLE_VALUE;
600                         file->ov_write.hEvent = INVALID_HANDLE_VALUE;
601                         PurgeComm(handle, PURGE_RXABORT|PURGE_RXCLEAR|PURGE_TXABORT|PURGE_TXCLEAR);
602                         Sleep(100);
603                         CloseHandle(handle);
604                         file->handle = INVALID_HANDLE_VALUE;
605                         CloseHandle(ov_read);
606                         CloseHandle(ov_write);
607                 }
608         }
609 }
610
611 #include <ws2bth.h>
612
613 #define LUP_SET (LUP_RETURN_NAME| LUP_CONTAINERS | LUP_RETURN_ADDR | LUP_FLUSHCACHE |\
614                  LUP_RETURN_TYPE | LUP_RETURN_BLOB | LUP_RES_SERVICE)
615
616 struct altos_bt_list {
617         WSADATA WSAData;
618         HANDLE  lookup;
619 };
620
621 struct altos_bt_list *
622 altos_bt_list_start(int inquiry_time)
623 {
624         struct altos_bt_list    *bt_list;
625         WSAQUERYSET             query_set;
626         int                     retCode;
627
628         /* Windows provides no way to set the time */
629         (void) inquiry_time;
630         bt_list = calloc(1, sizeof (struct altos_bt_list));
631         if (!bt_list) {
632                 altos_set_last_windows_error();
633                 return NULL;
634         }
635
636         if ((retCode = WSAStartup(MAKEWORD(2,2),&bt_list->WSAData)) != 0) {
637                 altos_set_last_winsock_error();
638                 free(bt_list);
639                 return NULL;
640         }
641
642         memset(&query_set, '\0', sizeof (query_set));
643         query_set.dwSize = sizeof(WSAQUERYSET);
644         query_set.dwNameSpace = NS_BTH;
645
646         retCode = WSALookupServiceBegin(&query_set, LUP_SET, &bt_list->lookup);
647
648         if (retCode != 0) {
649                 altos_set_last_winsock_error();
650                 free(bt_list);
651                 return NULL;
652         }
653         return bt_list;
654 }
655
656 static unsigned char get_byte(BTH_ADDR ba, int shift)
657 {
658         return (ba >> ((5 - shift) << 3)) & 0xff;
659 }
660
661 static BTH_ADDR put_byte(unsigned char c, int shift)
662 {
663         return ((BTH_ADDR) c) << ((5 - shift) << 3);
664 }
665
666 static void
667 ba2str(BTH_ADDR ba, char *str)
668 {
669
670         sprintf(str, "%02X:%02X:%02X:%02X:%02X:%02X",
671                 get_byte(ba, 0),
672                 get_byte(ba, 1),
673                 get_byte(ba, 2),
674                 get_byte(ba, 3),
675                 get_byte(ba, 4),
676                 get_byte(ba, 5));
677 }
678
679 static BTH_ADDR
680 str2ba(char *str)
681 {
682         unsigned int    bytes[6];
683
684         sscanf(str,  "%02x:%02x:%02x:%02x:%02x:%02x",
685                &bytes[0],
686                &bytes[1],
687                &bytes[2],
688                &bytes[3],
689                &bytes[4],
690                &bytes[5]);
691         return (put_byte(bytes[0], 0) |
692                 put_byte(bytes[1], 1) |
693                 put_byte(bytes[2], 2) |
694                 put_byte(bytes[3], 3) |
695                 put_byte(bytes[4], 4) |
696                 put_byte(bytes[5], 5));
697 }
698
699 int
700 altos_bt_list_next(struct altos_bt_list *bt_list,
701                    struct altos_bt_device *device)
702 {
703         for (;;) {
704                 BYTE            buffer[4096];
705                 DWORD           length = sizeof (buffer);;
706                 WSAQUERYSET     *results = (WSAQUERYSET *)buffer;
707                 CSADDR_INFO     *addr_info;
708                 int             retCode;
709                 SOCKADDR_BTH    *sockaddr_bth;
710
711                 memset(buffer, '\0', sizeof(buffer));
712
713                 retCode = WSALookupServiceNext(bt_list->lookup, LUP_SET, &length, results);
714
715                 if (retCode != 0) {
716                         int error = WSAGetLastError();
717                         if (error != WSAENOMORE && error != WSA_E_NO_MORE)
718                                 altos_set_last_winsock_error();
719                         return 0;
720                 }
721
722                 if (results->dwNumberOfCsAddrs > 0) {
723
724                         addr_info = results->lpcsaBuffer;
725
726                         strncpy(device->name, results->lpszServiceInstanceName, sizeof(device->name));
727                         device->name[sizeof(device->name)-1] = '\0';
728
729                         sockaddr_bth = (SOCKADDR_BTH *) addr_info->RemoteAddr.lpSockaddr;
730
731                         ba2str(sockaddr_bth->btAddr, device->addr);
732
733                         return 1;
734                 }
735         }
736 }
737
738 void
739 altos_bt_list_finish(struct altos_bt_list *bt_list)
740 {
741         WSALookupServiceEnd(bt_list->lookup);
742         free(bt_list);
743 }
744
745 void
746 altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device)
747 {
748         strncpy(device->name, name, sizeof (device->name));
749         device->name[sizeof(device->name)-1] = '\0';
750         strncpy(device->addr, addr, sizeof (device->addr));
751         device->addr[sizeof(device->addr)-1] = '\0';
752 }
753
754 struct altos_file *
755 altos_bt_open(struct altos_bt_device *device)
756 {
757         struct altos_file_windows       *file;
758         SOCKADDR_BTH            sockaddr_bth;
759         int                     ret;
760
761         file = calloc(1, sizeof (struct altos_file_windows));
762         if (!file) {
763                 return NULL;
764         }
765
766         file->is_winsock = TRUE;
767         file->socket = WSASocket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM, NULL, 0, WSA_FLAG_OVERLAPPED);
768
769         if (file->socket == INVALID_SOCKET) {
770                 altos_set_last_winsock_error();
771                 free(file);
772                 return NULL;
773         }
774
775         memset(&sockaddr_bth, '\0', sizeof (sockaddr_bth));
776         sockaddr_bth.addressFamily = AF_BTH;
777         sockaddr_bth.btAddr = str2ba(device->addr);
778         sockaddr_bth.port = altos_bt_port(device);
779
780         ret = connect(file->socket, (SOCKADDR *) &sockaddr_bth, sizeof (sockaddr_bth));
781
782         if (ret != 0) {
783                 altos_set_last_winsock_error();
784                 closesocket(file->socket);
785                 free(file);
786                 log_message("Connection attempted to address %s port %d\n", device->addr, sockaddr_bth.port);
787                 return NULL;
788         }
789         return &file->file;
790 }
791
792 void
793 altos_pause_one_second(void)
794 {
795         Sleep(1000);
796 }