git: 28fefc441e3b - main - ufshci: add a control device node

From: Jaeyoon Choi <jaeyoon_at_FreeBSD.org>
Date: Tue, 15 Sep 2026 01:47:47 UTC
The branch main has been updated by jaeyoon:

URL: https://cgit.FreeBSD.org/src/commit/?id=28fefc441e3b701acc2888892a518774394255c7

commit 28fefc441e3b701acc2888892a518774394255c7
Author:     Jaeyoon Choi <jaeyoon@FreeBSD.org>
AuthorDate: 2026-09-15 01:39:41 +0000
Commit:     Jaeyoon Choi <jaeyoon@FreeBSD.org>
CommitDate: 2026-09-15 01:44:33 +0000

    ufshci: add a control device node
    
    The driver only exposed a CAM SIM. Userland had no way to reach the
    device for anything that is not a SCSI command, so reading a descriptor
    or an attribute was impossible.
    
    Add /dev/ufshci%d as a root only node and the ioctl ABI header for it.
    The node answers no ioctl yet. The header pulls in ufshci.h, which
    declares bool only under _KERNEL, so include stdbool.h for userland the
    way nvme.h already does.
    
    Reviewed by:    imp (mentor)
    Sponsored by:   Samsung Electronics
    Differential Revision:  https://reviews.freebsd.org/D59558
---
 sys/dev/ufshci/ufshci.h         |  3 ++
 sys/dev/ufshci/ufshci_ctrlr.c   |  6 ++++
 sys/dev/ufshci/ufshci_ioctl.c   | 67 +++++++++++++++++++++++++++++++++++++++++
 sys/dev/ufshci/ufshci_ioctl.h   | 54 +++++++++++++++++++++++++++++++++
 sys/dev/ufshci/ufshci_private.h |  6 ++++
 sys/modules/ufshci/Makefile     |  1 +
 6 files changed, 137 insertions(+)

diff --git a/sys/dev/ufshci/ufshci.h b/sys/dev/ufshci/ufshci.h
index ab55c0af938f..aaf98cc70d37 100644
--- a/sys/dev/ufshci/ufshci.h
+++ b/sys/dev/ufshci/ufshci.h
@@ -9,6 +9,9 @@
 #define __UFSHCI_H__
 
 #include <sys/param.h>
