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