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