Switchtec Userspace PROJECT_NUMBER = 3.1
windows.c
1/*
2 * Microsemi Switchtec(tm) PCIe Management Library
3 * Copyright (c) 2017, Microsemi Corporation
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 * OTHER DEALINGS IN THE SOFTWARE.
22 *
23 */
24
25#include "switchtec/switchtec.h"
26#include "switchtec/portable.h"
27#include "switchtec/gas.h"
28#include "switchtec/utils.h"
29#include "../switchtec_priv.h"
30#include "gasops.h"
31
32#ifdef __WINDOWS__
33#include "windows/switchtec_public.h"
34#include "mmap_gas.h"
35
36#include <setupapi.h>
37
38#include <errno.h>
39#include <stdio.h>
40
41struct switchtec_windows {
42 struct switchtec_dev dev;
43 HANDLE hdl;
44};
45
46#define to_switchtec_windows(d) \
47 ((struct switchtec_windows *) \
48 ((char *)d - offsetof(struct switchtec_windows, dev)))
49
50static int earlier_error = 0;
51
52const char *platform_strerror(void)
53{
54 static char errmsg[500] = "";
55 int err = GetLastError();
56
57 if (!err && earlier_error)
58 err = earlier_error;
59
60 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM |
61 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err,
62 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
63 errmsg, sizeof (errmsg), NULL);
64
65 if (!strlen(errmsg))
66 sprintf(errmsg, "Error %d", err);
67 return errmsg;
68}
69
70static void platform_perror(const char *msg)
71{
72 fprintf(stderr, "%s: %s\n", msg, platform_strerror());
73}
74
75static int count_devices(void)
76{
77 HDEVINFO devinfo;
78 DWORD count = 0;
79 SP_DEVICE_INTERFACE_DATA deviface;
80
81 devinfo = SetupDiGetClassDevs(&SWITCHTEC_INTERFACE_GUID,
82 NULL, NULL, DIGCF_DEVICEINTERFACE |
83 DIGCF_PRESENT);
84
85 deviface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
86
87 while (SetupDiEnumDeviceInterfaces(devinfo, NULL,
88 &SWITCHTEC_INTERFACE_GUID,
89 count++, &deviface));
90
91 return count - 1;
92}
93
94static BOOL get_path(HDEVINFO devinfo, SP_DEVICE_INTERFACE_DATA *deviface,
95 SP_DEVINFO_DATA *devdata, char *path, size_t path_size)
96{
97 DWORD size;
98 SP_DEVICE_INTERFACE_DETAIL_DATA *devdetail;
99 BOOL status = TRUE;
100 char *hash;
101
102 devdata->cbSize = sizeof(SP_DEVINFO_DATA);
103
104 SetupDiGetDeviceInterfaceDetail(devinfo, deviface, NULL, 0, &size,
105 NULL);
106
107 devdetail = malloc(size);
108 if (!devdetail) {
109 perror("Enumeration");
110 return FALSE;
111 }
112
113 devdetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
114
115 status = SetupDiGetDeviceInterfaceDetail(devinfo, deviface, devdetail,
116 size, NULL, devdata);
117 if (!status) {
118 platform_perror("SetupDiGetDeviceInterfaceDetail");
119 goto out;
120 }
121
122 strcpy_s(path, path_size, devdetail->DevicePath);
123
124 /* Chop off the GUID */
125 hash = strrchr(path, '#');
126 if (hash)
127 *hash = 0;
128
129out:
130 free(devdetail);
131 return status;
132}
133
134static BOOL get_pci_address(HDEVINFO devinfo, SP_DEVINFO_DATA *devdata,
135 int *bus, int *dev, int *func)
136{
137 BOOL status;
138 int ret;
139 char loc[256];
140
141 status = SetupDiGetDeviceRegistryProperty(devinfo, devdata,
142 SPDRP_LOCATION_INFORMATION, NULL,
143 (BYTE *)loc, sizeof(loc), NULL);
144 if (!status) {
145 platform_perror("SetupDiGetDeviceRegistryProperty (LOC)");
146 return FALSE;
147 }
148
149 ret = sscanf(loc, "PCI bus %d, device %d, function %d", bus, dev, func);
150 if (ret != 3) {
151 fprintf(stderr, "Error parsing PCI BUS: '%s'\n", loc);
152 return FALSE;
153 }
154
155 return TRUE;
156}
157
158static void get_pci_address_str(HDEVINFO devinfo, SP_DEVINFO_DATA *devdata,
159 char *res, size_t res_size)
160{
161 BOOL status;
162 int bus, dev, func;
163
164 status = get_pci_address(devinfo, devdata, &bus, &dev, &func);
165 if (!status)
166 snprintf(res, res_size, "??:??.?");
167 else
168 snprintf(res, res_size, "%02x:%02x.%x", bus, dev, func);
169}
170
171static void get_description(HDEVINFO devinfo, SP_DEVINFO_DATA *devdata,
172 char *res, size_t res_size)
173{
174 SetupDiGetDeviceRegistryProperty(devinfo, devdata,
175 SPDRP_DEVICEDESC, NULL,(BYTE *)res, res_size, NULL);
176}
177
178/*
179 * Sigh... Mingw doesn't define this API yet in it's header and the library
180 * only has the WCHAR version.
181 */
182WINSETUPAPI WINBOOL WINAPI SetupDiGetDevicePropertyW(HDEVINFO DeviceInfoSet,
183 PSP_DEVINFO_DATA DeviceInfoData, const DEVPROPKEY *PropertyKey,
184 DEVPROPTYPE *PropertyType, PBYTE PropertyBuffer,
185 DWORD PropertyBufferSize, PDWORD RequiredSize,
186 DWORD Flags);
187
188static void get_property(HDEVINFO devinfo, SP_DEVINFO_DATA *devdata,
189 const DEVPROPKEY *propkey, char *res, size_t res_size)
190{
191 DEVPROPTYPE ptype;
192 WCHAR buf[res_size];
193
194 SetupDiGetDevicePropertyW(devinfo, devdata, propkey, &ptype,
195 (PBYTE)buf, sizeof(buf), NULL, 0);
196 wcstombs(res, buf, res_size);
197}
198
199static void get_fw_property(HDEVINFO devinfo, SP_DEVINFO_DATA *devdata,
200 char *res, size_t res_size)
201{
202 char buf[16];
203 long fw_ver;
204
205 get_property(devinfo, devdata, &SWITCHTEC_PROP_FW_VERSION,
206 buf, sizeof(buf));
207
208 fw_ver = strtol(buf, NULL, 16);
209
210 if (fw_ver < 0)
211 snprintf(res, res_size, "unknown");
212 else
213 version_to_string(fw_ver, res, res_size);
214}
215
216static void append_guid(const char *path, char *path_with_guid, size_t bufsize,
217 const GUID *guid)
218{
219 snprintf(path_with_guid, bufsize,
220 "%s#{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
221 path, guid->Data1, guid->Data2, guid->Data3,
222 guid->Data4[0], guid->Data4[1], guid->Data4[2],
223 guid->Data4[3], guid->Data4[4], guid->Data4[5],
224 guid->Data4[6], guid->Data4[7]);
225}
226
227#ifdef __CHECKER__
228#define __force __attribute__((force))
229#else
230#define __force
231#endif
232
233static BOOL map_gas(struct switchtec_windows *wdev)
234{
235 BOOL status;
236 struct switchtec_gas_map map;
237
238 status = DeviceIoControl(wdev->hdl, IOCTL_SWITCHTEC_GAS_MAP, NULL, 0,
239 &map, sizeof(map), NULL, NULL);
240 if (!status) {
241 earlier_error = GetLastError();
242 return status;
243 }
244
245 wdev->dev.gas_map = (gasptr_t __force)map.gas;
246 wdev->dev.gas_map_size = map.length;
247 return TRUE;
248}
249
250static void unmap_gas(struct switchtec_windows *wdev)
251{
252 struct switchtec_gas_map map = {
253 .gas = (void * __force)wdev->dev.gas_map,
254 .length = wdev->dev.gas_map_size,
255 };
256
257 DeviceIoControl(wdev->hdl, IOCTL_SWITCHTEC_GAS_UNMAP, &map, sizeof(map),
258 NULL, 0, NULL, NULL);
259}
260
261static void windows_close(struct switchtec_dev *dev)
262{
263 struct switchtec_windows *wdev = to_switchtec_windows(dev);
264
265 unmap_gas(wdev);
266 CloseHandle(wdev->hdl);
267}
268
269int switchtec_list(struct switchtec_device_info **devlist)
270{
271 HDEVINFO devinfo;
272 SP_DEVICE_INTERFACE_DATA deviface;
273 SP_DEVINFO_DATA devdata;
274 struct switchtec_device_info *dl;
275
276 BOOL status;
277 DWORD idx = 0;
278 DWORD cnt = 0;
279
280 dl = *devlist = calloc(count_devices(),
281 sizeof(struct switchtec_device_info));
282 if (!dl) {
283 errno = ENOMEM;
284 return -errno;
285 }
286
287 devinfo = SetupDiGetClassDevs(&SWITCHTEC_INTERFACE_GUID,
288 NULL, NULL, DIGCF_DEVICEINTERFACE |
289 DIGCF_PRESENT);
290 if (devinfo == INVALID_HANDLE_VALUE)
291 return 0;
292
293 deviface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
294
295 while (SetupDiEnumDeviceInterfaces(devinfo, NULL,
296 &SWITCHTEC_INTERFACE_GUID,
297 idx, &deviface))
298 {
299 snprintf(dl[cnt].name, sizeof(dl[cnt].name),
300 "switchtec%ld", idx++);
301
302 status = get_path(devinfo, &deviface, &devdata,
303 dl[cnt].path, sizeof(dl[cnt].path));
304 if (!status)
305 continue;
306
307 get_pci_address_str(devinfo, &devdata, dl[cnt].pci_dev,
308 sizeof(dl[cnt].pci_dev));
309 get_description(devinfo, &devdata, dl[cnt].desc,
310 sizeof(dl[cnt].desc));
311
312 get_property(devinfo, &devdata, &SWITCHTEC_PROP_PRODUCT_ID,
313 dl[cnt].product_id, sizeof(dl[cnt].product_id));
314 get_property(devinfo, &devdata, &SWITCHTEC_PROP_PRODUCT_REV,
315 dl[cnt].product_rev, sizeof(dl[cnt].product_rev));
316 get_fw_property(devinfo, &devdata, dl[cnt].fw_version,
317 sizeof(dl[cnt].fw_version));
318 cnt++;
319 }
320
321 SetupDiDestroyDeviceInfoList(devinfo);
322
323 return cnt;
324}
325
326static int windows_cmd(struct switchtec_dev *dev, uint32_t cmd,
327 const void *payload, size_t payload_len, void *resp,
328 size_t resp_len)
329{
330 struct switchtec_windows *wdev = to_switchtec_windows(dev);
331 BOOL status;
332 int ret;
333
334 struct switchtec_mrpc_cmd *mcmd;
335 struct switchtec_mrpc_result *mres;
336 size_t mcmd_len, mres_len;
337
338 mcmd_len = offsetof(struct switchtec_mrpc_cmd, data) + payload_len;
339 mres_len = offsetof(struct switchtec_mrpc_result, data) + resp_len;
340
341 mcmd = calloc(1, mcmd_len);
342 if (!mcmd)
343 return -errno;
344
345 mres = calloc(1, mres_len);
346 if (!mres) {
347 free(mcmd);
348 return -errno;
349 }
350
351 mcmd->cmd = cmd;
352 memcpy(mcmd->data, payload, payload_len);
353
354 status = DeviceIoControl(wdev->hdl, IOCTL_SWITCHTEC_MRPC,
355 mcmd, (DWORD)mcmd_len,
356 mres, (DWORD)mres_len,
357 NULL, NULL);
358 if (!status) {
359 ret = -EIO;
360 goto free_and_exit;
361 }
362
363 if (resp)
364 memcpy(resp, mres->data, resp_len);
365
366 ret = mres->status;
367 if (ret)
368 errno = ret;
369
370free_and_exit:
371 free(mres);
372 free(mcmd);
373 return ret;
374}
375
376static int windows_event_wait(struct switchtec_dev *dev, int timeout_ms)
377{
378 struct switchtec_windows *wdev = to_switchtec_windows(dev);
379 OVERLAPPED overlap = {
380 .hEvent = CreateEvent(NULL, TRUE, FALSE, NULL),
381 };
382 DWORD ret;
383 DWORD transferred;
384 BOOL error;
385
386 errno = 0;
387
388 if (!overlap.hEvent)
389 return -1;
390
391 DeviceIoControl(wdev->hdl, IOCTL_SWITCHTEC_WAIT_FOR_EVENT, NULL, 0,
392 NULL, 0, NULL, &overlap);
393 if (GetLastError() != ERROR_IO_PENDING)
394 return -1;
395
396 ret = WaitForSingleObject(overlap.hEvent, timeout_ms);
397 if (ret == WAIT_TIMEOUT) {
398 CancelIoEx(wdev->hdl, &overlap);
399 return 0;
400 } else if (ret) {
401 return -1;
402 }
403
404 error = GetOverlappedResult(wdev->hdl, &overlap, &transferred, FALSE);
405 if (!error)
406 return -1;
407
408 return 1;
409}
410
411static gasptr_t windows_gas_map(struct switchtec_dev *dev, int writeable,
412 size_t *map_size)
413{
414 int ret;
415
416 if (map_size)
417 *map_size = dev->gas_map_size;
418
419 ret = gasop_access_check(dev);
420 if (ret) {
421 errno = ENODEV;
422 return SWITCHTEC_MAP_FAILED;
423 }
424 return dev->gas_map;
425}
426
427static const struct switchtec_ops windows_ops = {
428 .close = windows_close,
429 .cmd = windows_cmd,
430 .gas_map = windows_gas_map,
431 .event_wait = windows_event_wait,
432
433 .get_device_id = gasop_get_device_id,
434 .get_fw_version = gasop_get_fw_version,
435 .pff_to_port = gasop_pff_to_port,
436 .port_to_pff = gasop_port_to_pff,
437 .flash_part = gasop_flash_part,
438 .event_summary = gasop_event_summary,
439 .event_ctl = gasop_event_ctl,
440
441 .gas_read8 = mmap_gas_read8,
442 .gas_read16 = mmap_gas_read16,
443 .gas_read32 = mmap_gas_read32,
444 .gas_read64 = mmap_gas_read64,
445 .gas_write8 = mmap_gas_write8,
446 .gas_write16 = mmap_gas_write16,
447 .gas_write32 = mmap_gas_write32,
448 .gas_write32_no_retry = mmap_gas_write32,
449 .gas_write64 = mmap_gas_write64,
450 .memcpy_to_gas = mmap_memcpy_to_gas,
451 .memcpy_from_gas = mmap_memcpy_from_gas,
452 .write_from_gas = mmap_write_from_gas,
453};
454
455struct switchtec_dev *switchtec_open_by_path(const char *path)
456{
457 struct switchtec_windows *wdev;
458 char path_with_guid[MAX_PATH];
459 int idx;
460
461 if (sscanf(path, "/dev/switchtec%d", &idx) == 1)
462 return switchtec_open_by_index(idx);
463
464 wdev = malloc(sizeof(*wdev));
465 if (!wdev)
466 return NULL;
467
468 append_guid(path, path_with_guid, sizeof(path_with_guid),
469 &SWITCHTEC_INTERFACE_GUID);
470
471 wdev->hdl = CreateFile(path_with_guid, GENERIC_READ | GENERIC_WRITE,
472 FILE_SHARE_READ | FILE_SHARE_WRITE,
473 NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
474
475 if (wdev->hdl == INVALID_HANDLE_VALUE)
476 goto err_free;
477
478 if (!map_gas(wdev))
479 goto err_close;
480
481 wdev->dev.ops = &windows_ops;
482
483 gasop_set_partition_info(&wdev->dev);
484
485 return &wdev->dev;
486
487err_close:
488 CloseHandle(wdev->hdl);
489err_free:
490 free(wdev);
491 return NULL;
492}
493
494struct switchtec_dev *switchtec_open_by_index(int index)
495{
496 HDEVINFO devinfo;
497 SP_DEVICE_INTERFACE_DATA deviface;
498 SP_DEVINFO_DATA devdata;
499 char path[MAX_PATH];
500 struct switchtec_dev *dev = NULL;
501 BOOL status;
502
503 devinfo = SetupDiGetClassDevs(&SWITCHTEC_INTERFACE_GUID,
504 NULL, NULL, DIGCF_DEVICEINTERFACE |
505 DIGCF_PRESENT);
506 if (devinfo == INVALID_HANDLE_VALUE)
507 return NULL;
508
509 deviface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
510
511 status = SetupDiEnumDeviceInterfaces(devinfo, NULL,
512 &SWITCHTEC_INTERFACE_GUID,
513 index, &deviface);
514 if (!status) {
515 errno = ENODEV;
516 goto out;
517 }
518
519 status = get_path(devinfo, &deviface, &devdata,
520 path, sizeof(path));
521 if (!status)
522 goto out;
523
524 dev = switchtec_open_by_path(path);
525
526out:
527 SetupDiDestroyDeviceInfoList(devinfo);
528 return dev;
529}
530
531struct switchtec_dev *switchtec_open_by_pci_addr(int domain, int bus,
532 int device, int func)
533{
534 HDEVINFO devinfo;
535 SP_DEVICE_INTERFACE_DATA deviface;
536 SP_DEVINFO_DATA devdata;
537 char path[MAX_PATH];
538 struct switchtec_dev *dev = NULL;
539 BOOL status;
540 int dbus, ddevice, dfunc;
541 int idx = 0;
542
543 devinfo = SetupDiGetClassDevs(&SWITCHTEC_INTERFACE_GUID,
544 NULL, NULL, DIGCF_DEVICEINTERFACE |
545 DIGCF_PRESENT);
546 if (devinfo == INVALID_HANDLE_VALUE)
547 return NULL;
548
549 deviface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
550
551 while (SetupDiEnumDeviceInterfaces(devinfo, NULL,
552 &SWITCHTEC_INTERFACE_GUID,
553 idx++, &deviface))
554 {
555 status = get_path(devinfo, &deviface, &devdata,
556 path, sizeof(path));
557 if (!status)
558 continue;
559
560 get_pci_address(devinfo, &devdata, &dbus, &ddevice, &dfunc);
561 if (dbus == bus && ddevice == device && dfunc == func) {
562 dev = switchtec_open_by_path(path);
563 break;
564 }
565 }
566
567 if (!dev)
568 errno = ENODEV;
569
570 SetupDiDestroyDeviceInfoList(devinfo);
571 return dev;
572}
573
574struct switchtec_dev *switchtec_open_i2c(const char *path, int i2c_addr)
575{
576 errno = ENOTSUP;
577 return NULL;
578}
579
580struct switchtec_dev *switchtec_open_i2c_by_adapter(int adapter, int i2c_addr)
581{
582 errno = ENOTSUP;
583 return NULL;
584}
585
586struct switchtec_dev *switchtec_open_uart(int fd)
587{
588 errno = ENOTSUP;
589 return NULL;
590}
591
592struct switchtec_dev *switchtec_open_eth(const char *ip, const int inst)
593{
594 errno = ENOTSUP;
595 return NULL;
596}
597
598#endif
GAS Accessor functions.
struct switchtec_dev * switchtec_open_uart(int fd)
Open a switchtec device behind a uart device.
int switchtec_list(struct switchtec_device_info **devlist)
List all the switchtec devices in the system.
struct switchtec_dev * switchtec_open_by_index(int index)
Open a switchtec device by index.
struct switchtec_dev * switchtec_open_i2c(const char *path, int i2c_addr)
Open a switchtec device behind an I2C device.
struct switchtec_dev * switchtec_open_by_path(const char *path)
Open a switchtec device by path.
struct switchtec_dev * switchtec_open_eth(const char *ip, const int inst)
Open a switchtec device over ethernet.
struct switchtec_dev * switchtec_open_by_pci_addr(int domain, int bus, int device, int func)
Open a switchtec device by PCI address (BDF)
gasptr_t switchtec_gas_map(struct switchtec_dev *dev, int writeable, size_t *map_size)
Map the GAS and return a pointer to access the gas.
Definition: platform.c:255
Gas Operations for platforms that the gas is mapped into the address space.
Represents a Switchtec device in the switchtec_list() function.
Definition: switchtec.h:131
char fw_version[32]
Firmware version.
Definition: switchtec.h:137
char pci_dev[256]
PCI BDF string.
Definition: switchtec.h:134
char desc[256]
Device description, if available.
Definition: switchtec.h:133
char path[PATH_MAX]
Path to the device.
Definition: switchtec.h:138
char name[256]
Device name, eg. switchtec0.
Definition: switchtec.h:132
char product_id[32]
Product ID.
Definition: switchtec.h:135
char product_rev[8]
Product revision.
Definition: switchtec.h:136
Main Switchtec header.
__gas struct switchtec_gas * gasptr_t
Shortform for a pointer to the GAS register space.
Definition: switchtec.h:80