Velocity Userspace
linux.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 #ifdef __linux__
26 
27 #define SWITCHTEC_LIB_LINUX
28 
29 #include "../switchtec_priv.h"
30 #include "switchtec/switchtec.h"
31 #include "switchtec/pci.h"
32 #include "switchtec/utils.h"
33 #include "mmap_gas.h"
34 #include "gasops.h"
35 
36 #include <linux/switchtec_ioctl.h>
37 
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include <endian.h>
41 #include <dirent.h>
42 #include <libgen.h>
43 #include <sys/stat.h>
44 #include <sys/ioctl.h>
45 #include <sys/mman.h>
46 #include <sys/sysmacros.h>
47 #include <glob.h>
48 #include <poll.h>
49 
50 #include <errno.h>
51 #include <string.h>
52 #include <stddef.h>
53 
54 static const char *sys_path = "/sys/class/switchtec";
55 
57  struct switchtec_dev dev;
58  int fd;
59 };
60 
61 #define to_switchtec_linux(d) \
62  ((struct switchtec_linux *) \
63  ((char *)d - offsetof(struct switchtec_linux, dev)))
64 
65 const char *platform_strerror(void)
66 {
67  return "Success";
68 }
69 
70 static int dev_to_sysfs_path(struct switchtec_linux *ldev, const char *suffix,
71  char *buf, size_t buflen)
72 {
73  int ret;
74  struct stat stat;
75 
76  ret = fstat(ldev->fd, &stat);
77  if (ret < 0)
78  return ret;
79 
80  snprintf(buf, buflen,
81  "/sys/dev/char/%d:%d/%s",
82  major(stat.st_rdev), minor(stat.st_rdev), suffix);
83 
84  return 0;
85 }
86 
87 static int sysfs_read_str(const char *path, char *buf, size_t buflen)
88 {
89  int ret;
90  int fd;
91 
92  fd = open(path, O_RDONLY);
93  if (fd < 0)
94  return -1;
95 
96  ret = read(fd, buf, buflen);
97 
98  close(fd);
99 
100  return ret;
101 }
102 
103 static long long sysfs_read_int(const char *path, int base)
104 {
105  int ret;
106  char buf[64];
107 
108  ret = sysfs_read_str(path, buf, sizeof(buf));
109  if (ret < 0)
110  return ret;
111 
112  return strtoll(buf, NULL, base);
113 }
114 
115 static int check_switchtec_device(struct switchtec_linux *ldev)
116 {
117  int ret;
118  char syspath[PATH_MAX];
119 
120  ret = dev_to_sysfs_path(ldev, "device/switchtec", syspath,
121  sizeof(syspath));
122  if (ret)
123  return ret;
124 
125  ret = access(syspath, F_OK);
126  if (ret)
127  errno = ENOTTY;
128 
129  return ret;
130 }
131 
132 static int get_partition(struct switchtec_linux *ldev)
133 {
134  int ret;
135  char syspath[PATH_MAX];
136 
137  ret = dev_to_sysfs_path(ldev, "partition", syspath,
138  sizeof(syspath));
139  if (ret)
140  return ret;
141 
142  ldev->dev.partition = sysfs_read_int(syspath, 10);
143  if (ldev->dev.partition < 0)
144  return ldev->dev.partition;
145 
146  ret = dev_to_sysfs_path(ldev, "partition_count", syspath,
147  sizeof(syspath));
148  if (ret)
149  return ret;
150 
151  ldev->dev.partition_count = sysfs_read_int(syspath, 10);
152  if (ldev->dev.partition_count < 1)
153  return -1;
154 
155  return 0;
156 }
157 
158 static void linux_close(struct switchtec_dev *dev)
159 {
160  struct switchtec_linux *ldev = to_switchtec_linux(dev);
161 
162  close(ldev->fd);
163  free(ldev);
164 }
165 
166 static int scan_dev_filter(const struct dirent *d)
167 {
168  if (d->d_name[0] == '.')
169  return 0;
170 
171  return 1;
172 }
173 
174 static void get_device_str(const char *path, const char *file,
175  char *buf, size_t buflen)
176 {
177  char sysfs_path[PATH_MAX];
178  int ret;
179 
180  snprintf(sysfs_path, sizeof(sysfs_path), "%s/%s",
181  path, file);
182 
183  ret = sysfs_read_str(sysfs_path, buf, buflen);
184  if (ret < 0 || buf[0] == -1)
185  snprintf(buf, buflen, "unknown");
186 
187  buf[strcspn(buf, "\n")] = 0;
188 }
189 
190 static void get_fw_version(const char *path, char *buf, size_t buflen)
191 {
192  char sysfs_path[PATH_MAX];
193  int fw_ver;
194  int ret;
195 
196  ret = snprintf(sysfs_path, sizeof(sysfs_path), "%s/fw_version",
197  path);
198  if (ret >= sizeof(sysfs_path))
199  goto unknown_version;
200 
201  fw_ver = sysfs_read_int(sysfs_path, 16);
202 
203  if (fw_ver < 0)
204  goto unknown_version;
205 
206  version_to_string(fw_ver, buf, buflen);
207  return;
208 
209 unknown_version:
210  snprintf(buf, buflen, "unknown");
211 }
212 
214 {
215  struct dirent **devices;
216  int i, n;
217  char link_path[PATH_MAX];
218  char pci_path[PATH_MAX] = "";
219  struct switchtec_device_info *dl;
220 
221  n = scandir(sys_path, &devices, scan_dev_filter, alphasort);
222  if (n <= 0)
223  return n;
224 
225  dl = *devlist = calloc(n, sizeof(struct switchtec_device_info));
226 
227  if (!dl) {
228  for (i = 0; i < n; i++)
229  free(devices[i]);
230  free(devices);
231  errno = ENOMEM;
232  return -errno;
233  }
234 
235  for (i = 0; i < n; i++) {
236  snprintf(dl[i].name, sizeof(dl[i].name),
237  "%s", devices[i]->d_name);
238  snprintf(dl[i].path, sizeof(dl[i].path),
239  "/dev/%s", devices[i]->d_name);
240 
241  snprintf(link_path, sizeof(link_path), "%s/%s/device",
242  sys_path, devices[i]->d_name);
243 
244  if (readlink(link_path, pci_path, sizeof(pci_path)) > 0)
245  snprintf(dl[i].pci_dev, sizeof(dl[i].pci_dev),
246  "%s", basename(pci_path));
247  else
248  snprintf(dl[i].pci_dev, sizeof(dl[i].pci_dev),
249  "unknown pci device");
250 
251  snprintf(link_path, sizeof(link_path), "%s/%s",
252  sys_path, devices[i]->d_name);
253 
254  get_device_str(link_path, "product_id", dl[i].product_id,
255  sizeof(dl[i].product_id));
256  get_device_str(link_path, "product_revision",
257  dl[i].product_rev, sizeof(dl[i].product_rev));
258  get_fw_version(link_path, dl[i].fw_version,
259  sizeof(dl[i].fw_version));
260 
261  free(devices[i]);
262  }
263 
264  free(devices);
265  return n;
266 }
267 
268 static int linux_get_device_id(struct switchtec_dev *dev)
269 {
270  int ret;
271  char link_path[PATH_MAX];
272  struct switchtec_linux *ldev = to_switchtec_linux(dev);
273 
274  ret = dev_to_sysfs_path(ldev, "device/device", link_path,
275  sizeof(link_path));
276  if (ret)
277  return ret;
278 
279  return sysfs_read_int(link_path, 16);
280 }
281 
282 static int linux_get_fw_version(struct switchtec_dev *dev, char *buf,
283  size_t buflen)
284 {
285  int ret;
286  long long version;
287  char syspath[PATH_MAX];
288  struct switchtec_linux *ldev = to_switchtec_linux(dev);
289 
290  ret = dev_to_sysfs_path(ldev, "fw_version", syspath, sizeof(syspath));
291  if (ret)
292  return ret;
293 
294  version = sysfs_read_int(syspath, 16);
295  if (version < 0)
296  return version;
297 
298  version_to_string(version, buf, buflen);
299 
300  return 0;
301 }
302 
303 static int submit_cmd(struct switchtec_linux *ldev, uint32_t cmd,
304  const void *payload, size_t payload_len)
305 {
306  int ret;
307  size_t bufsize = payload_len + sizeof(cmd);
308  char buf[bufsize];
309 
310  cmd = htole32(cmd);
311  memcpy(buf, &cmd, sizeof(cmd));
312  memcpy(&buf[sizeof(cmd)], payload, payload_len);
313 
314  ret = write(ldev->fd, buf, bufsize);
315 
316  if (ret < 0)
317  return ret;
318 
319  if (ret != bufsize) {
320  errno = EIO;
321  return -errno;
322  }
323 
324  return 0;
325 }
326 
327 static int read_resp(struct switchtec_linux *ldev, void *resp,
328  size_t resp_len)
329 {
330  int32_t ret;
331  size_t bufsize = sizeof(uint32_t) + resp_len;
332  char buf[bufsize];
333 
334  ret = read(ldev->fd, buf, bufsize);
335 
336  if (ret < 0)
337  return ret;
338 
339  if (ret != bufsize) {
340  errno = EIO;
341  return -errno;
342  }
343 
344  memcpy(&ret, buf, sizeof(ret));
345  if (ret)
346  errno = ret;
347 
348  if (!resp)
349  return ret;
350 
351  memcpy(resp, &buf[sizeof(ret)], resp_len);
352 
353  return ret;
354 }
355 
356 static int linux_cmd(struct switchtec_dev *dev, uint32_t cmd,
357  const void *payload, size_t payload_len, void *resp,
358  size_t resp_len)
359 {
360  int ret;
361  struct switchtec_linux *ldev = to_switchtec_linux(dev);
362 
363 retry:
364  ret = submit_cmd(ldev, cmd, payload, payload_len);
365  if (errno == EBADE) {
366  read_resp(ldev, NULL, 0);
367  errno = 0;
368  goto retry;
369  }
370 
371  if (ret < 0)
372  return ret;
373 
374  return read_resp(ldev, resp, resp_len);
375 }
376 
377 static int get_class_devices(const char *searchpath,
378  struct switchtec_status *status)
379 {
380  int i;
381  ssize_t len;
382  char syspath[PATH_MAX];
383  glob_t paths;
384  int found = 0;
385  const size_t MAX_LEN = 256;
386 
387  snprintf(syspath, sizeof(syspath), "%s*/*/device", searchpath);
388  glob(syspath, 0, NULL, &paths);
389 
390  for (i = 0; i < paths.gl_pathc; i++) {
391  char *p = paths.gl_pathv[i];
392 
393  len = readlink(p, syspath, sizeof(syspath));
394  if (len <= 0)
395  continue;
396 
397  p = dirname(p);
398 
399  if (!status->class_devices) {
400  status->class_devices = calloc(MAX_LEN, 1);
401  strcpy(status->class_devices, basename(p));
402  } else {
403  len = strlen(status->class_devices);
404  snprintf(&status->class_devices[len], MAX_LEN - len,
405  ", %s", basename(p));
406  }
407 
408  found = 1;
409  }
410 
411  globfree(&paths);
412  return found;
413 }
414 
415 static void get_port_bdf(const char *searchpath, int port,
416  struct switchtec_status *status)
417 {
418  char syspath[PATH_MAX];
419  glob_t paths;
420  int ret;
421 
422  ret = snprintf(syspath, sizeof(syspath), "%s/*:*:%02x.?",
423  searchpath, port);
424  if (ret >= sizeof(syspath))
425  return;
426 
427  glob(syspath, 0, NULL, &paths);
428 
429  if (paths.gl_pathc == 1)
430  status->pci_bdf = strdup(basename(paths.gl_pathv[0]));
431 
432  globfree(&paths);
433 }
434 
435 static void get_port_bdf_path(struct switchtec_status *status)
436 {
437  char path[PATH_MAX];
438  char rpath[PATH_MAX];
439  int domain, bus, dev, fn;
440  int ptr = 0;
441  char *subpath;
442  int ret;
443 
444  if (!status->pci_bdf)
445  return;
446 
447  snprintf(path, sizeof(path), "/sys/bus/pci/devices/%s",
448  status->pci_bdf);
449 
450  if (!realpath(path, rpath))
451  return;
452 
453  subpath = strtok(rpath, "/");
454  while (subpath) {
455  ret = sscanf(subpath, "%x:%x:%x.%x", &domain, &bus, &dev, &fn);
456  if (ret == 4) {
457  if (ptr == 0)
458  ret = snprintf(path + ptr, sizeof(path) - ptr,
459  "%04x:%02x:%02x:%x/",
460  domain, bus, dev, fn);
461  else
462  ret = snprintf(path + ptr, sizeof(path) - ptr,
463  "%02x.%x/", dev, fn);
464 
465  if (ret <= 0 || ret >= sizeof(path) - ptr)
466  break;
467 
468  ptr += ret;
469  }
470  subpath = strtok(NULL, "/");
471  }
472 
473  if (ptr)
474  path[ptr - 1] = 0;
475 
476  status->pci_bdf_path = strdup(path);
477 }
478 
479 static void get_port_info(struct switchtec_status *status)
480 {
481  int i;
482  char syspath[PATH_MAX];
483  glob_t paths;
484 
485  if (!status->pci_bdf)
486  return;
487 
488  snprintf(syspath, sizeof(syspath), "/sys/bus/pci/devices/%s/*:*:*/",
489  status->pci_bdf);
490 
491  glob(syspath, 0, NULL, &paths);
492 
493  for (i = 0; i < paths.gl_pathc; i++) {
494  char *p = paths.gl_pathv[i];
495 
496  snprintf(syspath, sizeof(syspath), "%s/vendor", p);
497  status->vendor_id = sysfs_read_int(syspath, 16);
498  if (status->vendor_id < 0)
499  continue;
500 
501  snprintf(syspath, sizeof(syspath), "%s/device", p);
502  status->device_id = sysfs_read_int(syspath, 16);
503  if (status->device_id < 0)
504  continue;
505 
506  if (get_class_devices(p, status)) {
507  if (status->pci_dev)
508  free(status->pci_dev);
509  status->pci_dev = strdup(basename(p));
510  }
511 
512  if (!status->pci_dev)
513  status->pci_dev = strdup(basename(p));
514  }
515 
516  globfree(&paths);
517 }
518 
519 static void get_config_info(struct switchtec_status *status)
520 {
521  int ret;
522  int fd;
523  char syspath[PATH_MAX];
524  uint32_t extcap;
525  int pos = PCI_EXT_CAP_OFFSET;
526  uint16_t acs;
527 
528  snprintf(syspath, sizeof(syspath), "/sys/bus/pci/devices/%s/config",
529  status->pci_bdf);
530 
531  fd = open(syspath, O_RDONLY);
532  if (fd < -1)
533  return;
534 
535  while (1) {
536  ret = pread(fd, &extcap, sizeof(extcap), pos);
537  if (ret != sizeof(extcap) || !extcap)
538  goto close_and_exit;
539 
540  if (PCI_EXT_CAP_ID(extcap) == PCI_EXT_CAP_ID_ACS)
541  break;
542 
543  pos = PCI_EXT_CAP_NEXT(extcap);
544  if (pos < PCI_EXT_CAP_OFFSET)
545  goto close_and_exit;
546  }
547 
548  ret = pread(fd, &acs, sizeof(acs), pos + PCI_ACS_CTRL);
549  if (ret != sizeof(acs))
550  goto close_and_exit;
551 
552  status->acs_ctrl = acs;
553 
554 close_and_exit:
555  close(fd);
556 }
557 
558 static int linux_get_devices(struct switchtec_dev *dev,
559  struct switchtec_status *status,
560  int ports)
561 {
562  int ret;
563  int i;
564  int local_part;
565  char syspath[PATH_MAX];
566  char searchpath[PATH_MAX];
567  struct switchtec_linux *ldev = to_switchtec_linux(dev);
568 
569  ret = dev_to_sysfs_path(ldev, "device", syspath,
570  sizeof(syspath));
571  if (ret)
572  return ret;
573 
574  if (!realpath(syspath, searchpath)) {
575  errno = ENXIO;
576  return -errno;
577  }
578 
579  //Replace eg "0000:03:00.1" into "0000:03:00.0"
580  searchpath[strlen(searchpath) - 1] = '0';
581 
582  local_part = switchtec_partition(dev);
583 
584  for (i = 0; i < ports; i++) {
585  if (status[i].port.partition != local_part)
586  continue;
587 
588  if (status[i].port.upstream) {
589  status[i].pci_bdf = strdup(basename(searchpath));
590  get_port_bdf_path(&status[i]);
591  continue;
592  }
593 
594  get_port_bdf(searchpath, status[i].port.log_id - 1, &status[i]);
595  get_port_bdf_path(&status[i]);
596  get_port_info(&status[i]);
597  get_config_info(&status[i]);
598  }
599 
600  return 0;
601 }
602 
603 static int linux_pff_to_port(struct switchtec_dev *dev, int pff,
604  int *partition, int *port)
605 {
606  int ret;
607  struct switchtec_ioctl_pff_port p;
608  struct switchtec_linux *ldev = to_switchtec_linux(dev);
609 
610  p.pff = pff;
611  ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_PFF_TO_PORT, &p);
612  if (ret)
613  return ret;
614 
615  if (partition)
616  *partition = p.partition;
617  if (port)
618  *port = p.port;
619 
620  return 0;
621 }
622 
623 static int linux_port_to_pff(struct switchtec_dev *dev, int partition,
624  int port, int *pff)
625 {
626  int ret;
627  struct switchtec_ioctl_pff_port p;
628  struct switchtec_linux *ldev = to_switchtec_linux(dev);
629 
630  p.port = port;
631  p.partition = partition;
632 
633  ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_PORT_TO_PFF, &p);
634  if (ret)
635  return ret;
636 
637  if (pff)
638  *pff = p.pff;
639 
640  return 0;
641 }
642 
643 #ifdef __CHECKER__
644 #define __force __attribute__((force))
645 #else
646 #define __force
647 #endif
648 
649 static ssize_t resource_size(struct switchtec_linux *ldev, const char *fname)
650 {
651  char respath[PATH_MAX];
652  struct stat stat;
653  int fd, ret;
654 
655  ret = dev_to_sysfs_path(ldev, fname, respath,
656  sizeof(respath));
657  if (ret) {
658  errno = ret;
659  return -1;
660  }
661 
662  fd = open(respath, O_RDONLY);
663  if (fd < 0)
664  return -1;
665 
666  ret = fstat(fd, &stat);
667  if (ret < 0) {
668  close(fd);
669  return -1;
670  }
671 
672  close(fd);
673  return stat.st_size;
674 }
675 
676 static int mmap_resource(struct switchtec_linux *ldev, const char *fname,
677  void *addr, size_t offset, size_t size, int writeable)
678 {
679  char respath[PATH_MAX];
680  void *map;
681  int fd, ret = 0;
682 
683  ret = dev_to_sysfs_path(ldev, fname, respath,
684  sizeof(respath));
685  if (ret) {
686  errno = ret;
687  return -1;
688  }
689 
690  fd = open(respath, writeable ? O_RDWR : O_RDONLY);
691  if (fd < 0)
692  return -1;
693 
694  map = mmap(addr, size, (writeable ? PROT_WRITE : 0) | PROT_READ,
695  MAP_SHARED | MAP_FIXED, fd, offset);
696  if (map == MAP_FAILED)
697  ret = -1;
698 
699  close(fd);
700  return ret;
701 }
702 
703 /*
704  * GAS map maps the hardware registers into user memory space.
705  * Needless to say, this can be very dangerous and should only
706  * be done if you know what you are doing. Any register accesses
707  * that use this will remain unsupported by Microsemi unless it's
708  * done within the switchtec user project or otherwise specified.
709  */
710 static gasptr_t linux_gas_map(struct switchtec_dev *dev, int writeable,
711  size_t *map_size)
712 {
713  int ret;
714  void *map;
715  ssize_t msize;
716  struct switchtec_linux *ldev = to_switchtec_linux(dev);
717 
718  msize = resource_size(ldev, "device/resource0");
719  if (msize <= 0)
720  return SWITCHTEC_MAP_FAILED;
721 
722  /*
723  * Reserve virtual address space for the entire GAS mapping.
724  */
725  map = mmap(NULL, msize, PROT_NONE,
726  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
727  if (map == MAP_FAILED)
728  return SWITCHTEC_MAP_FAILED;
729 
730  ret = mmap_resource(ldev, "device/resource0_wc", map, 0,
731  SWITCHTEC_GAS_TOP_CFG_OFFSET, writeable);
732  if (ret) {
733  ret = mmap_resource(ldev, "device/resource0", map, 0,
734  SWITCHTEC_GAS_TOP_CFG_OFFSET,
735  writeable);
736  if (ret)
737  goto unmap_and_exit;
738  }
739 
740  ret = mmap_resource(ldev, "device/resource0",
741  map + SWITCHTEC_GAS_TOP_CFG_OFFSET,
742  SWITCHTEC_GAS_TOP_CFG_OFFSET,
743  msize - SWITCHTEC_GAS_TOP_CFG_OFFSET,
744  writeable);
745  if (ret)
746  goto unmap_and_exit;
747 
748  if (map_size)
749  *map_size = msize;
750 
751  dev->gas_map = (gasptr_t __force)map;
752  dev->gas_map_size = msize;
753 
754  ret = gasop_access_check(dev);
755  if (ret) {
756  errno = ENODEV;
757  goto unmap_and_exit;
758  }
759  return (gasptr_t __force)map;
760 
761 unmap_and_exit:
762  munmap(map, msize);
763  return SWITCHTEC_MAP_FAILED;
764 }
765 
766 static void linux_gas_unmap(struct switchtec_dev *dev, gasptr_t map)
767 {
768  munmap((void __force *)map, dev->gas_map_size);
769 }
770 
771 static int linux_flash_part(struct switchtec_dev *dev,
772  struct switchtec_fw_image_info *info,
773  enum switchtec_fw_image_part_id_gen3 part)
774 {
775  struct switchtec_linux *ldev = to_switchtec_linux(dev);
776  struct switchtec_ioctl_flash_part_info ioctl_info = {0};
777  int ret;
778 
779  switch (part) {
780  case SWITCHTEC_FW_PART_ID_G3_IMG0:
781  ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_IMG0;
782  break;
783  case SWITCHTEC_FW_PART_ID_G3_IMG1:
784  ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_IMG1;
785  break;
786  case SWITCHTEC_FW_PART_ID_G3_DAT0:
787  ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_CFG0;
788  break;
789  case SWITCHTEC_FW_PART_ID_G3_DAT1:
790  ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_CFG1;
791  break;
792  case SWITCHTEC_FW_PART_ID_G3_NVLOG:
793  ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_NVLOG;
794  break;
795  default:
796  return -EINVAL;
797  }
798 
799  ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_FLASH_PART_INFO, &ioctl_info);
800  if (ret)
801  return ret;
802 
803  info->part_addr = ioctl_info.address;
804  info->part_len = ioctl_info.length;
805  info->active = false;
806  info->running = false;
807 
808  if (ioctl_info.active & SWITCHTEC_IOCTL_PART_ACTIVE)
809  info->active = true;
810 
811  if (ioctl_info.active & SWITCHTEC_IOCTL_PART_RUNNING)
812  info->running = true;
813 
814  return 0;
815 }
816 
817 static void event_summary_copy(struct switchtec_event_summary *dst,
818  struct switchtec_ioctl_event_summary *src,
819  int size)
820 {
821  int i;
822 
823  dst->global = src->global;
824  dst->part_bitmap = src->part_bitmap;
825  dst->local_part = src->local_part;
826 
827  for (i = 0; i < SWITCHTEC_MAX_PARTS; i++)
828  dst->part[i] = src->part[i];
829 
830  for (i = 0; i < SWITCHTEC_MAX_PFF_CSR && i < size; i++)
831  dst->pff[i] = src->pff[i];
832 
833  for (; i < SWITCHTEC_MAX_PFF_CSR; i++)
834  dst->pff[i] = 0;
835 }
836 
837 #define EV(t, n)[SWITCHTEC_ ## t ## _EVT_ ## n] = \
838  SWITCHTEC_IOCTL_EVENT_ ## n
839 
840 static const int event_map[] = {
841  EV(GLOBAL, STACK_ERROR),
842  EV(GLOBAL, PPU_ERROR),
843  EV(GLOBAL, ISP_ERROR),
844  EV(GLOBAL, SYS_RESET),
845  EV(GLOBAL, FW_EXC),
846  EV(GLOBAL, FW_NMI),
847  EV(GLOBAL, FW_NON_FATAL),
848  EV(GLOBAL, FW_FATAL),
849  EV(GLOBAL, TWI_MRPC_COMP),
850  EV(GLOBAL, TWI_MRPC_COMP_ASYNC),
851  EV(GLOBAL, CLI_MRPC_COMP),
852  EV(GLOBAL, CLI_MRPC_COMP_ASYNC),
853  EV(GLOBAL, GPIO_INT),
854  EV(GLOBAL, GFMS),
855  EV(PART, PART_RESET),
856  EV(PART, MRPC_COMP),
857  EV(PART, MRPC_COMP_ASYNC),
858  EV(PART, DYN_PART_BIND_COMP),
859  EV(PFF, AER_IN_P2P),
860  EV(PFF, AER_IN_VEP),
861  EV(PFF, DPC),
862  EV(PFF, CTS),
863  EV(PFF, UEC),
864  EV(PFF, HOTPLUG),
865  EV(PFF, IER),
866  EV(PFF, THRESH),
867  EV(PFF, POWER_MGMT),
868  EV(PFF, TLP_THROTTLING),
869  EV(PFF, FORCE_SPEED),
870  EV(PFF, CREDIT_TIMEOUT),
871  EV(PFF, LINK_STATE),
872 };
873 
874 static int linux_event_summary(struct switchtec_dev *dev,
875  struct switchtec_event_summary *sum)
876 {
877  int ret;
878  struct switchtec_ioctl_event_summary isum;
879  struct switchtec_ioctl_event_summary_legacy isum_legacy;
880  struct switchtec_linux *ldev = to_switchtec_linux(dev);
881 
882  if (!sum)
883  return 0;
884 
885  ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_EVENT_SUMMARY, &isum);
886  if (!ret) {
887  event_summary_copy(sum, &isum, ARRAY_SIZE(isum.pff));
888  return ret;
889  }
890 
891  ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_EVENT_SUMMARY_LEGACY, &isum);
892  if (ret < 0)
893  return ret;
894 
895  event_summary_copy(sum, &isum, ARRAY_SIZE(isum_legacy.pff));
896 
897  return 0;
898 }
899 
900 static int linux_event_ctl(struct switchtec_dev *dev,
901  enum switchtec_event_id e,
902  int index, int flags,
903  uint32_t data[5])
904 {
905  int ret;
906  struct switchtec_ioctl_event_ctl ctl;
907  struct switchtec_linux *ldev = to_switchtec_linux(dev);
908 
909  if (e >= SWITCHTEC_MAX_EVENTS)
910  return -EINVAL;
911 
912  ctl.event_id = event_map[e];
913  ctl.flags = 0;
914 
915  if (flags & SWITCHTEC_EVT_FLAG_CLEAR)
916  ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_CLEAR;
917  if (flags & SWITCHTEC_EVT_FLAG_EN_POLL)
918  ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_POLL;
919  if (flags & SWITCHTEC_EVT_FLAG_EN_LOG)
920  ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_LOG;
921  if (flags & SWITCHTEC_EVT_FLAG_EN_CLI)
922  ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_CLI;
923  if (flags & SWITCHTEC_EVT_FLAG_EN_FATAL)
924  ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_FATAL;
925  if (flags & SWITCHTEC_EVT_FLAG_DIS_POLL)
926  ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_DIS_POLL;
927  if (flags & SWITCHTEC_EVT_FLAG_DIS_LOG)
928  ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_DIS_LOG;
929  if (flags & SWITCHTEC_EVT_FLAG_DIS_CLI)
930  ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_DIS_CLI;
931  if (flags & SWITCHTEC_EVT_FLAG_DIS_FATAL)
932  ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_DIS_FATAL;
933 
934  ctl.index = index;
935  ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_EVENT_CTL, &ctl);
936 
937  if (ret)
938  return ret;
939 
940  if (data)
941  memcpy(data, ctl.data, sizeof(ctl.data));
942 
943  return ctl.count;
944 }
945 
946 static int linux_event_wait(struct switchtec_dev *dev, int timeout_ms)
947 {
948  int ret;
949  struct switchtec_linux *ldev = to_switchtec_linux(dev);
950  struct pollfd fds = {
951  .fd = ldev->fd,
952  .events = POLLPRI,
953  };
954 
955  ret = poll(&fds, 1, timeout_ms);
956  if (ret <= 0)
957  return ret;
958 
959  if (fds.revents & POLLERR) {
960  errno = ENODEV;
961  return -1;
962  }
963 
964  if (fds.revents & POLLPRI)
965  return 1;
966 
967  return 0;
968 }
969 
970 static const struct switchtec_ops linux_ops = {
971  .close = linux_close,
972  .get_device_id = linux_get_device_id,
973  .get_fw_version = linux_get_fw_version,
974  .cmd = linux_cmd,
975  .get_devices = linux_get_devices,
976  .pff_to_port = linux_pff_to_port,
977  .port_to_pff = linux_port_to_pff,
978  .gas_map = linux_gas_map,
979  .gas_unmap = linux_gas_unmap,
980  .flash_part = linux_flash_part,
981  .event_summary = linux_event_summary,
982  .event_ctl = linux_event_ctl,
983  .event_wait = linux_event_wait,
984 
985  .gas_read8 = mmap_gas_read8,
986  .gas_read16 = mmap_gas_read16,
987  .gas_read32 = mmap_gas_read32,
988  .gas_read64 = mmap_gas_read64,
989  .gas_write8 = mmap_gas_write8,
990  .gas_write16 = mmap_gas_write16,
991  .gas_write32 = mmap_gas_write32,
992  .gas_write32_no_retry = mmap_gas_write32,
993  .gas_write64 = mmap_gas_write64,
994  .memcpy_to_gas = mmap_memcpy_to_gas,
995  .memcpy_from_gas = mmap_memcpy_from_gas,
996  .write_from_gas = mmap_write_from_gas,
997 };
998 
999 struct switchtec_dev *switchtec_open_by_path(const char *path)
1000 {
1001  struct switchtec_linux *ldev;
1002  int fd;
1003 
1004  fd = open(path, O_RDWR | O_CLOEXEC);
1005  if (fd < 0)
1006  return NULL;
1007 
1008  if (isatty(fd))
1009  return switchtec_open_uart(fd);
1010  else
1011  errno = 0;
1012 
1013  ldev = malloc(sizeof(*ldev));
1014  if (!ldev)
1015  return NULL;
1016 
1017  ldev->fd = fd;
1018 
1019  if (check_switchtec_device(ldev))
1020  goto err_close_free;
1021 
1022  if (get_partition(ldev))
1023  goto err_close_free;
1024 
1025  ldev->dev.ops = &linux_ops;
1026 
1027  return &ldev->dev;
1028 
1029 err_close_free:
1030  close(ldev->fd);
1031  free(ldev);
1032  return NULL;
1033 }
1034 
1035 struct switchtec_dev *switchtec_open_by_index(int index)
1036 {
1037  char path[PATH_MAX];
1038  struct switchtec_dev *dev;
1039 
1040  snprintf(path, sizeof(path), "/dev/switchtec%d", index);
1041 
1042  dev = switchtec_open_by_path(path);
1043 
1044  if (errno == ENOENT)
1045  errno = ENODEV;
1046 
1047  return dev;
1048 }
1049 
1050 struct switchtec_dev *switchtec_open_by_pci_addr(int domain, int bus,
1051  int device, int func)
1052 {
1053  char path[PATH_MAX];
1054  struct switchtec_dev *dev;
1055  struct dirent *dirent;
1056  DIR *dir;
1057 
1058  snprintf(path, sizeof(path),
1059  "/sys/bus/pci/devices/%04x:%02x:%02x.%x/switchtec",
1060  domain, bus, device, func);
1061 
1062  dir = opendir(path);
1063  if (!dir)
1064  goto err_out;
1065 
1066  while ((dirent = readdir(dir))) {
1067  if (dirent->d_name[0] != '.')
1068  break;
1069  }
1070 
1071  if (!dirent)
1072  goto err_close;
1073 
1074  /*
1075  * Should only be one switchtec device, if there are
1076  * more then something is wrong
1077  */
1078  if (readdir(dir))
1079  goto err_close;
1080 
1081  snprintf(path, sizeof(path), "/dev/%s", dirent->d_name);
1082  printf("%s\n", path);
1083  dev = switchtec_open(path);
1084 
1085  closedir(dir);
1086  return dev;
1087 
1088 err_close:
1089  closedir(dir);
1090 err_out:
1091  errno = ENODEV;
1092  return NULL;
1093 }
1094 
1095 #endif
switchtec_linux
Definition: linux.c:56
switchtec_status
Port status structure.
Definition: switchtec.h:184
switchtec_event_summary::local_part
unsigned local_part
Bitmap of events in the local partition.
Definition: switchtec.h:325
switchtec.h
Main Switchtec header.
switchtec_partition
_PURE int switchtec_partition(struct switchtec_dev *dev)
Get the partiton number of the device that was opened.
Definition: switchtec.c:401
switchtec_status::pci_bdf_path
char * pci_bdf_path
PCI BDF path of the port.
Definition: switchtec.h:198
switchtec_event_summary::pff
unsigned pff[SWITCHTEC_MAX_PFF_CSR]
Bitmap of events in each port function.
Definition: switchtec.h:331
switchtec_open_by_pci_addr
struct switchtec_dev * switchtec_open_by_pci_addr(int domain, int bus, int device, int func)
Open a switchtec device by PCI address (BDF)
Definition: linux.c:1050
switchtec_device_info::name
char name[256]
Device name, eg. switchtec0.
Definition: switchtec.h:156
switchtec_status::device_id
int device_id
Device ID.
Definition: switchtec.h:202
switchtec_fw_image_info
Information about a firmware image or partition.
Definition: switchtec.h:285
switchtec_device_info::product_rev
char product_rev[8]
Product revision.
Definition: switchtec.h:160
switchtec_event_id
switchtec_event_id
Enumeration of all possible events.
Definition: switchtec.h:337
mmap_gas.h
Gas Operations for platforms that the gas is mapped into the address space.
switchtec_list
int switchtec_list(struct switchtec_device_info **devlist)
List all the switchtec devices in the system.
Definition: linux.c:213
switchtec_open_by_index
struct switchtec_dev * switchtec_open_by_index(int index)
Open a switchtec device by index.
Definition: linux.c:1035
switchtec_open_uart
struct switchtec_dev * switchtec_open_uart(int fd)
Open a switchtec device behind a uart device.
Definition: linux-uart.c:497
switchtec_device_info::product_id
char product_id[32]
Product ID.
Definition: switchtec.h:159
switchtec_status::pci_bdf
char * pci_bdf
PCI BDF of the port.
Definition: switchtec.h:197
switchtec_event_summary::part
unsigned part[SWITCHTEC_MAX_PARTS]
Bitmap of events in each partition.
Definition: switchtec.h:328
switchtec_event_summary
Event summary bitmaps.
Definition: switchtec.h:322
switchtec_fw_image_info::part_len
size_t part_len
Length of the partition.
Definition: switchtec.h:291
switchtec_status::vendor_id
int vendor_id
Vendor ID.
Definition: switchtec.h:201
switchtec_device_info::path
char path[PATH_MAX]
Path to the device.
Definition: switchtec.h:162
switchtec_open
struct switchtec_dev * switchtec_open(const char *device)
Open a Switchtec device by string.
Definition: switchtec.c:258
gasptr_t
__gas struct switchtec_gas * gasptr_t
Shortform for a pointer to the GAS register space.
Definition: switchtec.h:83
switchtec_device_info::fw_version
char fw_version[32]
Firmware version.
Definition: switchtec.h:161
switchtec_event_summary::part_bitmap
uint64_t part_bitmap
Bitmap of partitions with active events.
Definition: switchtec.h:324
switchtec_status::acs_ctrl
unsigned int acs_ctrl
ACS Setting of the Port.
Definition: switchtec.h:204
switchtec_status::pci_dev
char * pci_dev
PCI BDF of the device on the port.
Definition: switchtec.h:200
switchtec_open_by_path
struct switchtec_dev * switchtec_open_by_path(const char *path)
Open a switchtec device by path.
Definition: linux.c:999
switchtec_device_info
Represents a Switchtec device in the switchtec_list() function.
Definition: switchtec.h:155
switchtec_status::class_devices
char * class_devices
Comma seperated list of classes.
Definition: switchtec.h:203
switchtec_device_info::pci_dev
char pci_dev[256]
PCI BDF string.
Definition: switchtec.h:158
switchtec_fw_image_info::part_addr
size_t part_addr
Address of the partition.
Definition: switchtec.h:290
switchtec_event_summary::global
uint64_t global
Bitmap of global events.
Definition: switchtec.h:323