+#ifndef _KERNEL
+#include <stdbool.h>
+#endif
 #include <sys/endian.h>
 
 /*
diff --git a/sys/dev/ufshci/ufshci_ctrlr.c b/sys/dev/ufshci/ufshci_ctrlr.c
index 480bbfd43d81..58f101ea27f6 100644
--- a/sys/dev/ufshci/ufshci_ctrlr.c
+++ b/sys/dev/ufshci/ufshci_ctrlr.c
@@ -459,12 +459,18 @@ ufshci_ctrlr_construct(struct ufshci_controller *ctrlr, device_t dev)
 
 	TASK_INIT(&ctrlr->reset_task, 0, ufshci_ctrlr_reset_task, ctrlr);
 
+	error = ufshci_ioctl_construct(ctrlr, dev);
+	if (error)
+		return (error);
+
 	return (0);
 }
 
 void
 ufshci_ctrlr_destruct(struct ufshci_controller *ctrlr, device_t dev)
 {
+	ufshci_ioctl_destruct(ctrlr);
+
 	if (ctrlr->resource == NULL)
 		goto nores;
 
diff --git a/sys/dev/ufshci/ufshci_ioctl.c b/sys/dev/ufshci/ufshci_ioctl.c
new file mode 100644
index 000000000000..976b28de4cf5
--- /dev/null
+++ b/sys/dev/ufshci/ufshci_ioctl.c
@@ -0,0 +1,67 @@
+/*-
+ * Copyright (c) 2026, Samsung Electronics Co., Ltd.
+ * Written by Jaeyoon Choi
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/ioccom.h>
+
+#include "ufshci_private.h"
+#include "ufshci_ioctl.h"
+
+static d_ioctl_t ufshci_ioctl;
+
+static struct cdevsw ufshci_cdevsw = {
+	.d_version = D_VERSION,
+	.d_ioctl = ufshci_ioctl,
+	.d_name = "ufshci",
+};
+
+static int
+ufshci_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int flag,
+    struct thread *td)
+{
+	switch (cmd) {
+	default:
+		return (ENOTTY);
+	}
+}
+
+int
+ufshci_ioctl_construct(struct ufshci_controller *ctrlr, device_t dev)
+{
+	struct make_dev_args md_args;
+	int error;
+
+	/*
+	 * Set si_drv1 as the node is created. A separate assignment after
+	 * make_dev leaves a window where an open that races the attach finds
+	 * it unset.
+	 */
+	make_dev_args_init(&md_args);
+	md_args.mda_devsw = &ufshci_cdevsw;
+	md_args.mda_uid = UID_ROOT;
+	md_args.mda_gid = GID_WHEEL;
+	md_args.mda_mode = 0600;
+	md_args.mda_unit = device_get_unit(dev);
+	md_args.mda_si_drv1 = ctrlr;
+
+	error = make_dev_s(&md_args, &ctrlr->cdev, "ufshci%d",
+	    device_get_unit(dev));
+	if (error != 0)
+		return (error);
+
+	return (0);
+}
+
+void
+ufshci_ioctl_destruct(struct ufshci_controller *ctrlr)
+{
+	if (ctrlr->cdev != NULL) {
+		destroy_dev(ctrlr->cdev);
+		ctrlr->cdev = NULL;
+	}
+}
diff --git a/sys/dev/ufshci/ufshci_ioctl.h b/sys/dev/ufshci/ufshci_ioctl.h
new file mode 100644
index 000000000000..57e1ce6fa869
--- /dev/null
+++ b/sys/dev/ufshci/ufshci_ioctl.h
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 2026, Samsung Electronics Co., Ltd.
+ * Written by Jaeyoon Choi
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef __UFSHCI_IOCTL_H__
+#define __UFSHCI_IOCTL_H__
+
+#include <sys/ioccom.h>
+
+#include <dev/ufshci/ufshci.h>
+
+#define UFSHCI_PT_FLAG_DATA_IN	0x01 /* device -> host */
+#define UFSHCI_PT_FLAG_DATA_OUT	0x02 /* host -> device */
+
+/*
+ * Largest data buffer a single passthrough request may carry. A transfer
+ * this long covers 256 pages of 4096 bytes, and one more when the buffer
+ * does not start on a page boundary, which is what the PRDT holds.
+ */
+#define UFSHCI_PT_MAX_XFER	(1024 * 1024)
+
+/*
+ * req_upiu and resp_upiu carry the UPIU whole, including its EHS and its
+ * data segment. A query request keeps its descriptor bytes in that data
+ * segment, so such a request leaves buf NULL and len zero.
+ *
+ * buf is the separate buffer a command UPIU moves through the PRDT. Only
+ * a command UPIU uses it.
+ */
+struct ufshci_pt_command {
+	struct ufshci_upiu req_upiu;  /* [in] */
+	struct ufshci_upiu resp_upiu; /* [out] */
+	void *buf;		      /* [in] PRDT payload, may be NULL */
+	uint32_t len;		      /* [in] length of buf */
+	uint32_t flags;		      /* [in] UFSHCI_PT_FLAG_* */
+	uint32_t timeout_ms;	      /* [in] 0 means the driver default */
+	uint32_t xfer_len;	      /* [out] bytes actually moved */
+	uint8_t ocs;		      /* [out] overall command status */
+	uint8_t reserved[7];
+};
+
+struct ufshci_pt_uic_command {
+	struct ufshci_uic_cmd cmd; /* [in/out] opcode and argument1..3 */
+	uint32_t timeout_ms;	   /* [in] */
+	uint32_t result;	   /* [out] UICCMDARG2 result code */
+};
+
+#define UFSHCI_PASSTHROUGH_CMD	_IOWR('u', 0, struct ufshci_pt_command)
+#define UFSHCI_PASSTHROUGH_UIC	_IOWR('u', 1, struct ufshci_pt_uic_command)
+
+#endif /* __UFSHCI_IOCTL_H__ */
diff --git a/sys/dev/ufshci/ufshci_private.h b/sys/dev/ufshci/ufshci_private.h
index f9c6ce2bed98..846d7a355d82 100644
--- a/sys/dev/ufshci/ufshci_private.h
+++ b/sys/dev/ufshci/ufshci_private.h
@@ -302,6 +302,7 @@ struct ufshci_device {
  */
 struct ufshci_controller {
 	device_t dev;
+	struct cdev *cdev;
 
 	uint32_t quirks;
 #define UFSHCI_QUIRK_IGNORE_UIC_POWER_MODE \
@@ -457,6 +458,11 @@ void ufshci_ctrlr_poll(struct ufshci_controller *ctrlr);
 
 int ufshci_ctrlr_submit_task_mgmt_request(struct ufshci_controller *ctrlr,
     struct ufshci_request *req);
+/* ioctl */
+int ufshci_ioctl_construct(struct ufshci_controller *ctrlr,
+    device_t dev);
+void ufshci_ioctl_destruct(struct ufshci_controller *ctrlr);
+
 int ufshci_ctrlr_submit_transfer_request(struct ufshci_controller *ctrlr,
     struct ufshci_request *req);
 int ufshci_ctrlr_send_nop(struct ufshci_controller *ctrlr);
diff --git a/sys/modules/ufshci/Makefile b/sys/modules/ufshci/Makefile
index aa0419d3a6d6..8b07766bd6fc 100644
--- a/sys/modules/ufshci/Makefile
+++ b/sys/modules/ufshci/Makefile
@@ -12,6 +12,7 @@ SRCS =	ufshci.c	\
 	ufshci_req_queue.c	\
 	ufshci_req_sdb.c	\
 	ufshci_sim.c		\
+	ufshci_ioctl.c		\
 	ufshci_sysctl.c		\
 	acpi_if.h		\
 	bus_if.h		\