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; either version 2 of the License, or
7 * (at your option) any later version.
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.
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.
19 #include "libaltos_private.h"
31 #define USB_BUF_SIZE 64
33 struct altos_file_windows {
34 struct altos_file file;
37 /* Data used by the regular I/O */
43 /* Data used by winsock */
50 log_message(char *fmt, ...)
52 static FILE *log = NULL;
56 log = fopen("\\temp\\altos.txt", "w");
62 __ms_sprintf (buffer, "%4d-%02d-%02d %2d:%02d:%02d. ",
63 time.wYear, time.wMonth, time.wDay,
64 time.wHour, time.wMinute, time.wSecond);
67 __ms_vsprintf(buffer + strlen(buffer), fmt, a);
72 fputs(buffer, stdout);
78 _altos_set_last_windows_error(char *file, int line, DWORD error)
81 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
86 sizeof (message) / sizeof (TCHAR),
88 if (error != ERROR_SUCCESS)
89 log_message ("%s:%d (%d) %s\n", file, line, error, message);
90 altos_set_last_error(error, message);
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())
96 PUBLIC struct altos_list *
97 altos_list_start(void)
99 struct altos_list *list = calloc(1, sizeof (struct altos_list));
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();
115 PUBLIC struct altos_list *
116 altos_ftdi_list_start(void)
118 struct altos_list *list = calloc(1, sizeof (struct altos_list));
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();
135 unsigned int vid, pid;
138 { .vid = 0xfffe, .pid = 0x000d, .name = "EasyTimer" },
139 { .vid = 0xfffe, .pid = 0x0028, .name = "EasyMega" },
140 { .vid = 0xfffe, .pid = 0x002c, .name = "EasyMotor" },
145 altos_list_next(struct altos_list *list, struct altos_device *device)
147 SP_DEVINFO_DATA dev_info_data;
150 char friendlyname[256];
154 unsigned int vid, pid;
157 DWORD friendlyname_type;
158 DWORD friendlyname_len;
159 char instanceid[1024];
160 DWORD instanceid_len;
163 dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA);
164 while(SetupDiEnumDeviceInfo(list->dev_info, list->index,
169 dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data,
170 DICS_FLAG_GLOBAL, 0, DIREG_DEV,
172 if (dev_key == INVALID_HANDLE_VALUE) {
173 altos_set_last_windows_error();
182 vid = pid = serial = 0;
183 /* Fetch symbolic name for this device and parse out
184 * the vid/pid/serial info */
185 symbolic_len = sizeof(symbolic);
186 result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL,
187 symbolic, &symbolic_len);
189 altos_set_last_windows_error();
191 __ms_sscanf((char *) symbolic + sizeof("\\??\\USB#VID_") - 1,
193 __ms_sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1,
195 __ms_sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1,
198 if (vid == 0 || pid == 0 || serial == 0) {
199 if (SetupDiGetDeviceInstanceId(list->dev_info,
204 __ms_sscanf((char *) instanceid + sizeof("USB\\VID_") - 1,
206 __ms_sscanf((char *) instanceid + sizeof("USB\\VID_XXXX&PID_") - 1,
208 __ms_sscanf((char *) instanceid + sizeof("USB\\VID_XXXX&PID_XXXX\\") - 1,
211 altos_set_last_windows_error();
214 if (vid == 0 || pid == 0 || serial == 0) {
215 RegCloseKey(dev_key);
220 /* Fetch the com port name */
221 port_len = sizeof (port);
222 result = RegQueryValueEx(dev_key, "PortName", NULL, NULL,
224 RegCloseKey(dev_key);
226 altos_set_last_windows_error();
230 /* Fetch the device description which is the device name,
231 * with firmware that has unique USB ids */
232 friendlyname_len = sizeof (friendlyname);
233 if(!SetupDiGetDeviceRegistryProperty(list->dev_info,
237 (BYTE *)friendlyname,
238 sizeof(friendlyname),
241 altos_set_last_windows_error();
245 char *space = friendlyname;
254 for (i = 0; name_map[i].name; i++) {
255 if (name_map[i].vid == vid && name_map[i].pid == pid) {
256 strcpy(friendlyname, name_map[i].name);
261 device->vendor = vid;
262 device->product = pid;
263 device->serial = serial;
264 strcpy(device->name, friendlyname);
266 strcpy(device->path, (char *) port);
269 result = GetLastError();
270 if (result != ERROR_NO_MORE_ITEMS)
271 altos_set_last_windows_error();
276 altos_list_finish(struct altos_list *list)
278 SetupDiDestroyDeviceInfoList(list->dev_info);
283 altos_queue_read(struct altos_file_windows *file)
287 return LIBALTOS_SUCCESS;
289 if (!ReadFile(file->handle, file->file.in_data, USB_BUF_SIZE, &got, &file->ov_read)) {
290 if (GetLastError() != ERROR_IO_PENDING) {
291 altos_set_last_windows_error();
292 return LIBALTOS_ERROR;
294 file->pend_read = TRUE;
296 file->pend_read = FALSE;
297 file->file.in_read = 0;
298 file->file.in_used = got;
300 return LIBALTOS_SUCCESS;
304 altos_wait_read(struct altos_file_windows *file, int timeout)
309 if (!file->pend_read)
310 return LIBALTOS_SUCCESS;
315 ret = WaitForSingleObject(file->ov_read.hEvent, timeout);
318 if (!GetOverlappedResult(file->handle, &file->ov_read, &got, FALSE)) {
319 if (GetLastError () != ERROR_OPERATION_ABORTED)
320 altos_set_last_windows_error();
321 return LIBALTOS_ERROR;
323 file->pend_read = FALSE;
324 file->file.in_read = 0;
325 file->file.in_used = got;
328 return LIBALTOS_TIMEOUT;
331 altos_set_last_windows_error();
332 return LIBALTOS_ERROR;
334 return LIBALTOS_SUCCESS;
338 altos_fill(struct altos_file *file_common, int timeout)
340 struct altos_file_windows *file = (struct altos_file_windows *) file_common;
344 if (file->file.in_read < file->file.in_used)
345 return LIBALTOS_SUCCESS;
347 file->file.in_read = file->file.in_used = 0;
349 if (file->is_winsock) {
356 /* Check to see if the socket has been closed */
357 if (file->socket == INVALID_SOCKET)
358 return LIBALTOS_ERROR;
360 #define POLL_TIMEOUT 10000
362 /* Poll to see if the socket has been closed
363 * as select doesn't abort when that happens
366 thistimeout = timeout;
367 if (thistimeout > POLL_TIMEOUT)
368 thistimeout = POLL_TIMEOUT;
370 thistimeout = POLL_TIMEOUT;
373 timeval.tv_sec = thistimeout / 1000;
374 timeval.tv_usec = (thistimeout % 1000) * 1000;
377 FD_SET(file->socket, &readfds);
379 ret = select(1, &readfds, NULL, NULL, &timeval);
383 timeout -= thistimeout;
385 return LIBALTOS_TIMEOUT;
392 altos_set_last_winsock_error();
393 return LIBALTOS_ERROR;
398 if (file->socket == INVALID_SOCKET) {
399 altos_set_last_winsock_error();
400 return LIBALTOS_ERROR;
403 ret = recv(file->socket, (char *) file->file.in_data, USB_BUF_SIZE, 0);
406 altos_set_last_winsock_error();
407 return LIBALTOS_ERROR;
409 file->file.in_read = 0;
410 file->file.in_used = ret;
412 if (file->handle == INVALID_HANDLE_VALUE) {
413 altos_set_last_windows_error();
414 return LIBALTOS_ERROR;
417 ret = altos_queue_read(file);
420 ret = altos_wait_read(file, timeout);
425 return LIBALTOS_SUCCESS;
429 altos_flush(struct altos_file *file_common)
431 struct altos_file_windows *file = (struct altos_file_windows *) file_common;
433 unsigned char *data = file->file.out_data;
434 int used = file->file.out_used;
437 if (file->is_winsock) {
440 put = send(file->socket, (char *) data, used, 0);
442 altos_set_last_winsock_error();
443 return LIBALTOS_ERROR;
450 if (!WriteFile(file->handle, data, used, &put, &file->ov_write)) {
451 if (GetLastError() != ERROR_IO_PENDING) {
452 altos_set_last_windows_error();
453 return LIBALTOS_ERROR;
455 ret = WaitForSingleObject(file->ov_write.hEvent, INFINITE);
458 if (!GetOverlappedResult(file->handle, &file->ov_write, &put, FALSE)) {
459 altos_set_last_windows_error();
460 return LIBALTOS_ERROR;
464 altos_set_last_windows_error();
465 return LIBALTOS_ERROR;
472 file->file.out_used = 0;
473 return LIBALTOS_SUCCESS;
477 open_serial(char *full_name)
482 handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE,
483 0, NULL, OPEN_EXISTING,
484 FILE_FLAG_OVERLAPPED, NULL);
486 if (handle == INVALID_HANDLE_VALUE) {
487 altos_set_last_windows_error();
488 return INVALID_HANDLE_VALUE;
491 if (!GetCommState(handle, &dcb)) {
492 altos_set_last_windows_error();
494 return INVALID_HANDLE_VALUE;
496 dcb.BaudRate = CBR_9600;
499 dcb.fOutxCtsFlow = FALSE;
500 dcb.fOutxDsrFlow = FALSE;
501 dcb.fDtrControl = DTR_CONTROL_ENABLE;
502 dcb.fDsrSensitivity = FALSE;
503 dcb.fTXContinueOnXoff = FALSE;
506 dcb.fErrorChar = FALSE;
508 dcb.fRtsControl = RTS_CONTROL_ENABLE;
509 dcb.fAbortOnError = FALSE;
513 dcb.Parity = NOPARITY;
514 dcb.StopBits = ONESTOPBIT;
522 if (!SetCommState(handle, &dcb)) {
523 altos_set_last_windows_error();
525 return INVALID_HANDLE_VALUE;
530 PUBLIC struct altos_file *
531 altos_open(struct altos_device *device)
533 struct altos_file_windows *file = calloc (1, sizeof (struct altos_file_windows));
535 COMMTIMEOUTS timeouts;
541 strcpy(full_name, "\\\\.\\");
542 strcat(full_name, device->path);
544 file->handle = INVALID_HANDLE_VALUE;
546 for (i = 0; i < 5; i++) {
547 file->handle = open_serial(full_name);
548 if (file->handle != INVALID_HANDLE_VALUE)
550 altos_set_last_windows_error();
554 if (file->handle == INVALID_HANDLE_VALUE) {
559 /* The FTDI driver doesn't appear to work right unless you open it twice */
560 if (device->vendor == 0x0403) {
561 CloseHandle(file->handle);
562 file->handle = open_serial(full_name);
563 if (file->handle == INVALID_HANDLE_VALUE) {
569 timeouts.ReadIntervalTimeout = MAXDWORD;
570 timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
571 timeouts.ReadTotalTimeoutConstant = 1 << 30; /* almost forever */
572 timeouts.WriteTotalTimeoutMultiplier = 0;
573 timeouts.WriteTotalTimeoutConstant = 0;
574 SetCommTimeouts(file->handle, &timeouts);
576 file->ov_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
577 file->ov_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
583 altos_close(struct altos_file *file_common)
585 struct altos_file_windows *file = (struct altos_file_windows *) file_common;
587 if (file->is_winsock) {
588 SOCKET socket = file->socket;
589 if (socket != INVALID_SOCKET) {
590 file->socket = INVALID_SOCKET;
594 HANDLE handle = file->handle;
595 if (handle != INVALID_HANDLE_VALUE) {
596 HANDLE ov_read = file->ov_read.hEvent;
597 HANDLE ov_write = file->ov_write.hEvent;
598 file->handle = INVALID_HANDLE_VALUE;
599 file->ov_read.hEvent = INVALID_HANDLE_VALUE;
600 file->ov_write.hEvent = INVALID_HANDLE_VALUE;
601 PurgeComm(handle, PURGE_RXABORT|PURGE_RXCLEAR|PURGE_TXABORT|PURGE_TXCLEAR);
604 file->handle = INVALID_HANDLE_VALUE;
605 CloseHandle(ov_read);
606 CloseHandle(ov_write);
613 #define LUP_SET (LUP_RETURN_NAME| LUP_CONTAINERS | LUP_RETURN_ADDR | LUP_FLUSHCACHE |\
614 LUP_RETURN_TYPE | LUP_RETURN_BLOB | LUP_RES_SERVICE)
616 struct altos_bt_list {
621 struct altos_bt_list *
622 altos_bt_list_start(int inquiry_time)
624 struct altos_bt_list *bt_list;
625 WSAQUERYSET query_set;
628 /* Windows provides no way to set the time */
630 bt_list = calloc(1, sizeof (struct altos_bt_list));
632 altos_set_last_windows_error();
636 if ((retCode = WSAStartup(MAKEWORD(2,2),&bt_list->WSAData)) != 0) {
637 altos_set_last_winsock_error();
642 memset(&query_set, '\0', sizeof (query_set));
643 query_set.dwSize = sizeof(WSAQUERYSET);
644 query_set.dwNameSpace = NS_BTH;
646 retCode = WSALookupServiceBegin(&query_set, LUP_SET, &bt_list->lookup);
649 altos_set_last_winsock_error();
656 static unsigned char get_byte(BTH_ADDR ba, int shift)
658 return (ba >> ((5 - shift) << 3)) & 0xff;
661 static BTH_ADDR put_byte(unsigned char c, int shift)
663 return ((BTH_ADDR) c) << ((5 - shift) << 3);
667 ba2str(BTH_ADDR ba, char *str)
670 __ms_sprintf(str, "%02X:%02X:%02X:%02X:%02X:%02X",
682 unsigned int bytes[6];
684 __ms_sscanf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
691 return (put_byte(bytes[0], 0) |
692 put_byte(bytes[1], 1) |
693 put_byte(bytes[2], 2) |
694 put_byte(bytes[3], 3) |
695 put_byte(bytes[4], 4) |
696 put_byte(bytes[5], 5));
700 altos_bt_list_next(struct altos_bt_list *bt_list,
701 struct altos_bt_device *device)
705 DWORD length = sizeof (buffer);;
706 WSAQUERYSET *results = (WSAQUERYSET *)buffer;
707 CSADDR_INFO *addr_info;
709 SOCKADDR_BTH *sockaddr_bth;
711 memset(buffer, '\0', sizeof(buffer));
713 retCode = WSALookupServiceNext(bt_list->lookup, LUP_SET, &length, results);
716 int error = WSAGetLastError();
717 if (error != WSAENOMORE && error != WSA_E_NO_MORE)
718 altos_set_last_winsock_error();
722 if (results->dwNumberOfCsAddrs > 0) {
724 addr_info = results->lpcsaBuffer;
726 strncpy(device->name, results->lpszServiceInstanceName, sizeof(device->name));
727 device->name[sizeof(device->name)-1] = '\0';
729 sockaddr_bth = (SOCKADDR_BTH *) addr_info->RemoteAddr.lpSockaddr;
731 ba2str(sockaddr_bth->btAddr, device->addr);
739 altos_bt_list_finish(struct altos_bt_list *bt_list)
741 WSALookupServiceEnd(bt_list->lookup);
746 altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device)
748 strncpy(device->name, name, sizeof (device->name));
749 device->name[sizeof(device->name)-1] = '\0';
750 strncpy(device->addr, addr, sizeof (device->addr));
751 device->addr[sizeof(device->addr)-1] = '\0';
755 altos_bt_open(struct altos_bt_device *device)
757 struct altos_file_windows *file;
758 SOCKADDR_BTH sockaddr_bth;
762 file = calloc(1, sizeof (struct altos_file_windows));
767 file->is_winsock = TRUE;
768 file->socket = WSASocket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM, NULL, 0, WSA_FLAG_OVERLAPPED);
770 if (file->socket == INVALID_SOCKET) {
771 altos_set_last_winsock_error();
776 memset(&sockaddr_bth, '\0', sizeof (sockaddr_bth));
777 sockaddr_bth.addressFamily = AF_BTH;
778 sockaddr_bth.btAddr = str2ba(device->addr);
780 channel = altos_bt_port(device);
782 channel = BT_PORT_DEFAULT;
784 sockaddr_bth.port = channel;
786 ret = connect(file->socket, (SOCKADDR *) &sockaddr_bth, sizeof (sockaddr_bth));
789 altos_set_last_winsock_error();
790 closesocket(file->socket);
792 log_message("Connection attempted to address %s port %d\n", device->addr, sockaddr_bth.port);
799 altos_pause_one_second(void)