162c85bab8fa6aa658479017bb920395fb678e8a
[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         char    *windows;
136         char    *real;
137 } name_map[] = {
138         { .windows = "AltusMetrum28", .real = "EasyMega" },
139         { .windows = "AltusMetrum2c", .real = "EasyMotor" },
140         { 0, 0 },
141 };
142
143 PUBLIC int
144 altos_list_next(struct altos_list *list, struct altos_device *device)
145 {
146         SP_DEVINFO_DATA dev_info_data;
147         BYTE            port[128];
148         DWORD           port_len;
149         char            friendlyname[256];
150         BYTE            symbolic[256];
151         DWORD           symbolic_len;
152         HKEY            dev_key;
153         unsigned int    vid, pid;
154         int             serial;
155         HRESULT         result;
156         DWORD           friendlyname_type;
157         DWORD           friendlyname_len;
158         char            instanceid[1024];
159         DWORD           instanceid_len;
160         int             i;
161
162         dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA);
163         while(SetupDiEnumDeviceInfo(list->dev_info, list->index,
164                                     &dev_info_data))
165         {
166                 list->index++;
167
168                 dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data,
169                                                DICS_FLAG_GLOBAL, 0, DIREG_DEV,
170                                                KEY_READ);
171                 if (dev_key == INVALID_HANDLE_VALUE) {
172                         altos_set_last_windows_error();
173                         continue;
174                 }
175
176                 if (list->ftdi) {
177                         vid = 0x0403;
178                         pid = 0x6015;
179                         serial = 0;
180                 } else {
181                         vid = pid = serial = 0;
182                         /* Fetch symbolic name for this device and parse out
183                          * the vid/pid/serial info */
184                         symbolic_len = sizeof(symbolic);
185                         result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL,
186                                                  symbolic, &symbolic_len);
187                         if (result != 0) {
188                                 altos_set_last_windows_error();
189                         } else {
190                                 sscanf((char *) symbolic + sizeof("\\??\\USB#VID_") - 1,
191                                        "%04X", &vid);
192                                 sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1,
193                                        "%04X", &pid);
194                                 sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1,
195                                        "%d", &serial);
196                         }
197                         if (vid == 0 || pid == 0 || serial == 0) {
198                                 if (SetupDiGetDeviceInstanceId(list->dev_info,
199                                                                &dev_info_data,
200                                                                instanceid,
201                                                                sizeof (instanceid),
202                                                                &instanceid_len)) {
203                                         sscanf((char *) instanceid + sizeof("USB\\VID_") - 1,
204                                                "%04X", &vid);
205                                         sscanf((char *) instanceid + sizeof("USB\\VID_XXXX&PID_") - 1,
206                                                "%04X", &pid);
207                                         sscanf((char *) instanceid + sizeof("USB\\VID_XXXX&PID_XXXX\\") - 1,
208                                                "%d", &serial);
209                                 } else {
210                                         altos_set_last_windows_error();
211                                 }
212                         }
213                         if (vid == 0 || pid == 0 || serial == 0) {
214                                 RegCloseKey(dev_key);
215                                 continue;
216                         }
217                 }
218
219                 /* Fetch the com port name */
220                 port_len = sizeof (port);
221                 result = RegQueryValueEx(dev_key, "PortName", NULL, NULL,
222                                          port, &port_len);
223                 RegCloseKey(dev_key);
224                 if (result != 0) {
225                         altos_set_last_windows_error();
226                         continue;
227                 }
228
229                 /* Fetch the device description which is the device name,
230                  * with firmware that has unique USB ids */
231                 friendlyname_len = sizeof (friendlyname);
232                 if(!SetupDiGetDeviceRegistryProperty(list->dev_info,
233                                                      &dev_info_data,
234                                                      SPDRP_FRIENDLYNAME,
235                                                      &friendlyname_type,
236                                                      (BYTE *)friendlyname,
237                                                      sizeof(friendlyname),
238                                                      &friendlyname_len))
239                 {
240                         altos_set_last_windows_error();
241                         continue;
242                 }
243                 for (i = 0; name_map[i].windows; i++)
244                         if (!strcmp(name_map[i].windows, friendlyname)) {
245                                 strcpy(friendlyname, name_map[i].real);
246                                 break;
247                         }
248
249                 char *space = strchr(friendlyname, ' ');
250                 if  (space)
251                         *space = '\0';
252
253                 device->vendor = vid;
254                 device->product = pid;
255                 device->serial = serial;
256                 strcpy(device->name, friendlyname);
257
258                 strcpy(device->path, (char *) port);
259                 return 1;
260         }
261         result = GetLastError();
262         if (result != ERROR_NO_MORE_ITEMS)
263                 altos_set_last_windows_error();
264         return 0;
265 }
266
267 PUBLIC void
268 altos_list_finish(struct altos_list *list)
269 {
270         SetupDiDestroyDeviceInfoList(list->dev_info);
271         free(list);
272 }
273
274 static int
275 altos_queue_read(struct altos_file_windows *file)
276 {
277         DWORD   got;
278         if (file->pend_read)
279                 return LIBALTOS_SUCCESS;
280
281         if (!ReadFile(file->handle, file->file.in_data, USB_BUF_SIZE, &got, &file->ov_read)) {
282                 if (GetLastError() != ERROR_IO_PENDING) {
283                         altos_set_last_windows_error();
284                         return LIBALTOS_ERROR;
285                 }
286                 file->pend_read = TRUE;
287         } else {
288                 file->pend_read = FALSE;
289                 file->file.in_read = 0;
290                 file->file.in_used = got;
291         }
292         return LIBALTOS_SUCCESS;
293 }
294
295 static int
296 altos_wait_read(struct altos_file_windows *file, int timeout)
297 {
298         DWORD   ret;
299         DWORD   got;
300
301         if (!file->pend_read)
302                 return LIBALTOS_SUCCESS;
303
304         if (!timeout)
305                 timeout = INFINITE;
306
307         ret = WaitForSingleObject(file->ov_read.hEvent, timeout);
308         switch (ret) {
309         case WAIT_OBJECT_0:
310                 if (!GetOverlappedResult(file->handle, &file->ov_read, &got, FALSE)) {
311                         if (GetLastError () != ERROR_OPERATION_ABORTED)
312                                 altos_set_last_windows_error();
313                         return LIBALTOS_ERROR;
314                 }
315                 file->pend_read = FALSE;
316                 file->file.in_read = 0;
317                 file->file.in_used = got;
318                 break;
319         case WAIT_TIMEOUT:
320                 return LIBALTOS_TIMEOUT;
321                 break;
322         default:
323                 altos_set_last_windows_error();
324                 return LIBALTOS_ERROR;
325         }
326         return LIBALTOS_SUCCESS;
327 }
328
329 int
330 altos_fill(struct altos_file *file_common, int timeout)
331 {
332         struct altos_file_windows       *file = (struct altos_file_windows *) file_common;
333
334         int     ret;
335
336         if (file->file.in_read < file->file.in_used)
337                 return LIBALTOS_SUCCESS;
338
339         file->file.in_read = file->file.in_used = 0;
340
341         if (file->is_winsock) {
342
343                 for (;;) {
344                         fd_set  readfds;
345                         TIMEVAL timeval;
346                         int     thistimeout;
347
348                         /* Check to see if the socket has been closed */
349                         if (file->socket == INVALID_SOCKET)
350                                 return LIBALTOS_ERROR;
351
352 #define POLL_TIMEOUT    10000
353
354                         /* Poll to see if the socket has been closed
355                          * as select doesn't abort when that happens
356                          */
357                         if (timeout) {
358                                 thistimeout = timeout;
359                                 if (thistimeout > POLL_TIMEOUT)
360                                         thistimeout = POLL_TIMEOUT;
361                         } else {
362                                 thistimeout = POLL_TIMEOUT;
363                         }
364
365                         timeval.tv_sec = thistimeout / 1000;
366                         timeval.tv_usec = (thistimeout % 1000) * 1000;
367
368                         FD_ZERO(&readfds);
369                         FD_SET(file->socket, &readfds);
370
371                         ret = select(1, &readfds, NULL, NULL, &timeval);
372
373                         if (ret == 0) {
374                                 if (timeout) {
375                                         timeout -= thistimeout;
376                                         if (timeout == 0)
377                                                 return LIBALTOS_TIMEOUT;
378                                 }
379                         } else {
380                                 if (ret > 0)
381                                         break;
382
383                                 if (ret < 0) {
384                                         altos_set_last_winsock_error();
385                                         return LIBALTOS_ERROR;
386                                 }
387                         }
388                 }
389
390                 if (file->socket == INVALID_SOCKET) {
391                         altos_set_last_winsock_error();
392                         return LIBALTOS_ERROR;
393                 }
394
395                 ret = recv(file->socket, (char *) file->file.in_data, USB_BUF_SIZE, 0);
396
397                 if (ret <= 0) {
398                         altos_set_last_winsock_error();
399                         return LIBALTOS_ERROR;
400                 }
401                 file->file.in_read = 0;
402                 file->file.in_used = ret;
403         } else {
404                 if (file->handle == INVALID_HANDLE_VALUE) {
405                         altos_set_last_windows_error();
406                         return LIBALTOS_ERROR;
407                 }
408
409                 ret = altos_queue_read(file);
410                 if (ret)
411                         return ret;
412                 ret = altos_wait_read(file, timeout);
413                 if (ret)
414                         return ret;
415         }
416
417         return LIBALTOS_SUCCESS;
418 }
419
420 PUBLIC int
421 altos_flush(struct altos_file *file_common)
422 {
423         struct altos_file_windows       *file = (struct altos_file_windows *) file_common;
424
425         unsigned char   *data = file->file.out_data;
426         int             used = file->file.out_used;
427
428         while (used) {
429                 if (file->is_winsock) {
430                         int     put;
431
432                         put = send(file->socket, (char *) data, used, 0);
433                         if (put <= 0) {
434                                 altos_set_last_winsock_error();
435                                 return LIBALTOS_ERROR;
436                         }
437                         data += put;
438                         used -= put;
439                 } else {
440                         DWORD           put;
441                         DWORD           ret;
442                         if (!WriteFile(file->handle, data, used, &put, &file->ov_write)) {
443                                 if (GetLastError() != ERROR_IO_PENDING) {
444                                         altos_set_last_windows_error();
445                                         return LIBALTOS_ERROR;
446                                 }
447                                 ret = WaitForSingleObject(file->ov_write.hEvent, INFINITE);
448                                 switch (ret) {
449                                 case WAIT_OBJECT_0:
450                                         if (!GetOverlappedResult(file->handle, &file->ov_write, &put, FALSE)) {
451                                                 altos_set_last_windows_error();
452                                                 return LIBALTOS_ERROR;
453                                         }
454                                         break;
455                                 default:
456                                         altos_set_last_windows_error();
457                                         return LIBALTOS_ERROR;
458                                 }
459                         }
460                         data += put;
461                         used -= put;
462                 }
463         }
464         file->file.out_used = 0;
465         return LIBALTOS_SUCCESS;
466 }
467
468 static HANDLE
469 open_serial(char *full_name)
470 {
471         HANDLE  handle;
472         DCB     dcb;
473
474         handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE,
475                             0, NULL, OPEN_EXISTING,
476                             FILE_FLAG_OVERLAPPED, NULL);
477
478         if (handle == INVALID_HANDLE_VALUE) {
479                 altos_set_last_windows_error();
480                 return INVALID_HANDLE_VALUE;
481         }
482
483         if (!GetCommState(handle, &dcb)) {
484                 altos_set_last_windows_error();
485                 CloseHandle(handle);
486                 return INVALID_HANDLE_VALUE;
487         }
488         dcb.BaudRate = CBR_9600;
489         dcb.fBinary = TRUE;
490         dcb.fParity = FALSE;
491         dcb.fOutxCtsFlow = FALSE;
492         dcb.fOutxDsrFlow = FALSE;
493         dcb.fDtrControl = DTR_CONTROL_ENABLE;
494         dcb.fDsrSensitivity = FALSE;
495         dcb.fTXContinueOnXoff = FALSE;
496         dcb.fOutX = FALSE;
497         dcb.fInX = FALSE;
498         dcb.fErrorChar = FALSE;
499         dcb.fNull = FALSE;
500         dcb.fRtsControl = RTS_CONTROL_ENABLE;
501         dcb.fAbortOnError = FALSE;
502         dcb.XonLim = 10;
503         dcb.XoffLim = 10;
504         dcb.ByteSize = 8;
505         dcb.Parity = NOPARITY;
506         dcb.StopBits = ONESTOPBIT;
507         dcb.XonChar = 17;
508         dcb.XoffChar = 19;
509 #if 0
510         dcb.ErrorChar = 0;
511         dcb.EofChar = 0;
512         dcb.EvtChar = 0;
513 #endif
514         if (!SetCommState(handle, &dcb)) {
515                 altos_set_last_windows_error();
516                 CloseHandle(handle);
517                 return INVALID_HANDLE_VALUE;
518         }
519         return handle;
520 }
521
522 PUBLIC struct altos_file *
523 altos_open(struct altos_device *device)
524 {
525         struct altos_file_windows       *file = calloc (1, sizeof (struct altos_file_windows));
526         char    full_name[64];
527         COMMTIMEOUTS timeouts;
528         int i;
529
530         if (!file)
531                 return NULL;
532
533         strcpy(full_name, "\\\\.\\");
534         strcat(full_name, device->path);
535
536         file->handle = INVALID_HANDLE_VALUE;
537
538         for (i = 0; i < 5; i++) {
539                 file->handle = open_serial(full_name);
540                 if (file->handle != INVALID_HANDLE_VALUE)
541                         break;
542                 altos_set_last_windows_error();
543                 Sleep(100);
544         }
545
546         if (file->handle == INVALID_HANDLE_VALUE) {
547                 free(file);
548                 return NULL;
549         }
550
551         /* The FTDI driver doesn't appear to work right unless you open it twice */
552         if (device->vendor == 0x0403) {
553                 CloseHandle(file->handle);
554                 file->handle = open_serial(full_name);
555                 if (file->handle == INVALID_HANDLE_VALUE) {
556                         free(file);
557                         return NULL;
558                 }
559         }
560
561         timeouts.ReadIntervalTimeout = MAXDWORD;
562         timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
563         timeouts.ReadTotalTimeoutConstant = 1 << 30;    /* almost forever */
564         timeouts.WriteTotalTimeoutMultiplier = 0;
565         timeouts.WriteTotalTimeoutConstant = 0;
566         SetCommTimeouts(file->handle, &timeouts);
567
568         file->ov_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
569         file->ov_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
570
571         return &file->file;
572 }
573
574 PUBLIC void
575 altos_close(struct altos_file *file_common)
576 {
577         struct altos_file_windows       *file = (struct altos_file_windows *) file_common;
578
579         if (file->is_winsock) {
580                 SOCKET  socket = file->socket;
581                 if (socket != INVALID_SOCKET) {
582                         file->socket = INVALID_SOCKET;
583                         closesocket(socket);
584                 }
585         } else {
586                 HANDLE  handle = file->handle;
587                 if (handle != INVALID_HANDLE_VALUE) {
588                         HANDLE  ov_read = file->ov_read.hEvent;
589                         HANDLE  ov_write = file->ov_write.hEvent;
590                         file->handle = INVALID_HANDLE_VALUE;
591                         file->ov_read.hEvent = INVALID_HANDLE_VALUE;
592                         file->ov_write.hEvent = INVALID_HANDLE_VALUE;
593                         PurgeComm(handle, PURGE_RXABORT|PURGE_RXCLEAR|PURGE_TXABORT|PURGE_TXCLEAR);
594                         Sleep(100);
595                         CloseHandle(handle);
596                         file->handle = INVALID_HANDLE_VALUE;
597                         CloseHandle(ov_read);
598                         CloseHandle(ov_write);
599                 }
600         }
601 }
602
603 #include <ws2bth.h>
604
605 #define LUP_SET (LUP_RETURN_NAME| LUP_CONTAINERS | LUP_RETURN_ADDR | LUP_FLUSHCACHE |\
606                  LUP_RETURN_TYPE | LUP_RETURN_BLOB | LUP_RES_SERVICE)
607
608 struct altos_bt_list {
609         WSADATA WSAData;
610         HANDLE  lookup;
611 };
612
613 struct altos_bt_list *
614 altos_bt_list_start(int inquiry_time)
615 {
616         struct altos_bt_list    *bt_list;
617         WSAQUERYSET             query_set;
618         int                     retCode;
619
620         /* Windows provides no way to set the time */
621         (void) inquiry_time;
622         bt_list = calloc(1, sizeof (struct altos_bt_list));
623         if (!bt_list) {
624                 altos_set_last_windows_error();
625                 return NULL;
626         }
627
628         if ((retCode = WSAStartup(MAKEWORD(2,2),&bt_list->WSAData)) != 0) {
629                 altos_set_last_winsock_error();
630                 free(bt_list);
631                 return NULL;
632         }
633
634         memset(&query_set, '\0', sizeof (query_set));
635         query_set.dwSize = sizeof(WSAQUERYSET);
636         query_set.dwNameSpace = NS_BTH;
637
638         retCode = WSALookupServiceBegin(&query_set, LUP_SET, &bt_list->lookup);
639
640         if (retCode != 0) {
641                 altos_set_last_winsock_error();
642                 free(bt_list);
643                 return NULL;
644         }
645         return bt_list;
646 }
647
648 static unsigned char get_byte(BTH_ADDR ba, int shift)
649 {
650         return (ba >> ((5 - shift) << 3)) & 0xff;
651 }
652
653 static BTH_ADDR put_byte(unsigned char c, int shift)
654 {
655         return ((BTH_ADDR) c) << ((5 - shift) << 3);
656 }
657
658 static void
659 ba2str(BTH_ADDR ba, char *str)
660 {
661
662         sprintf(str, "%02X:%02X:%02X:%02X:%02X:%02X",
663                 get_byte(ba, 0),
664                 get_byte(ba, 1),
665                 get_byte(ba, 2),
666                 get_byte(ba, 3),
667                 get_byte(ba, 4),
668                 get_byte(ba, 5));
669 }
670
671 static BTH_ADDR
672 str2ba(char *str)
673 {
674         unsigned int    bytes[6];
675
676         sscanf(str,  "%02x:%02x:%02x:%02x:%02x:%02x",
677                &bytes[0],
678                &bytes[1],
679                &bytes[2],
680                &bytes[3],
681                &bytes[4],
682                &bytes[5]);
683         return (put_byte(bytes[0], 0) |
684                 put_byte(bytes[1], 1) |
685                 put_byte(bytes[2], 2) |
686                 put_byte(bytes[3], 3) |
687                 put_byte(bytes[4], 4) |
688                 put_byte(bytes[5], 5));
689 }
690
691 int
692 altos_bt_list_next(struct altos_bt_list *bt_list,
693                    struct altos_bt_device *device)
694 {
695         for (;;) {
696                 BYTE            buffer[4096];
697                 DWORD           length = sizeof (buffer);;
698                 WSAQUERYSET     *results = (WSAQUERYSET *)buffer;
699                 CSADDR_INFO     *addr_info;
700                 int             retCode;
701                 SOCKADDR_BTH    *sockaddr_bth;
702
703                 memset(buffer, '\0', sizeof(buffer));
704
705                 retCode = WSALookupServiceNext(bt_list->lookup, LUP_SET, &length, results);
706
707                 if (retCode != 0) {
708                         int error = WSAGetLastError();
709                         if (error != WSAENOMORE && error != WSA_E_NO_MORE)
710                                 altos_set_last_winsock_error();
711                         return 0;
712                 }
713
714                 if (results->dwNumberOfCsAddrs > 0) {
715
716                         addr_info = results->lpcsaBuffer;
717
718                         strncpy(device->name, results->lpszServiceInstanceName, sizeof(device->name));
719                         device->name[sizeof(device->name)-1] = '\0';
720
721                         sockaddr_bth = (SOCKADDR_BTH *) addr_info->RemoteAddr.lpSockaddr;
722
723                         ba2str(sockaddr_bth->btAddr, device->addr);
724
725                         return 1;
726                 }
727         }
728 }
729
730 void
731 altos_bt_list_finish(struct altos_bt_list *bt_list)
732 {
733         WSALookupServiceEnd(bt_list->lookup);
734         free(bt_list);
735 }
736
737 void
738 altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device)
739 {
740         strncpy(device->name, name, sizeof (device->name));
741         device->name[sizeof(device->name)-1] = '\0';
742         strncpy(device->addr, addr, sizeof (device->addr));
743         device->addr[sizeof(device->addr)-1] = '\0';
744 }
745
746 struct altos_file *
747 altos_bt_open(struct altos_bt_device *device)
748 {
749         struct altos_file_windows       *file;
750         SOCKADDR_BTH            sockaddr_bth;
751         int                     ret;
752
753         file = calloc(1, sizeof (struct altos_file_windows));
754         if (!file) {
755                 return NULL;
756         }
757
758         file->is_winsock = TRUE;
759         file->socket = WSASocket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM, NULL, 0, WSA_FLAG_OVERLAPPED);
760
761         if (file->socket == INVALID_SOCKET) {
762                 altos_set_last_winsock_error();
763                 free(file);
764                 return NULL;
765         }
766
767         memset(&sockaddr_bth, '\0', sizeof (sockaddr_bth));
768         sockaddr_bth.addressFamily = AF_BTH;
769         sockaddr_bth.btAddr = str2ba(device->addr);
770         sockaddr_bth.port = altos_bt_port(device);
771
772         ret = connect(file->socket, (SOCKADDR *) &sockaddr_bth, sizeof (sockaddr_bth));
773
774         if (ret != 0) {
775                 altos_set_last_winsock_error();
776                 closesocket(file->socket);
777                 free(file);
778                 log_message("Connection attempted to address %s port %d\n", device->addr, sockaddr_bth.port);
779                 return NULL;
780         }
781         return &file->file;
782 }
783
784 void
785 altos_pause_one_second(void)
786 {
787         Sleep(1000);
788 }