aboutsummaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorJaeyoon Choi <jaeyoon@FreeBSD.org>2026-09-15 01:39:41 +0000
committerJaeyoon Choi <jaeyoon@FreeBSD.org>2026-09-15 01:44:33 +0000
commit28fefc441e3b701acc2888892a518774394255c7 (patch)
treeb73129d4bec245f0856410ab387bd3e1880ff35e /sys/dev
parentd3e5082ce4dcb154cbf50cba05d8f1dbbd55a5fc (diff)
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
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/ufshci/ufshci.h3
-rw-r--r--sys/dev/ufshci/ufshci_ctrlr.c6
-rw-r--r--sys/dev/ufshci/ufshci_ioctl.c67
-rw-r--r--sys/dev/ufshci/ufshci_ioctl.h54
-rw-r--r--sys/dev/ufshci/ufshci_private.h6
5 files changed, 136 insertions, 0 deletions
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);