diff options
| author | Jaeyoon Choi <jaeyoon@FreeBSD.org> | 2026-09-15 01:39:41 +0000 |
|---|---|---|
| committer | Jaeyoon Choi <jaeyoon@FreeBSD.org> | 2026-09-15 01:44:33 +0000 |
| commit | 28fefc441e3b701acc2888892a518774394255c7 (patch) | |
| tree | b73129d4bec245f0856410ab387bd3e1880ff35e /sys/dev | |
| parent | d3e5082ce4dcb154cbf50cba05d8f1dbbd55a5fc (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.h | 3 | ||||
| -rw-r--r-- | sys/dev/ufshci/ufshci_ctrlr.c | 6 | ||||
| -rw-r--r-- | sys/dev/ufshci/ufshci_ioctl.c | 67 | ||||
| -rw-r--r-- | sys/dev/ufshci/ufshci_ioctl.h | 54 | ||||
| -rw-r--r-- | sys/dev/ufshci/ufshci_private.h | 6 |
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); |
