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