xref: /haiku/src/add-ons/kernel/drivers/disk/nvme/nvme_disk.cpp (revision 5c1e072463878d1d30d9ecb9842e6d461132306e)
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 	struct nvme_ns*			ns;
79 
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 		return ret;
350 
351 	await_status(qpinfo->qpair, status);
352 
353 	if (status != B_OK)
354 		*rounded_len = 0;
355 	return status;
356 }
357 
358 
359 static status_t
360 do_nvme_segmented_io(nvme_disk_driver_info* info, off_t rounded_pos,
361 	void* buffer, size_t* rounded_len, bool write = false)
362 {
363 	// The max transfer size is already a multiple of the block size,
364 	// so divide and iterate appropriately. In the case where the length
365 	// is less than the maximum transfer size, we'll wind up with 0 in the
366 	// division, and only one transfer to take care of.
367 	const size_t max_xfer = info->max_transfer_size;
368 	int32 transfers = *rounded_len / max_xfer;
369 	if ((*rounded_len % max_xfer) != 0)
370 		transfers++;
371 
372 	size_t transferred = 0;
373 	for (int32 i = 0; i < transfers; i++) {
374 		size_t transfer_len = max_xfer;
375 		// The last transfer will usually be smaller.
376 		if (i == (transfers - 1))
377 			transfer_len = *rounded_len - transferred;
378 
379 		status_t status = do_nvme_io(info, rounded_pos, buffer,
380 			&transfer_len, write);
381 		if (status != B_OK) {
382 			*rounded_len = transferred;
383 			return transferred > 0 ? (write ? B_PARTIAL_WRITE : B_PARTIAL_READ)
384 				: status;
385 		}
386 
387 		transferred += transfer_len;
388 		rounded_pos += transfer_len;
389 		buffer = ((int8*)buffer) + transfer_len;
390 	}
391 	return B_OK;
392 }
393 
394 
395 static status_t
396 nvme_disk_read(void* cookie, off_t pos, void* buffer, size_t* length)
397 {
398 	CALLED();
399 	nvme_disk_handle* handle = (nvme_disk_handle*)cookie;
400 	const size_t block_size = handle->info->block_size;
401 
402 	// libnvme does transfers in units of device sectors, so if we have to
403 	// round either the position or the length, we will need a bounce buffer.
404 	const off_t rounded_pos = ROUNDDOWN(pos, block_size);
405 	size_t rounded_len = ROUNDUP((*length) + (pos - rounded_pos), block_size);
406 	if (rounded_pos != pos || rounded_len != *length
407 			|| IS_USER_ADDRESS(buffer)) {
408 		void* bounceBuffer = malloc(rounded_len);
409 		MemoryDeleter _(bounceBuffer);
410 		if (bounceBuffer == NULL) {
411 			*length = 0;
412 			return B_NO_MEMORY;
413 		}
414 
415 		status_t status = nvme_disk_read(cookie, rounded_pos, bounceBuffer,
416 			&rounded_len);
417 		if (status != B_OK) {
418 			// The "rounded_len" will be the actual transferred length, but
419 			// of course it will contain the padding.
420 			*length = std::min(*length, std::max((size_t)0,
421 				rounded_len - (size_t)(pos - rounded_pos)));
422 			if (*length == 0)
423 				return status;
424 		}
425 
426 		void* offsetBuffer = ((int8*)bounceBuffer) + (pos - rounded_pos);
427 		if (IS_USER_ADDRESS(buffer))
428 			status = user_memcpy(buffer, offsetBuffer, *length);
429 		else
430 			memcpy(buffer, offsetBuffer, *length);
431 		return status;
432 	}
433 
434 	// If we got here, that means the arguments are already rounded to LBAs,
435 	// so just do the I/O directly.
436 	return do_nvme_segmented_io(handle->info, pos, buffer, length);
437 }
438 
439 
440 static status_t
441 nvme_disk_write(void* cookie, off_t pos, const void* buffer, size_t* length)
442 {
443 	CALLED();
444 	nvme_disk_handle* handle = (nvme_disk_handle*)cookie;
445 	const size_t block_size = handle->info->block_size;
446 
447 	const off_t rounded_pos = ROUNDDOWN(pos, block_size);
448 	size_t rounded_len = ROUNDUP((*length) + (pos - rounded_pos), block_size);
449 	if (rounded_pos != pos || rounded_len != *length
450 			|| IS_USER_ADDRESS(buffer)) {
451 		void* bounceBuffer = malloc(rounded_len);
452 		MemoryDeleter _(bounceBuffer);
453 		if (bounceBuffer == NULL) {
454 			*length = 0;
455 			return B_NO_MEMORY;
456 		}
457 
458 		// Since we rounded, we need to read in the first and last logical
459 		// blocks before we copy our information to the bounce buffer.
460 		// TODO: This would be faster if we queued both reads at once!
461 		size_t readlen = block_size;
462 		status_t status = do_nvme_io(handle->info, rounded_pos, bounceBuffer,
463 			&readlen);
464 		if (status != B_OK) {
465 			*length = 0;
466 			return status;
467 		}
468 		if (rounded_len > block_size) {
469 			off_t offset = rounded_len - block_size;
470 			status = do_nvme_io(handle->info, rounded_pos + offset,
471 				((int8*)bounceBuffer) + offset, &readlen);
472 			if (status != B_OK) {
473 				*length = 0;
474 				return status;
475 			}
476 		}
477 
478 		void* offsetBuffer = ((int8*)bounceBuffer) + (pos - rounded_pos);
479 		if (IS_USER_ADDRESS(buffer))
480 			status = user_memcpy(offsetBuffer, buffer, *length);
481 		else
482 			memcpy(offsetBuffer, buffer, *length);
483 		if (status != B_OK) {
484 			*length = 0;
485 			return status;
486 		}
487 
488 		status = nvme_disk_write(cookie, rounded_pos, bounceBuffer,
489 			&rounded_len);
490 		if (status != B_OK) {
491 			*length = std::min(*length, std::max((size_t)0,
492 				rounded_len - (size_t)(pos - rounded_pos)));
493 		}
494 		return status;
495 	}
496 
497 	// If we got here, that means the arguments are already rounded to LBAs,
498 	// so just do the I/O directly.
499 	return do_nvme_segmented_io(handle->info, pos, (void*)buffer, length, true);
500 }
501 
502 
503 static status_t
504 nvme_disk_flush(nvme_disk_driver_info* info)
505 {
506 	status_t status = EINPROGRESS;
507 
508 	qpair_info* qpinfo = get_next_qpair(info);
509 	mutex_lock(&qpinfo->mtx);
510 	int ret = nvme_ns_flush(info->ns, qpinfo->qpair,
511 		(nvme_cmd_cb)disk_io_callback, &status);
512 	mutex_unlock(&qpinfo->mtx);
513 	if (ret != 0)
514 		return ret;
515 
516 	await_status(qpinfo->qpair, status);
517 	return status;
518 }
519 
520 
521 static status_t
522 nvme_disk_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
523 {
524 	CALLED();
525 	nvme_disk_handle* handle = (nvme_disk_handle*)cookie;
526 	nvme_disk_driver_info* info = handle->info;
527 
528 	TRACE("ioctl(op = %" B_PRId32 ")\n", op);
529 
530 	switch (op) {
531 		case B_GET_MEDIA_STATUS:
532 		{
533 			*(status_t *)buffer = info->media_status;
534 			info->media_status = B_OK;
535 			return B_OK;
536 			break;
537 		}
538 
539 		case B_GET_DEVICE_SIZE:
540 		{
541 			size_t size = info->capacity * info->block_size;
542 			return user_memcpy(buffer, &size, sizeof(size_t));
543 		}
544 
545 		case B_GET_GEOMETRY:
546 		{
547 			if (buffer == NULL /*|| length != sizeof(device_geometry)*/)
548 				return B_BAD_VALUE;
549 
550 		 	device_geometry geometry;
551 			status_t status = get_geometry(handle, &geometry);
552 			if (status != B_OK)
553 				return status;
554 
555 			return user_memcpy(buffer, &geometry, sizeof(device_geometry));
556 		}
557 
558 		case B_GET_ICON_NAME:
559 			return user_strlcpy((char*)buffer, "devices/drive-harddisk",
560 				B_FILE_NAME_LENGTH);
561 
562 		case B_GET_VECTOR_ICON:
563 		{
564 			device_icon iconData;
565 			if (length != sizeof(device_icon))
566 				return B_BAD_VALUE;
567 			if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK)
568 				return B_BAD_ADDRESS;
569 
570 			if (iconData.icon_size >= (int32)sizeof(kDriveIcon)) {
571 				if (user_memcpy(iconData.icon_data, kDriveIcon,
572 						sizeof(kDriveIcon)) != B_OK)
573 					return B_BAD_ADDRESS;
574 			}
575 
576 			iconData.icon_size = sizeof(kDriveIcon);
577 			return user_memcpy(buffer, &iconData, sizeof(device_icon));
578 		}
579 
580 		case B_FLUSH_DRIVE_CACHE:
581 			return nvme_disk_flush(info);
582 	}
583 
584 	return B_DEV_INVALID_IOCTL;
585 }
586 
587 
588 //	#pragma mark - driver module API
589 
590 
591 static float
592 nvme_disk_supports_device(device_node *parent)
593 {
594 	CALLED();
595 
596 	const char* bus;
597 	uint16 baseClass, subClass;
598 
599 	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false) != B_OK
600 		|| sDeviceManager->get_attr_uint16(parent, B_DEVICE_TYPE, &baseClass, false) != B_OK
601 		|| sDeviceManager->get_attr_uint16(parent, B_DEVICE_SUB_TYPE, &subClass, false) != B_OK)
602 		return -1.0f;
603 
604 	if (strcmp(bus, "pci") != 0 || baseClass != PCI_mass_storage)
605 		return 0.0f;
606 
607 	if (subClass != PCI_nvm)
608 		return 0.0f;
609 
610 	TRACE("NVMe device found!\n");
611 	return 1.0f;
612 }
613 
614 
615 static status_t
616 nvme_disk_register_device(device_node* parent)
617 {
618 	CALLED();
619 
620 	device_attr attrs[] = {
621 		{ NULL }
622 	};
623 
624 	return sDeviceManager->register_node(parent, NVME_DISK_DRIVER_MODULE_NAME,
625 		attrs, NULL, NULL);
626 }
627 
628 
629 static status_t
630 nvme_disk_init_driver(device_node* node, void** cookie)
631 {
632 	CALLED();
633 
634 	int ret = nvme_lib_init((enum nvme_log_level)0, (enum nvme_log_facility)0, NULL);
635 	if (ret != 0) {
636 		TRACE_ERROR("libnvme initialization failed!\n");
637 		return ret;
638 	}
639 
640 	nvme_disk_driver_info* info = (nvme_disk_driver_info*)malloc(
641 		sizeof(nvme_disk_driver_info));
642 	if (info == NULL)
643 		return B_NO_MEMORY;
644 
645 	memset(info, 0, sizeof(*info));
646 
647 	info->media_status = B_OK;
648 	info->node = node;
649 
650 	*cookie = info;
651 	return B_OK;
652 }
653 
654 
655 static void
656 nvme_disk_uninit_driver(void* _cookie)
657 {
658 	CALLED();
659 
660 	nvme_disk_driver_info* info = (nvme_disk_driver_info*)_cookie;
661 	free(info);
662 }
663 
664 
665 static status_t
666 nvme_disk_register_child_devices(void* _cookie)
667 {
668 	CALLED();
669 
670 	nvme_disk_driver_info* info = (nvme_disk_driver_info*)_cookie;
671 	status_t status;
672 
673 	int32 id = sDeviceManager->create_id(NVME_DISK_DEVICE_ID_GENERATOR);
674 	if (id < 0)
675 		return id;
676 
677 	char name[64];
678 	snprintf(name, sizeof(name), "disk/nvme/%" B_PRId32 "/raw",
679 		id);
680 
681 	status = sDeviceManager->publish_device(info->node, name,
682 		NVME_DISK_DEVICE_MODULE_NAME);
683 
684 	return status;
685 }
686 
687 
688 //	#pragma mark -
689 
690 
691 module_dependency module_dependencies[] = {
692 	{B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager},
693 	{}
694 };
695 
696 struct device_module_info sNvmeDiskDevice = {
697 	{
698 		NVME_DISK_DEVICE_MODULE_NAME,
699 		0,
700 		NULL
701 	},
702 
703 	nvme_disk_init_device,
704 	nvme_disk_uninit_device,
705 	NULL, // remove,
706 
707 	nvme_disk_open,
708 	nvme_disk_close,
709 	nvme_disk_free,
710 	nvme_disk_read,
711 	nvme_disk_write,
712 	NULL,
713 	nvme_disk_ioctl,
714 
715 	NULL,	// select
716 	NULL,	// deselect
717 };
718 
719 struct driver_module_info sNvmeDiskDriver = {
720 	{
721 		NVME_DISK_DRIVER_MODULE_NAME,
722 		0,
723 		NULL
724 	},
725 
726 	nvme_disk_supports_device,
727 	nvme_disk_register_device,
728 	nvme_disk_init_driver,
729 	nvme_disk_uninit_driver,
730 	nvme_disk_register_child_devices,
731 	NULL,	// rescan
732 	NULL,	// removed
733 };
734 
735 module_info* modules[] = {
736 	(module_info*)&sNvmeDiskDriver,
737 	(module_info*)&sNvmeDiskDevice,
738 	NULL
739 };
740