Switchtec Userspace  PROJECT_NUMBER = PROJECT_NUMBER=PROJECT_NUMBER = 2.2
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 
41 struct 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 
50 static int earlier_error = 0;
51 
52 const 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 
70 static void platform_perror(const char *msg)
71 {
72  fprintf(stderr, "%s: %s\n", msg, platform_strerror());
73 }
74 
75 static 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 
94 static 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 
129 out:
130  free(devdetail);
131  return status;
132 }
133 
134 static 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 
158 static 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 
171 static 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  */
182 WINSETUPAPI 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 
188 static 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 
199 static 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 
216 static 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 
233 static 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 
250 static 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 
261 static 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 
269 int 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 
326 static 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 
368 free_and_exit:
369  free(mres);
370  free(mcmd);
371  return ret;
372 }
373 
374 static int windows_event_wait(struct switchtec_dev *dev, int timeout_ms)
375 {
376  struct switchtec_windows *wdev = to_switchtec_windows(dev);
377  OVERLAPPED overlap = {
378  .hEvent = CreateEvent(NULL, TRUE, FALSE, NULL),
379  };
380  DWORD ret;
381  DWORD transferred;
382  BOOL error;
383 
384  errno = 0;
385 
386  if (!overlap.hEvent)
387  return -1;
388 
389  DeviceIoControl(wdev->hdl, IOCTL_SWITCHTEC_WAIT_FOR_EVENT, NULL, 0,
390  NULL, 0, NULL, &overlap);
391  if (GetLastError() != ERROR_IO_PENDING)
392  return -1;
393 
394  ret = WaitForSingleObject(overlap.hEvent, timeout_ms);
395  if (ret == WAIT_TIMEOUT) {
396  CancelIoEx(wdev->hdl, &overlap);
397  return 0;
398  } else if (ret) {
399  return -1;
400  }
401 
402  error = GetOverlappedResult(wdev->hdl, &overlap, &transferred, FALSE);
403  if (!error)
404  return -1;
405 
406  return 1;
407 }
408 
409 static gasptr_t windows_gas_map(struct switchtec_dev *dev, int writeable,
410  size_t *map_size)
411 {
412  int ret;
413 
414  if (map_size)
415  *map_size = dev->gas_map_size;
416 
417  ret = gasop_access_check(dev);
418  if (ret) {
419  errno = ENODEV;
420  return SWITCHTEC_MAP_FAILED;
421  }
422  return dev->gas_map;
423 }
424 
425 static const struct switchtec_ops windows_ops = {
426  .close = windows_close,
427  .cmd = windows_cmd,
428  .gas_map = windows_gas_map,
429  .event_wait = windows_event_wait,
430 
431  .get_device_id = gasop_get_device_id,
432  .get_fw_version = gasop_get_fw_version,
433  .pff_to_port = gasop_pff_to_port,
434  .port_to_pff = gasop_port_to_pff,
435  .flash_part = gasop_flash_part,
436  .event_summary = gasop_event_summary,
437  .event_ctl = gasop_event_ctl,
438 
439  .gas_read8 = mmap_gas_read8,
440  .gas_read16 = mmap_gas_read16,
441  .gas_read32 = mmap_gas_read32,
442  .gas_read64 = mmap_gas_read64,
443  .gas_write8 = mmap_gas_write8,
444  .gas_write16 = mmap_gas_write16,
445  .gas_write32 = mmap_gas_write32,
446  .gas_write32_no_retry = mmap_gas_write32,
447  .gas_write64 = mmap_gas_write64,
448  .memcpy_to_gas = mmap_memcpy_to_gas,
449  .memcpy_from_gas = mmap_memcpy_from_gas,
450  .write_from_gas = mmap_write_from_gas,
451 };
452 
453 struct switchtec_dev *switchtec_open_by_path(const char *path)
454 {
455  struct switchtec_windows *wdev;
456  char path_with_guid[MAX_PATH];
457  int idx;
458 
459  if (sscanf(path, "/dev/switchtec%d", &idx) == 1)
460  return switchtec_open_by_index(idx);
461 
462  wdev = malloc(sizeof(*wdev));
463  if (!wdev)
464  return NULL;
465 
466  append_guid(path, path_with_guid, sizeof(path_with_guid),
467  &SWITCHTEC_INTERFACE_GUID);
468 
469  wdev->hdl = CreateFile(path_with_guid, GENERIC_READ | GENERIC_WRITE,
470  FILE_SHARE_READ | FILE_SHARE_WRITE,
471  NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
472 
473  if (wdev->hdl == INVALID_HANDLE_VALUE)
474  goto err_free;
475 
476  if (!map_gas(wdev))
477  goto err_close;
478 
479  wdev->dev.ops = &windows_ops;
480 
481  gasop_set_partition_info(&wdev->dev);
482 
483  return &wdev->dev;
484 
485 err_close:
486  CloseHandle(wdev->hdl);
487 err_free:
488  free(wdev);
489  return NULL;
490 }
491 
492 struct switchtec_dev *switchtec_open_by_index(int index)
493 {
494  HDEVINFO devinfo;
495  SP_DEVICE_INTERFACE_DATA deviface;
496  SP_DEVINFO_DATA devdata;
497  char path[MAX_PATH];
498  struct switchtec_dev *dev = NULL;
499  BOOL status;
500 
501  devinfo = SetupDiGetClassDevs(&SWITCHTEC_INTERFACE_GUID,
502  NULL, NULL, DIGCF_DEVICEINTERFACE |
503  DIGCF_PRESENT);
504  if (devinfo == INVALID_HANDLE_VALUE)
505  return NULL;
506 
507  deviface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
508 
509  status = SetupDiEnumDeviceInterfaces(devinfo, NULL,
510  &SWITCHTEC_INTERFACE_GUID,
511  index, &deviface);
512  if (!status) {
513  errno = ENODEV;
514  goto out;
515  }
516 
517  status = get_path(devinfo, &deviface, &devdata,
518  path, sizeof(path));
519  if (!status)
520  goto out;
521 
522  dev = switchtec_open_by_path(path);
523 
524 out:
525  SetupDiDestroyDeviceInfoList(devinfo);
526  return dev;
527 }
528 
529 struct switchtec_dev *switchtec_open_by_pci_addr(int domain, int bus,
530  int device, int func)
531 {
532  HDEVINFO devinfo;
533  SP_DEVICE_INTERFACE_DATA deviface;
534  SP_DEVINFO_DATA devdata;
535  char path[MAX_PATH];
536  struct switchtec_dev *dev = NULL;
537  BOOL status;
538  int dbus, ddevice, dfunc;
539  int idx = 0;
540 
541  devinfo = SetupDiGetClassDevs(&SWITCHTEC_INTERFACE_GUID,
542  NULL, NULL, DIGCF_DEVICEINTERFACE |
543  DIGCF_PRESENT);
544  if (devinfo == INVALID_HANDLE_VALUE)
545  return NULL;
546 
547  deviface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
548 
549  while (SetupDiEnumDeviceInterfaces(devinfo, NULL,
550  &SWITCHTEC_INTERFACE_GUID,
551  idx++, &deviface))
552  {
553  status = get_path(devinfo, &deviface, &devdata,
554  path, sizeof(path));
555  if (!status)
556  continue;
557 
558  get_pci_address(devinfo, &devdata, &dbus, &ddevice, &dfunc);
559  if (dbus == bus && ddevice == device && dfunc == func) {
560  dev = switchtec_open_by_path(path);
561  break;
562  }
563  }
564 
565  if (!dev)
566  errno = ENODEV;
567 
568  SetupDiDestroyDeviceInfoList(devinfo);
569  return dev;
570 }
571 
572 struct switchtec_dev *switchtec_open_i2c(const char *path, int i2c_addr)
573 {
574  errno = ENOTSUP;
575  return NULL;
576 }
577 
578 struct switchtec_dev *switchtec_open_i2c_by_adapter(int adapter, int i2c_addr)
579 {
580  errno = ENOTSUP;
581  return NULL;
582 }
583 
584 struct switchtec_dev *switchtec_open_uart(int fd)
585 {
586  errno = ENOTSUP;
587  return NULL;
588 }
589 
590 #endif
char product_rev[8]
Product revision.
Definition: switchtec.h:129
Gas Operations for platforms that the gas is mapped into the address space.
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:223
__gas struct switchtec_gas * gasptr_t
Shortform for a pointer to the GAS register space.
Definition: switchtec.h:77
char product_id[32]
Product ID.
Definition: switchtec.h:128
int switchtec_list(struct switchtec_device_info **devlist)
List all the switchtec devices in the system.
GAS Accessor functions.
char desc[256]
Device description, if available.
Definition: switchtec.h:126
Main Switchtec header.
char pci_dev[256]
PCI BDF string.
Definition: switchtec.h:127
struct switchtec_dev * switchtec_open_uart(int fd)
Open a switchtec device behind a uart device.
struct switchtec_dev * switchtec_open_by_path(const char *path)
Open a switchtec device by path.
char fw_version[32]
Firmware version.
Definition: switchtec.h:130
char name[256]
Device name, eg. switchtec0.
Definition: switchtec.h:125
Represents a Switchtec device in the switchtec_list() function.
Definition: switchtec.h:124
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_index(int index)
Open a switchtec device by index.
struct switchtec_dev * switchtec_open_by_pci_addr(int domain, int bus, int device, int func)
Open a switchtec device by PCI address (BDF)