xref: /haiku/src/add-ons/kernel/drivers/disk/nvme/nvme_disk.cpp (revision 9d010ea47db677131e385b5e7855d38fd0c8103f)
1 /*
2  * Copyright 2019, Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Augustin Cavalier <waddlesplash>
7  */
8 
9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 
13 #include <algorithm>
14 #include <AutoDeleter.h>
15 #include <kernel.h>
16 #include <util/AutoLock.h>
17 
18 #include <fs/devfs.h>
19 #include <bus/PCI.h>
20 
21 extern "C" {
22 #include <libnvme/nvme.h>
23 }
24 
25 
26 //#define TRACE_NVME_DISK
27 #ifdef TRACE_NVME_DISK
28 #	define TRACE(x...) dprintf("nvme_disk: " x)
29 #else
30 #	define TRACE(x...) ;
31 #endif
32 #define TRACE_ALWAYS(x...)	dprintf("nvme_disk: " x)
33 #define TRACE_ERROR(x...)	dprintf("\33[33mnvme_disk:\33[0m " x)
34 #define CALLED() 			TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
35 
36 
37 static const uint8 kDriveIcon[] = {
38 	0x6e, 0x63, 0x69, 0x66, 0x08, 0x03, 0x01, 0x00, 0x00, 0x02, 0x00, 0x16,
39 	0x02, 0x3c, 0xc7, 0xee, 0x38, 0x9b, 0xc0, 0xba, 0x16, 0x57, 0x3e, 0x39,
40 	0xb0, 0x49, 0x77, 0xc8, 0x42, 0xad, 0xc7, 0x00, 0xff, 0xff, 0xd3, 0x02,
41 	0x00, 0x06, 0x02, 0x3c, 0x96, 0x32, 0x3a, 0x4d, 0x3f, 0xba, 0xfc, 0x01,
42 	0x3d, 0x5a, 0x97, 0x4b, 0x57, 0xa5, 0x49, 0x84, 0x4d, 0x00, 0x47, 0x47,
43 	0x47, 0xff, 0xa5, 0xa0, 0xa0, 0x02, 0x00, 0x16, 0x02, 0xbc, 0x59, 0x2f,
44 	0xbb, 0x29, 0xa7, 0x3c, 0x0c, 0xe4, 0xbd, 0x0b, 0x7c, 0x48, 0x92, 0xc0,
45 	0x4b, 0x79, 0x66, 0x00, 0x7d, 0xff, 0xd4, 0x02, 0x00, 0x06, 0x02, 0x38,
46 	0xdb, 0xb4, 0x39, 0x97, 0x33, 0xbc, 0x4a, 0x33, 0x3b, 0xa5, 0x42, 0x48,
47 	0x6e, 0x66, 0x49, 0xee, 0x7b, 0x00, 0x59, 0x67, 0x56, 0xff, 0xeb, 0xb2,
48 	0xb2, 0x03, 0xa7, 0xff, 0x00, 0x03, 0xff, 0x00, 0x00, 0x04, 0x01, 0x80,
49 	0x07, 0x0a, 0x06, 0x22, 0x3c, 0x22, 0x49, 0x44, 0x5b, 0x5a, 0x3e, 0x5a,
50 	0x31, 0x39, 0x25, 0x0a, 0x04, 0x22, 0x3c, 0x44, 0x4b, 0x5a, 0x31, 0x39,
51 	0x25, 0x0a, 0x04, 0x44, 0x4b, 0x44, 0x5b, 0x5a, 0x3e, 0x5a, 0x31, 0x0a,
52 	0x04, 0x22, 0x3c, 0x22, 0x49, 0x44, 0x5b, 0x44, 0x4b, 0x08, 0x02, 0x27,
53 	0x43, 0xb8, 0x14, 0xc1, 0xf1, 0x08, 0x02, 0x26, 0x43, 0x29, 0x44, 0x0a,
54 	0x05, 0x44, 0x5d, 0x49, 0x5d, 0x60, 0x3e, 0x5a, 0x3b, 0x5b, 0x3f, 0x08,
55 	0x0a, 0x07, 0x01, 0x06, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x10, 0x01, 0x17,
56 	0x84, 0x00, 0x04, 0x0a, 0x01, 0x01, 0x01, 0x00, 0x0a, 0x02, 0x01, 0x02,
57 	0x00, 0x0a, 0x03, 0x01, 0x03, 0x00, 0x0a, 0x04, 0x01, 0x04, 0x10, 0x01,
58 	0x17, 0x85, 0x20, 0x04, 0x0a, 0x06, 0x01, 0x05, 0x30, 0x24, 0xb3, 0x99,
59 	0x01, 0x17, 0x82, 0x00, 0x04, 0x0a, 0x05, 0x01, 0x05, 0x30, 0x20, 0xb2,
60 	0xe6, 0x01, 0x17, 0x82, 0x00, 0x04
61 };
62 
63 
64 #define NVME_DISK_DRIVER_MODULE_NAME 	"drivers/disk/nvme_disk/driver_v1"
65 #define NVME_DISK_DEVICE_MODULE_NAME 	"drivers/disk/nvme_disk/device_v1"
66 #define NVME_DISK_DEVICE_ID_GENERATOR	"nvme_disk/device_id"
67 
68 #define NVME_MAX_QPAIRS					(8)
69 
70 
71 static device_manager_info* sDeviceManager;
72 
73 typedef struct {
74 	device_node*			node;
75 	pci_info				info;
76 
77 	struct nvme_ctrlr*		ctrlr;
78 
79 	struct nvme_ns*			ns;
80 	uint64					capacity;
81 	uint32					block_size;
82 	size_t					max_transfer_size;
83 	status_t				media_status;
84 
85 	struct qpair_info {
86 		struct nvme_qpair*	qpair;
87 		mutex				mtx;
88 	}						qpairs[NVME_MAX_QPAIRS];
89 	uint32					qpair_count;
90 	uint32					next_qpair;
91 } nvme_disk_driver_info;
92 typedef nvme_disk_driver_info::qpair_info qpair_info;
93 
94 
95 typedef struct {
96 	nvme_disk_driver_info*		info;
97 } nvme_disk_handle;
98 
99 
100 static status_t
101 get_geometry(nvme_disk_handle* handle, device_geometry* geometry)
102 {
103 	nvme_disk_driver_info* info = handle->info;
104 
105 	devfs_compute_geometry_size(geometry, info->capacity, info->block_size);
106 
107 	geometry->device_type = B_DISK;
108 	geometry->removable = false;
109 
110 	geometry->read_only = false;
111 	geometry->write_once = false;
112 
113 	TRACE("get_geometry(): %" B_PRId32 ", %" B_PRId32 ", %" B_PRId32 ", %" B_PRId32 ", %d, %d, %d, %d\n",
114 		geometry->bytes_per_sector, geometry->sectors_per_track,
115 		geometry->cylinder_count, geometry->head_count, geometry->device_type,
116 		geometry->removable, geometry->read_only, geometry->write_once);
117 
118 	return B_OK;
119 }
120 
121 
122 static int
123 log2(uint32 x)
124 {
125 	int y;
126 
127 	for (y = 31; y >= 0; --y) {
128 		if (x == ((uint32)1 << y))
129 			break;
130 	}
131 
132 	return y;
133 }
134 
135 
136 static void
137 nvme_disk_set_capacity(nvme_disk_driver_info* info, uint64 capacity,
138 	uint32 blockSize)
139 {
140 	TRACE("set_capacity(device = %p, capacity = %" B_PRIu64 ", blockSize = %" B_PRIu32 ")\n",
141 		info, capacity, blockSize);
142 
143 	// get log2, if possible
144 	uint32 blockShift = log2(blockSize);
145 
146 	if ((1UL << blockShift) != blockSize)
147 		blockShift = 0;
148 
149 	info->capacity = capacity;
150 	info->block_size = blockSize;
151 }
152 
153 
154 //	#pragma mark - device module API
155 
156 
157 static status_t
158 nvme_disk_init_device(void* _info, void** _cookie)
159 {
160 	CALLED();
161 	nvme_disk_driver_info* info = (nvme_disk_driver_info*)_info;
162 
163 	pci_device_module_info* pci;
164 	pci_device* pcidev;
165 	device_node* parent = sDeviceManager->get_parent_node(info->node);
166 	sDeviceManager->get_driver(parent, (driver_module_info**)&pci,
167 		(void**)&pcidev);
168 	pci->get_pci_info(pcidev, &info->info);
169 	sDeviceManager->put_node(parent);
170 
171 	// construct the libnvme pci_device struct
172 	pci_device* device = new pci_device;
173 	device->vendor_id = info->info.vendor_id;
174 	device->device_id = info->info.device_id;
175 	device->subvendor_id = 0;
176 	device->subdevice_id = 0;
177 
178 	device->domain = 0;
179 	device->bus = info->info.bus;
180 	device->dev = info->info.device;
181 	device->func = info->info.function;
182 
183 	device->pci_info = &info->info;
184 
185 	// open the controller
186 	info->ctrlr = nvme_ctrlr_open(device, NULL);
187 	if (info->ctrlr == NULL) {
188 		TRACE_ERROR("failed to open the controller!\n");
189 		return B_ERROR;
190 	}
191 
192 	struct nvme_ctrlr_stat cstat;
193 	int err = nvme_ctrlr_stat(info->ctrlr, &cstat);
194 	if (err != 0) {
195 		TRACE_ERROR("failed to get controller information!\n");
196 		return err;
197 	}
198 
199 	TRACE_ALWAYS("attached to NVMe device \"%s (%s)\"\n", cstat.mn, cstat.sn);
200 	TRACE_ALWAYS("\tmaximum transfer size: %" B_PRIuSIZE "\n", cstat.max_xfer_size);
201 	TRACE_ALWAYS("\tqpair count: %d\n", cstat.io_qpairs);
202 
203 	// TODO: export more than just the first namespace!
204 	info->ns = nvme_ns_open(info->ctrlr, cstat.ns_ids[0]);
205 	if (info->ns == NULL) {
206 		TRACE_ERROR("failed to open namespace!\n");
207 		return B_ERROR;
208 	}
209 
210 	struct nvme_ns_stat nsstat;
211 	err = nvme_ns_stat(info->ns, &nsstat);
212 	if (err != 0) {
213 		TRACE_ERROR("failed to get namespace information!\n");
214 		return err;
215 	}
216 
217 	// store capacity information
218 	nvme_disk_set_capacity(info, nsstat.sectors, nsstat.sector_size);
219 	info->max_transfer_size = ROUNDDOWN(cstat.max_xfer_size,
220 		nsstat.sector_size);
221 
222 	TRACE("capacity: %" B_PRIu64 ", block_size %" B_PRIu32 "\n",
223 		info->capacity, info->block_size);
224 
225 	// allocate qpairs
226 	info->qpair_count = info->next_qpair = 0;
227 	for (uint32 i = 0; i < NVME_MAX_QPAIRS && i < cstat.io_qpairs; i++) {
228 		info->qpairs[i].qpair = nvme_ioqp_get(info->ctrlr,
229 			(enum nvme_qprio)0, 0);
230 		if (info->qpairs[i].qpair == NULL)
231 			break;
232 
233 		mutex_init(&info->qpairs[i].mtx, "qpair mutex");
234 		info->qpair_count++;
235 	}
236 	if (info->qpair_count == 0) {
237 		TRACE_ERROR("failed to allocate qpairs!\n");
238 		return B_NO_MEMORY;
239 	}
240 
241 	*_cookie = info;
242 	return B_OK;
243 }
244 
245 
246 static void
247 nvme_disk_uninit_device(void* _cookie)
248 {
249 	CALLED();
250 	nvme_disk_driver_info* info = (nvme_disk_driver_info*)_cookie;
251 }
252 
253 
254 static status_t
255 nvme_disk_open(void* _info, const char* path, int openMode, void** _cookie)
256 {
257 	CALLED();
258 
259 	nvme_disk_driver_info* info = (nvme_disk_driver_info*)_info;
260 	nvme_disk_handle* handle = (nvme_disk_handle*)malloc(
261 		sizeof(nvme_disk_handle));
262 	if (handle == NULL)
263 		return B_NO_MEMORY;
264 
265 	handle->info = info;
266 
267 	*_cookie = handle;
268 	return B_OK;
269 }
270 
271 
272 static status_t
273 nvme_disk_close(void* cookie)
274 {
275 	CALLED();
276 
277 	nvme_disk_handle* handle = (nvme_disk_handle*)cookie;
278 	return B_OK;
279 }
280 
281 
282 static status_t
283 nvme_disk_free(void* cookie)
284 {
285 	CALLED();
286 
287 	nvme_disk_handle* handle = (nvme_disk_handle*)cookie;
288 	free(handle);
289 	return B_OK;
290 }
291 
292 
293 // #pragma mark - I/O functions
294 
295 
296 static qpair_info*
297 get_next_qpair(nvme_disk_driver_info* info)
298 {
299 	return &info->qpairs[atomic_add((int32*)&info->next_qpair, 1)
300 		% info->qpair_count];
301 }
302 
303 
304 static void
305 disk_io_callback(status_t* status, const struct nvme_cpl* cpl)
306 {
307 	*status = nvme_cpl_is_error(cpl) ? B_IO_ERROR : B_OK;
308 }
309 
310 
311 static void
312 await_status(struct nvme_qpair* qpair, status_t& status)
313 {
314 	while (status == EINPROGRESS) {
315 		// nvme_ioqp_poll uses locking internally on the entire device,
316 		// not just this qpair, so it is entirely possible that it could
317 		// return 0 (i.e. no completions processed) and yet our status
318 		// changed, because some other thread processed the completion
319 		// before we got to it. So, recheck it before sleeping.
320 		if (nvme_ioqp_poll(qpair, 0) == 0 && status == EINPROGRESS)
321 			snooze(5);
322 	}
323 }
324 
325 
326 static status_t
327 do_nvme_io(nvme_disk_driver_info* info, off_t rounded_pos, void* buffer,
328 	size_t* rounded_len, bool write = false)
329 {
330 	CALLED();
331 	const size_t block_size = info->block_size;
332 
333 	status_t status = EINPROGRESS;
334 
335 	qpair_info* qpinfo = get_next_qpair(info);
336 	mutex_lock(&qpinfo->mtx);
337 	int ret = -1;
338 	if (write) {
339 		ret = nvme_ns_write(info->ns, qpinfo->qpair, buffer,
340 			rounded_pos / block_size, *rounded_len / block_size,
341 			(nvme_cmd_cb)disk_io_callback, &status, 0);
342 	} else {
343 		ret = nvme_ns_read(info->ns, qpinfo->qpair, buffer,
344 			rounded_pos / block_size, *rounded_len / block_size,
345 			(nvme_cmd_cb)disk_io_callback, &status, 0);
346 	}
347 	mutex_unlock(&qpinfo->mtx);
348 	if (ret != 0) {
349 		TRACE_ERROR("attempt to queue %s I/O at %" B_PRIdOFF " of %" B_PRIuSIZE
350 			" bytes failed!", write ? "write" : "read", rounded_pos, *rounded_len);
351 		return ret;
352 	}
353 
354 	await_status(qpinfo->qpair, status);
355 
356 	if (status != B_OK) {
357 		TRACE_ERROR("%s at %" B_PRIdOFF " of %" B_PRIuSIZE " bytes failed!",
358 			write ? "write" : "read", rounded_pos, *rounded_len);
359 		*rounded_len = 0;
360 	}
361 	return status;
362 }
363 
364 
365 static status_t
366 do_nvme_segmented_io(nvme_disk_driver_info* info, off_t rounded_pos,
367 	void* buffer, size_t* rounded_len, bool write = false)
368 {
369 	// The max transfer size is already a multiple of the block size,
370 	// so divide and iterate appropriately. In the case where the length
371 	// is less than the maximum transfer size, we'll wind up with 0 in the
372 	// division, and only one transfer to take care of.
373 	const size_t max_xfer = info->max_transfer_size;
374 	int32 transfers = *rounded_len / max_xfer;
375 	if ((*rounded_len % max_xfer) != 0)
376 		transfers++;
377 
378 	size_t transferred = 0;
379 	for (int32 i = 0; i < transfers; i++) {
380 		size_t transfer_len = max_xfer;
381 		// The last transfer will usually be smaller.
382 		if (i == (transfers - 1))
383 			transfer_len = *rounded_len - transferred;
384 
385 		status_t status = do_nvme_io(info, rounded_pos, buffer,
386 			&transfer_len, write);
387 		if (status != B_OK) {
388 			*rounded_len = transferred;
389 			return transferred > 0 ? (write ? B_PARTIAL_WRITE : B_PARTIAL_READ)
390 				: status;
391 		}
392 
393 		transferred += transfer_len;
394 		rounded_pos += transfer_len;
395 		buffer = ((int8*)buffer) + transfer_len;
396 	}
397 	return B_OK;
398 }
399 
400 
401 static status_t
402 nvme_disk_read(void* cookie, off_t pos, void* buffer, size_t* length)
403 {
404 	CALLED();
405 	nvme_disk_handle* handle = (nvme_disk_handle*)cookie;
406 	const size_t block_size = handle->info->block_size;
407 
408 	// libnvme does transfers in units of device sectors, so if we have to
409 	// round either the position or the length, we will need a bounce buffer.
410 	const off_t rounded_pos = ROUNDDOWN(pos, block_size);
411 	size_t rounded_len = ROUNDUP((*length) + (pos - rounded_pos), block_size);
412 	if (rounded_pos != pos || rounded_len != *length
413 			|| IS_USER_ADDRESS(buffer)) {
414 		void* bounceBuffer = malloc(rounded_len);
415 		MemoryDeleter _(bounceBuffer);
416 		if (bounceBuffer == NULL) {
417 			*length = 0;
418 			return B_NO_MEMORY;
419 		}
420 
421 		status_t status = do_nvme_segmented_io(handle->info, rounded_pos,
422 			bounceBuffer, &rounded_len);
423 		if (status != B_OK) {
424 			// The "rounded_len" will be the actual transferred length, but
425 			// of course it will contain the padding.
426 			*length = std::min(*length, (size_t)std::max((off_t)0,
427 				(off_t)rounded_len - (off_t)(pos - rounded_pos)));
428 			if (*length == 0)
429 				return status;
430 		}
431 
432 		void* offsetBuffer = ((int8*)bounceBuffer) + (pos - rounded_pos);
433 		if (IS_USER_ADDRESS(buffer))
434 			status = user_memcpy(buffer, offsetBuffer, *length);
435 		else
436 			memcpy(buffer, offsetBuffer, *length);
437 		return status;
438 	}
439 
440 	// If we got here, that means the arguments are already rounded to LBAs
441 	// and the buffer is a kernel one, so just do the I/O directly.
442 	return do_nvme_segmented_io(handle->info, pos, buffer, length);
443 }
444 
445 
446 static status_t
447 nvme_disk_write(void* cookie, off_t pos, const void* buffer, size_t* length)
448 {
449 	CALLED();
450 	nvme_disk_handle* handle = (nvme_disk_handle*)cookie;
451 	const size_t block_size = handle->info->block_size;
452 
453 	const off_t rounded_pos = ROUNDDOWN(pos, block_size);
454 	size_t rounded_len = ROUNDUP((*length) + (pos - rounded_pos), block_size);
455 	if (rounded_pos != pos || rounded_len != *length
456 			|| IS_USER_ADDRESS(buffer)) {
457 		void* bounceBuffer = malloc(rounded_len);
458 		MemoryDeleter _(bounceBuffer);
459 		if (bounceBuffer == NULL) {
460 			*length = 0;
461 			return B_NO_MEMORY;
462 		}
463 
464 		// Since we rounded, we need to read in the first and last logical
465 		// blocks before we copy our information to the bounce buffer.
466 		// TODO: This would be faster if we queued both reads at once!
467 		size_t readlen = block_size;
468 		status_t status;
469 		if (rounded_pos != pos) {
470 			status = do_nvme_io(handle->info, rounded_pos, bounceBuffer,
471 				&readlen);
472 			if (status != B_OK) {
473 				*length = 0;
474 				return status;
475 			}
476 		}
477 		if (rounded_len > block_size) {
478 			off_t offset = rounded_len - block_size;
479 			status = do_nvme_io(handle->info, rounded_pos + offset,
480 				((int8*)bounceBuffer) + offset, &readlen);
481 			if (status != B_OK) {
482 				*length = 0;
483 				return status;
484 			}
485 		}
486 
487 		// Now we can copy in the actual data to be written.
488 		void* offsetBuffer = ((int8*)bounceBuffer) + (pos - rounded_pos);
489 		if (IS_USER_ADDRESS(buffer))
490 			status = user_memcpy(offsetBuffer, buffer, *length);
491 		else
492 			memcpy(offsetBuffer, buffer, *length);
493 		if (status != B_OK) {
494 			*length = 0;
495 			return status;
496 		}
497 
498 		status = do_nvme_segmented_io(handle->info, rounded_pos, bounceBuffer,
499 			&rounded_len, true);
500 		if (status != B_OK) {
501 			*length = std::min(*length, (size_t)std::max((off_t)0,
502 				(off_t)rounded_len - (off_t)(pos - rounded_pos)));
503 		}
504 		return status;
505 	}
506 
507 	// If we got here, that means the arguments are already rounded to LBAs,
508 	// so just do the I/O directly.
509 	return do_nvme_segmented_io(handle->info, pos, (void*)buffer, length, true);
510 }
511 
512 
513 static status_t
514 nvme_disk_flush(nvme_disk_driver_info* info)
515 {
516 	status_t status = EINPROGRESS;
517 
518 	qpair_info* qpinfo = get_next_qpair(info);
519 	mutex_lock(&qpinfo->mtx);
520 	int ret = nvme_ns_flush(info->ns, qpinfo->qpair,
521 		(nvme_cmd_cb)disk_io_callback, &status);
522 	mutex_unlock(&qpinfo->mtx);
523 	if (ret != 0)
524 		return ret;
525 
526 	await_status(qpinfo->qpair, status);
527 	return status;
528 }
529 
530 
531 static status_t
532 nvme_disk_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
533 {
534 	CALLED();
535 	nvme_disk_handle* handle = (nvme_disk_handle*)cookie;
536 	nvme_disk_driver_info* info = handle->info;
537 
538 	TRACE("ioctl(op = %" B_PRId32 ")\n", op);
539 
540 	switch (op) {
541 		case B_GET_MEDIA_STATUS:
542 		{
543 			*(status_t *)buffer = info->media_status;
544 			info->media_status = B_OK;
545 			return B_OK;
546 			break;
547 		}
548 
549 		case B_GET_DEVICE_SIZE:
550 		{
551 			size_t size = info->capacity * info->block_size;
552 			return user_memcpy(buffer, &size, sizeof(size_t));
553 		}
554 
555 		case B_GET_GEOMETRY:
556 		{
557 			if (buffer == NULL /*|| length != sizeof(device_geometry)*/)
558 				return B_BAD_VALUE;
559 
560 		 	device_geometry geometry;
561 			status_t status = get_geometry(handle, &geometry);
562 			if (status != B_OK)
563 				return status;
564 
565 			return user_memcpy(buffer, &geometry, sizeof(device_geometry));
566 		}
567 
568 		case B_GET_ICON_NAME:
569 			return user_strlcpy((char*)buffer, "devices/drive-harddisk",
570 				B_FILE_NAME_LENGTH);
571 
572 		case B_GET_VECTOR_ICON:
573 		{
574 			device_icon iconData;
575 			if (length != sizeof(device_icon))
576 				return B_BAD_VALUE;
577 			if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK)
578 				return B_BAD_ADDRESS;
579 
580 			if (iconData.icon_size >= (int32)sizeof(kDriveIcon)) {
581 				if (user_memcpy(iconData.icon_data, kDriveIcon,
582 						sizeof(kDriveIcon)) != B_OK)
583 					return B_BAD_ADDRESS;
584 			}
585 
586 			iconData.icon_size = sizeof(kDriveIcon);
587 			return user_memcpy(buffer, &iconData, sizeof(device_icon));
588 		}
589 
590 		case B_FLUSH_DRIVE_CACHE:
591 			return nvme_disk_flush(info);
592 	}
593 
594 	return B_DEV_INVALID_IOCTL;
595 }
596 
597 
598 //	#pragma mark - driver module API
599 
600 
601 static float
602 nvme_disk_supports_device(device_node *parent)
603 {
604 	CALLED();
605 
606 	const char* bus;
607 	uint16 baseClass, subClass;
608 
609 	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false) != B_OK
610 		|| sDeviceManager->get_attr_uint16(parent, B_DEVICE_TYPE, &baseClass, false) != B_OK
611 		|| sDeviceManager->get_attr_uint16(parent, B_DEVICE_SUB_TYPE, &subClass, false) != B_OK)
612 		return -1.0f;
613 
614 	if (strcmp(bus, "pci") != 0 || baseClass != PCI_mass_storage)
615 		return 0.0f;
616 
617 	if (subClass != PCI_nvm)
618 		return 0.0f;
619 
620 	TRACE("NVMe device found!\n");
621 	return 1.0f;
622 }
623 
624 
625 static status_t
626 nvme_disk_register_device(device_node* parent)
627 {
628 	CALLED();
629 
630 	device_attr attrs[] = {
631 		{ NULL }
632 	};
633 
634 	return sDeviceManager->register_node(parent, NVME_DISK_DRIVER_MODULE_NAME,
635 		attrs, NULL, NULL);
636 }
637 
638 
639 static status_t
640 nvme_disk_init_driver(device_node* node, void** cookie)
641 {
642 	CALLED();
643 
644 	int ret = nvme_lib_init((enum nvme_log_level)0, (enum nvme_log_facility)0, NULL);
645 	if (ret != 0) {
646 		TRACE_ERROR("libnvme initialization failed!\n");
647 		return ret;
648 	}
649 
650 	nvme_disk_driver_info* info = (nvme_disk_driver_info*)malloc(
651 		sizeof(nvme_disk_driver_info));
652 	if (info == NULL)
653 		return B_NO_MEMORY;
654 
655 	memset(info, 0, sizeof(*info));
656 
657 	info->media_status = B_OK;
658 	info->node = node;
659 
660 	*cookie = info;
661 	return B_OK;
662 }
663 
664 
665 static void
666 nvme_disk_uninit_driver(void* _cookie)
667 {
668 	CALLED();
669 
670 	nvme_disk_driver_info* info = (nvme_disk_driver_info*)_cookie;
671 	free(info);
672 }
673 
674 
675 static status_t
676 nvme_disk_register_child_devices(void* _cookie)
677 {
678 	CALLED();
679 
680 	nvme_disk_driver_info* info = (nvme_disk_driver_info*)_cookie;
681 	status_t status;
682 
683 	int32 id = sDeviceManager->create_id(NVME_DISK_DEVICE_ID_GENERATOR);
684 	if (id < 0)
685 		return id;
686 
687 	char name[64];
688 	snprintf(name, sizeof(name), "disk/nvme/%" B_PRId32 "/raw",
689 		id);
690 
691 	status = sDeviceManager->publish_device(info->node, name,
692 		NVME_DISK_DEVICE_MODULE_NAME);
693 
694 	return status;
695 }
696 
697 
698 //	#pragma mark -
699 
700 
701 module_dependency module_dependencies[] = {
702 	{B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager},
703 	{}
704 };
705 
706 struct device_module_info sNvmeDiskDevice = {
707 	{
708 		NVME_DISK_DEVICE_MODULE_NAME,
709 		0,
710 		NULL
711 	},
712 
713 	nvme_disk_init_device,
714 	nvme_disk_uninit_device,
715 	NULL, // remove,
716 
717 	nvme_disk_open,
718 	nvme_disk_close,
719 	nvme_disk_free,
720 	nvme_disk_read,
721 	nvme_disk_write,
722 	NULL,
723 	nvme_disk_ioctl,
724 
725 	NULL,	// select
726 	NULL,	// deselect
727 };
728 
729 struct driver_module_info sNvmeDiskDriver = {
730 	{
731 		NVME_DISK_DRIVER_MODULE_NAME,
732 		0,
733 		NULL
734 	},
735 
736 	nvme_disk_supports_device,
737 	nvme_disk_register_device,
738 	nvme_disk_init_driver,
739 	nvme_disk_uninit_driver,
740 	nvme_disk_register_child_devices,
741 	NULL,	// rescan
742 	NULL,	// removed
743 };
744 
745 module_info* modules[] = {
746 	(module_info*)&sNvmeDiskDriver,
747 	(module_info*)&sNvmeDiskDevice,
748 	NULL
749 };
750