xref: /spdk/lib/env_dpdk/pci.c (revision 552e21cce6cccbf833ed9109827e08337377d7ce)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (c) Intel Corporation.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "env_internal.h"
35 
36 #include "spdk/env.h"
37 
38 #define SYSFS_PCI_DRIVERS	"/sys/bus/pci/drivers"
39 
40 #define PCI_CFG_SIZE		256
41 #define PCI_EXT_CAP_ID_SN	0x03
42 
43 /* DPDK 18.11+ hotplug isn't robust. Multiple apps starting at the same time
44  * might cause the internal IPC to misbehave. Just retry in such case.
45  */
46 #define DPDK_HOTPLUG_RETRY_COUNT 4
47 
48 static pthread_mutex_t g_pci_mutex = PTHREAD_MUTEX_INITIALIZER;
49 static TAILQ_HEAD(, spdk_pci_device) g_pci_devices = TAILQ_HEAD_INITIALIZER(g_pci_devices);
50 static TAILQ_HEAD(, spdk_pci_driver) g_pci_drivers = TAILQ_HEAD_INITIALIZER(g_pci_drivers);
51 
52 static int
53 spdk_map_bar_rte(struct spdk_pci_device *device, uint32_t bar,
54 		 void **mapped_addr, uint64_t *phys_addr, uint64_t *size)
55 {
56 	struct rte_pci_device *dev = device->dev_handle;
57 
58 	*mapped_addr = dev->mem_resource[bar].addr;
59 	*phys_addr = (uint64_t)dev->mem_resource[bar].phys_addr;
60 	*size = (uint64_t)dev->mem_resource[bar].len;
61 
62 	return 0;
63 }
64 
65 static int
66 spdk_unmap_bar_rte(struct spdk_pci_device *device, uint32_t bar, void *addr)
67 {
68 	return 0;
69 }
70 
71 static int
72 spdk_cfg_read_rte(struct spdk_pci_device *dev, void *value, uint32_t len, uint32_t offset)
73 {
74 	int rc;
75 
76 #if RTE_VERSION >= RTE_VERSION_NUM(17, 05, 0, 4)
77 	rc = rte_pci_read_config(dev->dev_handle, value, len, offset);
78 #else
79 	rc = rte_eal_pci_read_config(dev->dev_handle, value, len, offset);
80 #endif
81 
82 #if defined(__FreeBSD__) && RTE_VERSION < RTE_VERSION_NUM(18, 11, 0, 0)
83 	/* Older DPDKs return 0 on success and -1 on failure */
84 	return rc;
85 #endif
86 	return (rc > 0 && (uint32_t) rc == len) ? 0 : -1;
87 }
88 
89 static int
90 spdk_cfg_write_rte(struct spdk_pci_device *dev, void *value, uint32_t len, uint32_t offset)
91 {
92 	int rc;
93 
94 #if RTE_VERSION >= RTE_VERSION_NUM(17, 05, 0, 4)
95 	rc = rte_pci_write_config(dev->dev_handle, value, len, offset);
96 #else
97 	rc = rte_eal_pci_write_config(dev->dev_handle, value, len, offset);
98 #endif
99 
100 #ifdef __FreeBSD__
101 	/* DPDK returns 0 on success and -1 on failure */
102 	return rc;
103 #endif
104 	return (rc > 0 && (uint32_t) rc == len) ? 0 : -1;
105 }
106 
107 static void
108 spdk_detach_rte(struct spdk_pci_device *dev)
109 {
110 	struct rte_pci_device *rte_dev = dev->dev_handle;
111 
112 #if RTE_VERSION >= RTE_VERSION_NUM(18, 11, 0, 0)
113 	char bdf[32];
114 	int i = 0, rc;
115 
116 	snprintf(bdf, sizeof(bdf), "%s", rte_dev->device.name);
117 	do {
118 		rc = rte_eal_hotplug_remove("pci", bdf);
119 	} while (rc == -ENOMSG && ++i <= DPDK_HOTPLUG_RETRY_COUNT);
120 #elif RTE_VERSION >= RTE_VERSION_NUM(17, 11, 0, 3)
121 	rte_eal_dev_detach(&rte_dev->device);
122 #elif RTE_VERSION >= RTE_VERSION_NUM(17, 05, 0, 4)
123 	rte_pci_detach(&rte_dev->addr);
124 #else
125 	rte_eal_device_remove(&rte_dev->device);
126 	rte_eal_pci_detach(&rte_dev->addr);
127 #endif
128 }
129 
130 void
131 spdk_pci_driver_register(struct spdk_pci_driver *driver)
132 {
133 	TAILQ_INSERT_TAIL(&g_pci_drivers, driver, tailq);
134 }
135 
136 void
137 spdk_pci_init(void)
138 {
139 #if RTE_VERSION >= RTE_VERSION_NUM(18, 11, 0, 0)
140 	struct spdk_pci_driver *driver;
141 
142 	/* We need to pre-register pci drivers for the pci devices to be
143 	 * attachable in multi-process with DPDK 18.11+.
144 	 *
145 	 * DPDK 18.11+ does its best to ensure all devices are equally
146 	 * attached or detached in all processes within a shared memory group.
147 	 * For SPDK it means that if a device is hotplugged in the primary,
148 	 * then DPDK will automatically send an IPC hotplug request to all other
149 	 * processes. Those other processes may not have the same SPDK PCI
150 	 * driver registered and may fail to attach the device. DPDK will send
151 	 * back the failure status, and the the primary process will also fail
152 	 * to hotplug the device. To prevent that, we need to pre-register the
153 	 * pci drivers here.
154 	 */
155 	TAILQ_FOREACH(driver, &g_pci_drivers, tailq) {
156 		assert(!driver->is_registered);
157 		driver->is_registered = true;
158 		rte_pci_register(&driver->driver);
159 	}
160 #endif
161 }
162 
163 int
164 spdk_pci_device_init(struct rte_pci_driver *_drv,
165 		     struct rte_pci_device *_dev)
166 {
167 	struct spdk_pci_driver *driver = (struct spdk_pci_driver *)_drv;
168 	struct spdk_pci_device *dev;
169 	int rc;
170 
171 #if RTE_VERSION < RTE_VERSION_NUM(18, 11, 0, 0)
172 	if (!driver->cb_fn) {
173 #if RTE_VERSION < RTE_VERSION_NUM(17, 02, 0, 1)
174 		rte_eal_pci_unmap_device(_dev);
175 #endif
176 		/* Return a positive value to indicate that this device does
177 		 * not belong to this driver, but this isn't an error.
178 		 */
179 		return 1;
180 	}
181 #endif
182 
183 	dev = calloc(1, sizeof(*dev));
184 	if (dev == NULL) {
185 		return -1;
186 	}
187 
188 	dev->dev_handle = _dev;
189 
190 	dev->addr.domain = _dev->addr.domain;
191 	dev->addr.bus = _dev->addr.bus;
192 	dev->addr.dev = _dev->addr.devid;
193 	dev->addr.func = _dev->addr.function;
194 	dev->id.vendor_id = _dev->id.vendor_id;
195 	dev->id.device_id = _dev->id.device_id;
196 	dev->id.subvendor_id = _dev->id.subsystem_vendor_id;
197 	dev->id.subdevice_id = _dev->id.subsystem_device_id;
198 	dev->socket_id = _dev->device.numa_node;
199 
200 	dev->map_bar = spdk_map_bar_rte;
201 	dev->unmap_bar = spdk_unmap_bar_rte;
202 	dev->cfg_read = spdk_cfg_read_rte;
203 	dev->cfg_write = spdk_cfg_write_rte;
204 	dev->detach = spdk_detach_rte;
205 
206 	dev->internal.driver = driver;
207 
208 	if (driver->cb_fn != NULL) {
209 		rc = driver->cb_fn(driver->cb_arg, dev);
210 		if (rc != 0) {
211 			free(dev);
212 			return rc;
213 		}
214 		dev->internal.attached = true;
215 	}
216 
217 	TAILQ_INSERT_TAIL(&g_pci_devices, dev, internal.tailq);
218 	spdk_vtophys_pci_device_added(dev->dev_handle);
219 	return 0;
220 }
221 
222 int
223 spdk_pci_device_fini(struct rte_pci_device *_dev)
224 {
225 	struct spdk_pci_device *dev;
226 
227 	TAILQ_FOREACH(dev, &g_pci_devices, internal.tailq) {
228 		if (dev->dev_handle == _dev) {
229 			break;
230 		}
231 	}
232 
233 	if (dev == NULL || dev->internal.attached) {
234 		/* The device might be still referenced somewhere in SPDK. */
235 		return -1;
236 	}
237 
238 	spdk_vtophys_pci_device_removed(dev->dev_handle);
239 	TAILQ_REMOVE(&g_pci_devices, dev, internal.tailq);
240 	free(dev);
241 	return 0;
242 
243 }
244 
245 void
246 spdk_pci_device_detach(struct spdk_pci_device *dev)
247 {
248 	assert(dev->internal.attached);
249 	dev->internal.attached = false;
250 	dev->detach(dev);
251 }
252 
253 int
254 spdk_pci_device_attach(struct spdk_pci_driver *driver,
255 		       spdk_pci_enum_cb enum_cb,
256 		       void *enum_ctx, struct spdk_pci_addr *pci_address)
257 {
258 	struct spdk_pci_device *dev;
259 	int rc;
260 #if RTE_VERSION >= RTE_VERSION_NUM(17, 11, 0, 3)
261 	char bdf[32];
262 
263 	spdk_pci_addr_fmt(bdf, sizeof(bdf), pci_address);
264 #else
265 	struct rte_pci_addr addr;
266 
267 	addr.domain = pci_address->domain;
268 	addr.bus = pci_address->bus;
269 	addr.devid = pci_address->dev;
270 	addr.function = pci_address->func;
271 #endif
272 
273 	pthread_mutex_lock(&g_pci_mutex);
274 
275 	TAILQ_FOREACH(dev, &g_pci_devices, internal.tailq) {
276 		if (spdk_pci_addr_compare(&dev->addr, pci_address) == 0) {
277 			break;
278 		}
279 	}
280 
281 	if (dev != NULL && dev->internal.driver == driver) {
282 		if (dev->internal.attached) {
283 			pthread_mutex_unlock(&g_pci_mutex);
284 			return -1;
285 		}
286 
287 		rc = enum_cb(enum_ctx, dev);
288 		if (rc == 0) {
289 			dev->internal.attached = true;
290 		}
291 		pthread_mutex_unlock(&g_pci_mutex);
292 		return rc;
293 	}
294 
295 	if (!driver->is_registered) {
296 		driver->is_registered = true;
297 #if RTE_VERSION >= RTE_VERSION_NUM(17, 05, 0, 4)
298 		rte_pci_register(&driver->driver);
299 #else
300 		rte_eal_pci_register(&driver->driver);
301 #endif
302 	}
303 
304 	driver->cb_fn = enum_cb;
305 	driver->cb_arg = enum_ctx;
306 
307 #if RTE_VERSION >= RTE_VERSION_NUM(18, 11, 0, 0)
308 	int i = 0;
309 
310 	do {
311 		rc = rte_eal_hotplug_add("pci", bdf, "");
312 	} while (rc == -ENOMSG && ++i <= DPDK_HOTPLUG_RETRY_COUNT);
313 
314 	if (i > 1 && rc == -EEXIST) {
315 		/* Even though the previous request timed out, the device
316 		 * was attached successfully.
317 		 */
318 		rc = 0;
319 	}
320 #elif RTE_VERSION >= RTE_VERSION_NUM(17, 11, 0, 3)
321 	rc = rte_eal_dev_attach(bdf, "");
322 #elif RTE_VERSION >= RTE_VERSION_NUM(17, 05, 0, 4)
323 	rc = rte_pci_probe_one(&addr);
324 #else
325 	rc = rte_eal_pci_probe_one(&addr);
326 #endif
327 
328 	driver->cb_arg = NULL;
329 	driver->cb_fn = NULL;
330 	pthread_mutex_unlock(&g_pci_mutex);
331 
332 	return rc == 0 ? 0 : -1;
333 }
334 
335 /* Note: You can call spdk_pci_enumerate from more than one thread
336  *       simultaneously safely, but you cannot call spdk_pci_enumerate
337  *       and rte_eal_pci_probe simultaneously.
338  */
339 int
340 spdk_pci_enumerate(struct spdk_pci_driver *driver,
341 		   spdk_pci_enum_cb enum_cb,
342 		   void *enum_ctx)
343 {
344 	struct spdk_pci_device *dev;
345 	int rc;
346 
347 	pthread_mutex_lock(&g_pci_mutex);
348 
349 	TAILQ_FOREACH(dev, &g_pci_devices, internal.tailq) {
350 		if (dev->internal.attached || dev->internal.driver != driver) {
351 			continue;
352 		}
353 
354 		rc = enum_cb(enum_ctx, dev);
355 		if (rc == 0) {
356 			dev->internal.attached = true;
357 		} else if (rc < 0) {
358 			pthread_mutex_unlock(&g_pci_mutex);
359 			return -1;
360 		}
361 	}
362 
363 	if (!driver->is_registered) {
364 		driver->is_registered = true;
365 #if RTE_VERSION >= RTE_VERSION_NUM(17, 05, 0, 4)
366 		rte_pci_register(&driver->driver);
367 #else
368 		rte_eal_pci_register(&driver->driver);
369 #endif
370 	}
371 
372 	driver->cb_fn = enum_cb;
373 	driver->cb_arg = enum_ctx;
374 
375 #if RTE_VERSION >= RTE_VERSION_NUM(17, 11, 0, 3)
376 	if (rte_bus_scan() != 0 || rte_bus_probe() != 0) {
377 #elif RTE_VERSION >= RTE_VERSION_NUM(17, 05, 0, 4)
378 	if (rte_pci_scan() != 0 || rte_pci_probe() != 0) {
379 #else
380 	if (rte_eal_pci_scan() != 0 || rte_eal_pci_probe() != 0) {
381 #endif
382 		driver->cb_arg = NULL;
383 		driver->cb_fn = NULL;
384 		pthread_mutex_unlock(&g_pci_mutex);
385 		return -1;
386 	}
387 
388 	driver->cb_arg = NULL;
389 	driver->cb_fn = NULL;
390 	pthread_mutex_unlock(&g_pci_mutex);
391 
392 	return 0;
393 }
394 
395 int
396 spdk_pci_device_map_bar(struct spdk_pci_device *dev, uint32_t bar,
397 			void **mapped_addr, uint64_t *phys_addr, uint64_t *size)
398 {
399 	return dev->map_bar(dev, bar, mapped_addr, phys_addr, size);
400 }
401 
402 int
403 spdk_pci_device_unmap_bar(struct spdk_pci_device *dev, uint32_t bar, void *addr)
404 {
405 	return dev->unmap_bar(dev, bar, addr);
406 }
407 
408 uint32_t
409 spdk_pci_device_get_domain(struct spdk_pci_device *dev)
410 {
411 	return dev->addr.domain;
412 }
413 
414 uint8_t
415 spdk_pci_device_get_bus(struct spdk_pci_device *dev)
416 {
417 	return dev->addr.bus;
418 }
419 
420 uint8_t
421 spdk_pci_device_get_dev(struct spdk_pci_device *dev)
422 {
423 	return dev->addr.dev;
424 }
425 
426 uint8_t
427 spdk_pci_device_get_func(struct spdk_pci_device *dev)
428 {
429 	return dev->addr.func;
430 }
431 
432 uint16_t
433 spdk_pci_device_get_vendor_id(struct spdk_pci_device *dev)
434 {
435 	return dev->id.vendor_id;
436 }
437 
438 uint16_t
439 spdk_pci_device_get_device_id(struct spdk_pci_device *dev)
440 {
441 	return dev->id.device_id;
442 }
443 
444 uint16_t
445 spdk_pci_device_get_subvendor_id(struct spdk_pci_device *dev)
446 {
447 	return dev->id.subvendor_id;
448 }
449 
450 uint16_t
451 spdk_pci_device_get_subdevice_id(struct spdk_pci_device *dev)
452 {
453 	return dev->id.subdevice_id;
454 }
455 
456 struct spdk_pci_id
457 spdk_pci_device_get_id(struct spdk_pci_device *dev)
458 {
459 	return dev->id;
460 }
461 
462 int
463 spdk_pci_device_get_socket_id(struct spdk_pci_device *dev)
464 {
465 	return dev->socket_id;
466 }
467 
468 int
469 spdk_pci_device_cfg_read(struct spdk_pci_device *dev, void *value, uint32_t len, uint32_t offset)
470 {
471 	return dev->cfg_read(dev, value, len, offset);
472 }
473 
474 int
475 spdk_pci_device_cfg_write(struct spdk_pci_device *dev, void *value, uint32_t len, uint32_t offset)
476 {
477 	return dev->cfg_write(dev, value, len, offset);
478 }
479 
480 int
481 spdk_pci_device_cfg_read8(struct spdk_pci_device *dev, uint8_t *value, uint32_t offset)
482 {
483 	return spdk_pci_device_cfg_read(dev, value, 1, offset);
484 }
485 
486 int
487 spdk_pci_device_cfg_write8(struct spdk_pci_device *dev, uint8_t value, uint32_t offset)
488 {
489 	return spdk_pci_device_cfg_write(dev, &value, 1, offset);
490 }
491 
492 int
493 spdk_pci_device_cfg_read16(struct spdk_pci_device *dev, uint16_t *value, uint32_t offset)
494 {
495 	return spdk_pci_device_cfg_read(dev, value, 2, offset);
496 }
497 
498 int
499 spdk_pci_device_cfg_write16(struct spdk_pci_device *dev, uint16_t value, uint32_t offset)
500 {
501 	return spdk_pci_device_cfg_write(dev, &value, 2, offset);
502 }
503 
504 int
505 spdk_pci_device_cfg_read32(struct spdk_pci_device *dev, uint32_t *value, uint32_t offset)
506 {
507 	return spdk_pci_device_cfg_read(dev, value, 4, offset);
508 }
509 
510 int
511 spdk_pci_device_cfg_write32(struct spdk_pci_device *dev, uint32_t value, uint32_t offset)
512 {
513 	return spdk_pci_device_cfg_write(dev, &value, 4, offset);
514 }
515 
516 int
517 spdk_pci_device_get_serial_number(struct spdk_pci_device *dev, char *sn, size_t len)
518 {
519 	int err;
520 	uint32_t pos, header = 0;
521 	uint32_t i, buf[2];
522 
523 	if (len < 17) {
524 		return -1;
525 	}
526 
527 	err = spdk_pci_device_cfg_read32(dev, &header, PCI_CFG_SIZE);
528 	if (err || !header) {
529 		return -1;
530 	}
531 
532 	pos = PCI_CFG_SIZE;
533 	while (1) {
534 		if ((header & 0x0000ffff) == PCI_EXT_CAP_ID_SN) {
535 			if (pos) {
536 				/* skip the header */
537 				pos += 4;
538 				for (i = 0; i < 2; i++) {
539 					err = spdk_pci_device_cfg_read32(dev, &buf[i], pos + 4 * i);
540 					if (err) {
541 						return -1;
542 					}
543 				}
544 				snprintf(sn, len, "%08x%08x", buf[1], buf[0]);
545 				return 0;
546 			}
547 		}
548 		pos = (header >> 20) & 0xffc;
549 		/* 0 if no other items exist */
550 		if (pos < PCI_CFG_SIZE) {
551 			return -1;
552 		}
553 		err = spdk_pci_device_cfg_read32(dev, &header, pos);
554 		if (err) {
555 			return -1;
556 		}
557 	}
558 	return -1;
559 }
560 
561 struct spdk_pci_addr
562 spdk_pci_device_get_addr(struct spdk_pci_device *dev)
563 {
564 	return dev->addr;
565 }
566 
567 int
568 spdk_pci_addr_compare(const struct spdk_pci_addr *a1, const struct spdk_pci_addr *a2)
569 {
570 	if (a1->domain > a2->domain) {
571 		return 1;
572 	} else if (a1->domain < a2->domain) {
573 		return -1;
574 	} else if (a1->bus > a2->bus) {
575 		return 1;
576 	} else if (a1->bus < a2->bus) {
577 		return -1;
578 	} else if (a1->dev > a2->dev) {
579 		return 1;
580 	} else if (a1->dev < a2->dev) {
581 		return -1;
582 	} else if (a1->func > a2->func) {
583 		return 1;
584 	} else if (a1->func < a2->func) {
585 		return -1;
586 	}
587 
588 	return 0;
589 }
590 
591 #ifdef __linux__
592 int
593 spdk_pci_device_claim(const struct spdk_pci_addr *pci_addr)
594 {
595 	int dev_fd;
596 	char dev_name[64];
597 	int pid;
598 	void *dev_map;
599 	struct flock pcidev_lock = {
600 		.l_type = F_WRLCK,
601 		.l_whence = SEEK_SET,
602 		.l_start = 0,
603 		.l_len = 0,
604 	};
605 
606 	snprintf(dev_name, sizeof(dev_name), "/tmp/spdk_pci_lock_%04x:%02x:%02x.%x", pci_addr->domain,
607 		 pci_addr->bus,
608 		 pci_addr->dev, pci_addr->func);
609 
610 	dev_fd = open(dev_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
611 	if (dev_fd == -1) {
612 		fprintf(stderr, "could not open %s\n", dev_name);
613 		return -1;
614 	}
615 
616 	if (ftruncate(dev_fd, sizeof(int)) != 0) {
617 		fprintf(stderr, "could not truncate %s\n", dev_name);
618 		close(dev_fd);
619 		return -1;
620 	}
621 
622 	dev_map = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE,
623 		       MAP_SHARED, dev_fd, 0);
624 	if (dev_map == MAP_FAILED) {
625 		fprintf(stderr, "could not mmap dev %s (%d)\n", dev_name, errno);
626 		close(dev_fd);
627 		return -1;
628 	}
629 
630 	if (fcntl(dev_fd, F_SETLK, &pcidev_lock) != 0) {
631 		pid = *(int *)dev_map;
632 		fprintf(stderr, "Cannot create lock on device %s, probably"
633 			" process %d has claimed it\n", dev_name, pid);
634 		munmap(dev_map, sizeof(int));
635 		close(dev_fd);
636 		return -1;
637 	}
638 
639 	*(int *)dev_map = (int)getpid();
640 	munmap(dev_map, sizeof(int));
641 	/* Keep dev_fd open to maintain the lock. */
642 	return dev_fd;
643 }
644 #endif /* __linux__ */
645 
646 #ifdef __FreeBSD__
647 int
648 spdk_pci_device_claim(const struct spdk_pci_addr *pci_addr)
649 {
650 	/* TODO */
651 	return 0;
652 }
653 #endif /* __FreeBSD__ */
654 
655 int
656 spdk_pci_addr_parse(struct spdk_pci_addr *addr, const char *bdf)
657 {
658 	unsigned domain, bus, dev, func;
659 
660 	if (addr == NULL || bdf == NULL) {
661 		return -EINVAL;
662 	}
663 
664 	if ((sscanf(bdf, "%x:%x:%x.%x", &domain, &bus, &dev, &func) == 4) ||
665 	    (sscanf(bdf, "%x.%x.%x.%x", &domain, &bus, &dev, &func) == 4)) {
666 		/* Matched a full address - all variables are initialized */
667 	} else if (sscanf(bdf, "%x:%x:%x", &domain, &bus, &dev) == 3) {
668 		func = 0;
669 	} else if ((sscanf(bdf, "%x:%x.%x", &bus, &dev, &func) == 3) ||
670 		   (sscanf(bdf, "%x.%x.%x", &bus, &dev, &func) == 3)) {
671 		domain = 0;
672 	} else if ((sscanf(bdf, "%x:%x", &bus, &dev) == 2) ||
673 		   (sscanf(bdf, "%x.%x", &bus, &dev) == 2)) {
674 		domain = 0;
675 		func = 0;
676 	} else {
677 		return -EINVAL;
678 	}
679 
680 	if (bus > 0xFF || dev > 0x1F || func > 7) {
681 		return -EINVAL;
682 	}
683 
684 	addr->domain = domain;
685 	addr->bus = bus;
686 	addr->dev = dev;
687 	addr->func = func;
688 
689 	return 0;
690 }
691 
692 int
693 spdk_pci_addr_fmt(char *bdf, size_t sz, const struct spdk_pci_addr *addr)
694 {
695 	int rc;
696 
697 	rc = snprintf(bdf, sz, "%04x:%02x:%02x.%x",
698 		      addr->domain, addr->bus,
699 		      addr->dev, addr->func);
700 
701 	if (rc > 0 && (size_t)rc < sz) {
702 		return 0;
703 	}
704 
705 	return -1;
706 }
707 
708 void
709 spdk_pci_hook_device(struct spdk_pci_driver *drv, struct spdk_pci_device *dev)
710 {
711 	assert(dev->map_bar != NULL);
712 	assert(dev->unmap_bar != NULL);
713 	assert(dev->cfg_read != NULL);
714 	assert(dev->cfg_write != NULL);
715 	assert(dev->detach != NULL);
716 	dev->internal.driver = drv;
717 	TAILQ_INSERT_TAIL(&g_pci_devices, dev, internal.tailq);
718 }
719 
720 void
721 spdk_pci_unhook_device(struct spdk_pci_device *dev)
722 {
723 	assert(!dev->internal.attached);
724 	TAILQ_REMOVE(&g_pci_devices, dev, internal.tailq);
725 }
726