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