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 
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  if (ret)
368  errno = ret;
369 
370 free_and_exit:
371  free(mres);
372  free(mcmd);
373  return ret;
374 }
375 
376 static 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 
411 static 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 
427 static 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 
455 struct 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 
487 err_close:
488  CloseHandle(wdev->hdl);
489 err_free:
490  free(wdev);
491  return NULL;
492 }
493 
494 struct 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 
526 out:
527  SetupDiDestroyDeviceInfoList(devinfo);
528  return dev;
529 }
530 
531 struct 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 
574 struct switchtec_dev *switchtec_open_i2c(const char *path, int i2c_addr)
575 {
576  errno = ENOTSUP;
577  return NULL;
578 }
579 
580 struct switchtec_dev *switchtec_open_i2c_by_adapter(int adapter, int i2c_addr)
581 {
582  errno = ENOTSUP;
583  return NULL;
584 }
585 
586 struct switchtec_dev *switchtec_open_uart(int fd)
587 {
588  errno = ENOTSUP;
589  return NULL;
590 }
591 
592 struct switchtec_dev *switchtec_open_eth(const char *ip, const int inst)
593 {
594  errno = ENOTSUP;
595  return NULL;
596 }
597 
598 #endif
char product_rev[8]
Product revision.
Definition: switchtec.h:136
Gas Operations for platforms that the gas is mapped into the address space.
struct switchtec_dev * switchtec_open_eth(const char *ip, const int inst)
Open a switchtec device over ethernet.
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 struct switchtec_gas * gasptr_t
Shortform for a pointer to the GAS register space.
Definition: switchtec.h:80
char product_id[32]
Product ID.
Definition: switchtec.h:135
int switchtec_list(struct switchtec_device_info **devlist)
List all the switchtec devices in the system.
GAS Accessor functions.
char path[PATH_MAX]
Path to the device.
Definition: switchtec.h:138
char desc[256]
Device description, if available.
Definition: switchtec.h:133
Main Switchtec header.
char pci_dev[256]
PCI BDF string.
Definition: switchtec.h:134
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:137
char name[256]
Device name, eg. switchtec0.
Definition: switchtec.h:132
Represents a Switchtec device in the switchtec_list() function.
Definition: switchtec.h:131
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)