2 * Copyright © 2016 Keith Packard <keithp@keithp.com>
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.
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.
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.
18 #include "libaltos_private.h"
30 #define USB_BUF_SIZE 64
32 struct altos_file_windows {
33 struct altos_file file;
36 /* Data used by the regular I/O */
42 /* Data used by winsock */
49 log_message(char *fmt, ...)
51 static FILE *log = NULL;
55 log = fopen("\\temp\\altos.txt", "w");
61 sprintf (buffer, "%4d-%02d-%02d %2d:%02d:%02d. ",
62 time.wYear, time.wMonth, time.wDay,
63 time.wHour, time.wMinute, time.wSecond);
66 vsprintf(buffer + strlen(buffer), fmt, a);
71 fputs(buffer, stdout);
77 _altos_set_last_windows_error(char *file, int line, DWORD error)
80 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
85 sizeof (message) / sizeof (TCHAR),
87 if (error != ERROR_SUCCESS)
88 log_message ("%s:%d (%d) %s\n", file, line, error, message);
89 altos_set_last_error(error, message);
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())
95 PUBLIC struct altos_list *
96 altos_list_start(void)
98 struct altos_list *list = calloc(1, sizeof (struct altos_list));
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();
114 PUBLIC struct altos_list *
115 altos_ftdi_list_start(void)
117 struct altos_list *list = calloc(1, sizeof (struct altos_list));
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();
134 altos_list_next(struct altos_list *list, struct altos_device *device)
136 SP_DEVINFO_DATA dev_info_data;
139 char friendlyname[256];
143 unsigned int vid, pid;
146 DWORD friendlyname_type;
147 DWORD friendlyname_len;
148 char instanceid[1024];
149 DWORD instanceid_len;
151 dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA);
152 while(SetupDiEnumDeviceInfo(list->dev_info, list->index,
157 dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data,
158 DICS_FLAG_GLOBAL, 0, DIREG_DEV,
160 if (dev_key == INVALID_HANDLE_VALUE) {
161 altos_set_last_windows_error();
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);
177 altos_set_last_windows_error();
179 sscanf((char *) symbolic + sizeof("\\??\\USB#VID_") - 1,
181 sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1,
183 sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1,
186 if (vid == 0 || pid == 0 || serial == 0) {
187 if (SetupDiGetDeviceInstanceId(list->dev_info,
192 sscanf((char *) instanceid + sizeof("USB\\VID_") - 1,
194 sscanf((char *) instanceid + sizeof("USB\\VID_XXXX&PID_") - 1,
196 sscanf((char *) instanceid + sizeof("USB\\VID_XXXX&PID_XXXX\\") - 1,
199 altos_set_last_windows_error();
202 if (vid == 0 || pid == 0 || serial == 0) {
203 RegCloseKey(dev_key);
208 /* Fetch the com port name */
209 port_len = sizeof (port);
210 result = RegQueryValueEx(dev_key, "PortName", NULL, NULL,
212 RegCloseKey(dev_key);
214 altos_set_last_windows_error();
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,
225 (BYTE *)friendlyname,
226 sizeof(friendlyname),
229 altos_set_last_windows_error();
232 device->vendor = vid;
233 device->product = pid;
234 device->serial = serial;
235 strcpy(device->name, friendlyname);
237 strcpy(device->path, (char *) port);
240 result = GetLastError();
241 if (result != ERROR_NO_MORE_ITEMS)
242 altos_set_last_windows_error();
247 altos_list_finish(struct altos_list *list)
249 SetupDiDestroyDeviceInfoList(list->dev_info);
254 altos_queue_read(struct altos_file_windows *file)
258 return LIBALTOS_SUCCESS;
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;
265 file->pend_read = TRUE;
267 file->pend_read = FALSE;
268 file->file.in_read = 0;
269 file->file.in_used = got;
271 return LIBALTOS_SUCCESS;
275 altos_wait_read(struct altos_file_windows *file, int timeout)
280 if (!file->pend_read)
281 return LIBALTOS_SUCCESS;
286 ret = WaitForSingleObject(file->ov_read.hEvent, timeout);
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;
294 file->pend_read = FALSE;
295 file->file.in_read = 0;
296 file->file.in_used = got;
299 return LIBALTOS_TIMEOUT;
302 altos_set_last_windows_error();
303 return LIBALTOS_ERROR;
305 return LIBALTOS_SUCCESS;
309 altos_fill(struct altos_file *file_common, int timeout)
311 struct altos_file_windows *file = (struct altos_file_windows *) file_common;
315 if (file->file.in_read < file->file.in_used)
316 return LIBALTOS_SUCCESS;
318 file->file.in_read = file->file.in_used = 0;
320 if (file->is_winsock) {
327 /* Check to see if the socket has been closed */
328 if (file->socket == INVALID_SOCKET)
329 return LIBALTOS_ERROR;
331 #define POLL_TIMEOUT 10000
333 /* Poll to see if the socket has been closed
334 * as select doesn't abort when that happens
337 thistimeout = timeout;
338 if (thistimeout > POLL_TIMEOUT)
339 thistimeout = POLL_TIMEOUT;
341 thistimeout = POLL_TIMEOUT;
344 timeval.tv_sec = thistimeout / 1000;
345 timeval.tv_usec = (thistimeout % 1000) * 1000;
348 FD_SET(file->socket, &readfds);
350 ret = select(1, &readfds, NULL, NULL, &timeval);
354 timeout -= thistimeout;
356 return LIBALTOS_TIMEOUT;
363 altos_set_last_winsock_error();
364 return LIBALTOS_ERROR;
369 if (file->socket == INVALID_SOCKET) {
370 altos_set_last_winsock_error();
371 return LIBALTOS_ERROR;
374 ret = recv(file->socket, (char *) file->file.in_data, USB_BUF_SIZE, 0);
377 altos_set_last_winsock_error();
378 return LIBALTOS_ERROR;
380 file->file.in_read = 0;
381 file->file.in_used = ret;
383 if (file->handle == INVALID_HANDLE_VALUE) {
384 altos_set_last_windows_error();
385 return LIBALTOS_ERROR;
388 ret = altos_queue_read(file);
391 ret = altos_wait_read(file, timeout);
396 return LIBALTOS_SUCCESS;
400 altos_flush(struct altos_file *file_common)
402 struct altos_file_windows *file = (struct altos_file_windows *) file_common;
404 unsigned char *data = file->file.out_data;
405 int used = file->file.out_used;
408 if (file->is_winsock) {
411 put = send(file->socket, (char *) data, used, 0);
413 altos_set_last_winsock_error();
414 return LIBALTOS_ERROR;
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;
426 ret = WaitForSingleObject(file->ov_write.hEvent, INFINITE);
429 if (!GetOverlappedResult(file->handle, &file->ov_write, &put, FALSE)) {
430 altos_set_last_windows_error();
431 return LIBALTOS_ERROR;
435 altos_set_last_windows_error();
436 return LIBALTOS_ERROR;
443 file->file.out_used = 0;
444 return LIBALTOS_SUCCESS;
448 open_serial(char *full_name)
453 handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE,
454 0, NULL, OPEN_EXISTING,
455 FILE_FLAG_OVERLAPPED, NULL);
457 if (handle == INVALID_HANDLE_VALUE) {
458 altos_set_last_windows_error();
459 return INVALID_HANDLE_VALUE;
462 if (!GetCommState(handle, &dcb)) {
463 altos_set_last_windows_error();
465 return INVALID_HANDLE_VALUE;
467 dcb.BaudRate = CBR_9600;
470 dcb.fOutxCtsFlow = FALSE;
471 dcb.fOutxDsrFlow = FALSE;
472 dcb.fDtrControl = DTR_CONTROL_ENABLE;
473 dcb.fDsrSensitivity = FALSE;
474 dcb.fTXContinueOnXoff = FALSE;
477 dcb.fErrorChar = FALSE;
479 dcb.fRtsControl = RTS_CONTROL_ENABLE;
480 dcb.fAbortOnError = FALSE;
484 dcb.Parity = NOPARITY;
485 dcb.StopBits = ONESTOPBIT;
493 if (!SetCommState(handle, &dcb)) {
494 altos_set_last_windows_error();
496 return INVALID_HANDLE_VALUE;
501 PUBLIC struct altos_file *
502 altos_open(struct altos_device *device)
504 struct altos_file_windows *file = calloc (1, sizeof (struct altos_file_windows));
506 COMMTIMEOUTS timeouts;
512 strcpy(full_name, "\\\\.\\");
513 strcat(full_name, device->path);
515 file->handle = INVALID_HANDLE_VALUE;
517 for (i = 0; i < 5; i++) {
518 file->handle = open_serial(full_name);
519 if (file->handle != INVALID_HANDLE_VALUE)
521 altos_set_last_windows_error();
525 if (file->handle == INVALID_HANDLE_VALUE) {
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) {
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);
547 file->ov_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
548 file->ov_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
554 altos_close(struct altos_file *file_common)
556 struct altos_file_windows *file = (struct altos_file_windows *) file_common;
558 if (file->is_winsock) {
559 SOCKET socket = file->socket;
560 if (socket != INVALID_SOCKET) {
561 file->socket = INVALID_SOCKET;
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);
575 file->handle = INVALID_HANDLE_VALUE;
576 CloseHandle(ov_read);
577 CloseHandle(ov_write);
584 #define LUP_SET (LUP_RETURN_NAME| LUP_CONTAINERS | LUP_RETURN_ADDR | LUP_FLUSHCACHE |\
585 LUP_RETURN_TYPE | LUP_RETURN_BLOB | LUP_RES_SERVICE)
587 struct altos_bt_list {
592 struct altos_bt_list *
593 altos_bt_list_start(int inquiry_time)
595 struct altos_bt_list *bt_list;
596 WSAQUERYSET query_set;
599 /* Windows provides no way to set the time */
601 bt_list = calloc(1, sizeof (struct altos_bt_list));
603 altos_set_last_windows_error();
607 if ((retCode = WSAStartup(MAKEWORD(2,2),&bt_list->WSAData)) != 0) {
608 altos_set_last_winsock_error();
613 memset(&query_set, '\0', sizeof (query_set));
614 query_set.dwSize = sizeof(WSAQUERYSET);
615 query_set.dwNameSpace = NS_BTH;
617 retCode = WSALookupServiceBegin(&query_set, LUP_SET, &bt_list->lookup);
620 altos_set_last_winsock_error();
627 static unsigned char get_byte(BTH_ADDR ba, int shift)
629 return (ba >> ((5 - shift) << 3)) & 0xff;
632 static BTH_ADDR put_byte(unsigned char c, int shift)
634 return ((BTH_ADDR) c) << ((5 - shift) << 3);
638 ba2str(BTH_ADDR ba, char *str)
641 sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
653 unsigned int bytes[6];
655 sscanf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
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));
671 altos_bt_list_next(struct altos_bt_list *bt_list,
672 struct altos_bt_device *device)
676 DWORD length = sizeof (buffer);;
677 WSAQUERYSET *results = (WSAQUERYSET *)buffer;
678 CSADDR_INFO *addr_info;
680 SOCKADDR_BTH *sockaddr_bth;
682 memset(buffer, '\0', sizeof(buffer));
684 retCode = WSALookupServiceNext(bt_list->lookup, LUP_SET, &length, results);
687 int error = WSAGetLastError();
688 if (error != WSAENOMORE && error != WSA_E_NO_MORE)
689 altos_set_last_winsock_error();
693 if (results->dwNumberOfCsAddrs > 0) {
695 addr_info = results->lpcsaBuffer;
697 strncpy(device->name, results->lpszServiceInstanceName, sizeof(device->name));
698 device->name[sizeof(device->name)-1] = '\0';
700 sockaddr_bth = (SOCKADDR_BTH *) addr_info->RemoteAddr.lpSockaddr;
702 ba2str(sockaddr_bth->btAddr, device->addr);
710 altos_bt_list_finish(struct altos_bt_list *bt_list)
712 WSALookupServiceEnd(bt_list->lookup);
717 altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device)
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';
726 altos_bt_open(struct altos_bt_device *device)
728 struct altos_file_windows *file;
729 SOCKADDR_BTH sockaddr_bth;
732 file = calloc(1, sizeof (struct altos_file_windows));
737 file->is_winsock = TRUE;
738 file->socket = WSASocket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM, NULL, 0, WSA_FLAG_OVERLAPPED);
740 if (file->socket == INVALID_SOCKET) {
741 altos_set_last_winsock_error();
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;
751 ret = connect(file->socket, (SOCKADDR *) &sockaddr_bth, sizeof (sockaddr_bth));
754 altos_set_last_winsock_error();
755 closesocket(file->socket);