1*fd71220bSRobert Mustacchi /*
2*fd71220bSRobert Mustacchi * This file and its contents are supplied under the terms of the
3*fd71220bSRobert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0.
4*fd71220bSRobert Mustacchi * You may only use this file in accordance with the terms of version
5*fd71220bSRobert Mustacchi * 1.0 of the CDDL.
6*fd71220bSRobert Mustacchi *
7*fd71220bSRobert Mustacchi * A full copy of the text of the CDDL should have accompanied this
8*fd71220bSRobert Mustacchi * source. A copy of the CDDL is also available via the Internet at
9*fd71220bSRobert Mustacchi * http://www.illumos.org/license/CDDL.
10*fd71220bSRobert Mustacchi */
11*fd71220bSRobert Mustacchi
12*fd71220bSRobert Mustacchi /*
13*fd71220bSRobert Mustacchi * Copyright 2022 Oxide Computer Company
14*fd71220bSRobert Mustacchi */
15*fd71220bSRobert Mustacchi
16*fd71220bSRobert Mustacchi /*
17*fd71220bSRobert Mustacchi * Dedicated Purpose I/O related routines
18*fd71220bSRobert Mustacchi */
19*fd71220bSRobert Mustacchi
20*fd71220bSRobert Mustacchi #include <unistd.h>
21*fd71220bSRobert Mustacchi #include <strings.h>
22*fd71220bSRobert Mustacchi #include <errno.h>
23*fd71220bSRobert Mustacchi #include <sys/types.h>
24*fd71220bSRobert Mustacchi #include <sys/stat.h>
25*fd71220bSRobert Mustacchi #include <fcntl.h>
26*fd71220bSRobert Mustacchi #include <sys/debug.h>
27*fd71220bSRobert Mustacchi
28*fd71220bSRobert Mustacchi #include "libxpio_impl.h"
29*fd71220bSRobert Mustacchi
30*fd71220bSRobert Mustacchi /*
31*fd71220bSRobert Mustacchi * To avoid translation of flags we assume the kernel flags and our library
32*fd71220bSRobert Mustacchi * flags are the same. The casts are required as that gets us the underlying
33*fd71220bSRobert Mustacchi * enum's value in a way that a static assertion can handle.
34*fd71220bSRobert Mustacchi */
35*fd71220bSRobert Mustacchi CTASSERT((uint32_t)XPIO_DPIO_F_READ == (uint32_t)KGPIO_DPIO_F_READ);
36*fd71220bSRobert Mustacchi CTASSERT((uint32_t)XPIO_DPIO_F_WRITE == (uint32_t)KGPIO_DPIO_F_WRITE);
37*fd71220bSRobert Mustacchi CTASSERT((uint32_t)XPIO_DPIO_F_KERNEL == (uint32_t)KGPIO_DPIO_F_KERNEL);
38*fd71220bSRobert Mustacchi
39*fd71220bSRobert Mustacchi /*
40*fd71220bSRobert Mustacchi * We do not have a /dev entry point for the dpinfo minor that we need to use to
41*fd71220bSRobert Mustacchi * get information about DPIOs as this is private to the implementation. We
42*fd71220bSRobert Mustacchi * therefore record it here.
43*fd71220bSRobert Mustacchi */
44*fd71220bSRobert Mustacchi static const char *xpio_dpinfo_path = "/devices/pseudo/kgpio@0:dpinfo";
45*fd71220bSRobert Mustacchi
46*fd71220bSRobert Mustacchi typedef struct {
47*fd71220bSRobert Mustacchi xpio_t *xdc_xpio;
48*fd71220bSRobert Mustacchi xpio_dpio_disc_f xdc_func;
49*fd71220bSRobert Mustacchi void *xdc_arg;
50*fd71220bSRobert Mustacchi } xpio_dpio_cb_t;
51*fd71220bSRobert Mustacchi
52*fd71220bSRobert Mustacchi static int
xpio_dpio_discover_cb(di_node_t di,di_minor_t minor,void * arg)53*fd71220bSRobert Mustacchi xpio_dpio_discover_cb(di_node_t di, di_minor_t minor, void *arg)
54*fd71220bSRobert Mustacchi {
55*fd71220bSRobert Mustacchi bool ret;
56*fd71220bSRobert Mustacchi xpio_dpio_cb_t *cb = arg;
57*fd71220bSRobert Mustacchi xpio_dpio_disc_t disc;
58*fd71220bSRobert Mustacchi
59*fd71220bSRobert Mustacchi disc.xdd_minor = minor;
60*fd71220bSRobert Mustacchi
61*fd71220bSRobert Mustacchi ret = cb->xdc_func(cb->xdc_xpio, &disc, cb->xdc_arg);
62*fd71220bSRobert Mustacchi if (ret) {
63*fd71220bSRobert Mustacchi return (DI_WALK_CONTINUE);
64*fd71220bSRobert Mustacchi } else {
65*fd71220bSRobert Mustacchi return (DI_WALK_TERMINATE);
66*fd71220bSRobert Mustacchi }
67*fd71220bSRobert Mustacchi }
68*fd71220bSRobert Mustacchi
69*fd71220bSRobert Mustacchi void
xpio_dpio_discover(xpio_t * xpio,xpio_dpio_disc_f func,void * arg)70*fd71220bSRobert Mustacchi xpio_dpio_discover(xpio_t *xpio, xpio_dpio_disc_f func, void *arg)
71*fd71220bSRobert Mustacchi {
72*fd71220bSRobert Mustacchi xpio_dpio_cb_t cb;
73*fd71220bSRobert Mustacchi
74*fd71220bSRobert Mustacchi cb.xdc_xpio = xpio;
75*fd71220bSRobert Mustacchi cb.xdc_func = func;
76*fd71220bSRobert Mustacchi cb.xdc_arg = arg;
77*fd71220bSRobert Mustacchi (void) di_walk_minor(xpio->xp_devinfo, DDI_NT_GPIO_DPIO, 0, &cb,
78*fd71220bSRobert Mustacchi xpio_dpio_discover_cb);
79*fd71220bSRobert Mustacchi }
80*fd71220bSRobert Mustacchi
81*fd71220bSRobert Mustacchi void
xpio_dpio_info_free(xpio_dpio_info_t * info)82*fd71220bSRobert Mustacchi xpio_dpio_info_free(xpio_dpio_info_t *info)
83*fd71220bSRobert Mustacchi {
84*fd71220bSRobert Mustacchi free(info);
85*fd71220bSRobert Mustacchi }
86*fd71220bSRobert Mustacchi
87*fd71220bSRobert Mustacchi const char *
xpio_dpio_info_ctrl(xpio_dpio_info_t * info)88*fd71220bSRobert Mustacchi xpio_dpio_info_ctrl(xpio_dpio_info_t *info)
89*fd71220bSRobert Mustacchi {
90*fd71220bSRobert Mustacchi return (info->xdi_ctrl);
91*fd71220bSRobert Mustacchi }
92*fd71220bSRobert Mustacchi
93*fd71220bSRobert Mustacchi const char *
xpio_dpio_info_name(xpio_dpio_info_t * info)94*fd71220bSRobert Mustacchi xpio_dpio_info_name(xpio_dpio_info_t *info)
95*fd71220bSRobert Mustacchi {
96*fd71220bSRobert Mustacchi /*
97*fd71220bSRobert Mustacchi * The raw minor name which is what we use to go to the kernel with
98*fd71220bSRobert Mustacchi * includes a 'dpio:' prefix. However, that is not what users actually
99*fd71220bSRobert Mustacchi * create and use, so strip that out of the returned name.
100*fd71220bSRobert Mustacchi */
101*fd71220bSRobert Mustacchi return (info->xdi_dpio + 5);
102*fd71220bSRobert Mustacchi }
103*fd71220bSRobert Mustacchi
104*fd71220bSRobert Mustacchi uint32_t
xpio_dpio_info_gpionum(xpio_dpio_info_t * info)105*fd71220bSRobert Mustacchi xpio_dpio_info_gpionum(xpio_dpio_info_t *info)
106*fd71220bSRobert Mustacchi {
107*fd71220bSRobert Mustacchi return (info->xdi_gpio);
108*fd71220bSRobert Mustacchi }
109*fd71220bSRobert Mustacchi
110*fd71220bSRobert Mustacchi dpio_caps_t
xpio_dpio_info_caps(xpio_dpio_info_t * info)111*fd71220bSRobert Mustacchi xpio_dpio_info_caps(xpio_dpio_info_t *info)
112*fd71220bSRobert Mustacchi {
113*fd71220bSRobert Mustacchi return (info->xdi_caps);
114*fd71220bSRobert Mustacchi }
115*fd71220bSRobert Mustacchi
116*fd71220bSRobert Mustacchi dpio_flags_t
xpio_dpio_info_flags(xpio_dpio_info_t * info)117*fd71220bSRobert Mustacchi xpio_dpio_info_flags(xpio_dpio_info_t *info)
118*fd71220bSRobert Mustacchi {
119*fd71220bSRobert Mustacchi return (info->xdi_flags);
120*fd71220bSRobert Mustacchi }
121*fd71220bSRobert Mustacchi
122*fd71220bSRobert Mustacchi bool
xpio_dpio_info(xpio_t * xpio,di_minor_t minor,xpio_dpio_info_t ** outp)123*fd71220bSRobert Mustacchi xpio_dpio_info(xpio_t *xpio, di_minor_t minor, xpio_dpio_info_t **outp)
124*fd71220bSRobert Mustacchi {
125*fd71220bSRobert Mustacchi int fd = -1;
126*fd71220bSRobert Mustacchi xpio_dpio_info_t *info = NULL;
127*fd71220bSRobert Mustacchi dpio_info_t dpi;
128*fd71220bSRobert Mustacchi
129*fd71220bSRobert Mustacchi if (minor == DI_NODE_NIL) {
130*fd71220bSRobert Mustacchi return (xpio_error(xpio, XPIO_ERR_BAD_PTR, 0, "encountered "
131*fd71220bSRobert Mustacchi "invalid di_minor_t: %p", minor));
132*fd71220bSRobert Mustacchi }
133*fd71220bSRobert Mustacchi
134*fd71220bSRobert Mustacchi if (outp == NULL) {
135*fd71220bSRobert Mustacchi return (xpio_error(xpio, XPIO_ERR_BAD_PTR, 0, "encountered "
136*fd71220bSRobert Mustacchi "invalid xpio_ctrl_t output pointer: %p", outp));
137*fd71220bSRobert Mustacchi }
138*fd71220bSRobert Mustacchi *outp = NULL;
139*fd71220bSRobert Mustacchi
140*fd71220bSRobert Mustacchi if (strcmp(di_minor_nodetype(minor), DDI_NT_GPIO_DPIO) != 0) {
141*fd71220bSRobert Mustacchi return (xpio_error(xpio, XPIO_ERR_WRONG_MINOR_TYPE, 0,
142*fd71220bSRobert Mustacchi "minor %s has incorrect node type: %s, expected %s",
143*fd71220bSRobert Mustacchi di_minor_name(minor), di_minor_nodetype(minor),
144*fd71220bSRobert Mustacchi DDI_NT_GPIO_DPIO));
145*fd71220bSRobert Mustacchi }
146*fd71220bSRobert Mustacchi
147*fd71220bSRobert Mustacchi if ((fd = open(xpio_dpinfo_path, O_RDONLY)) < 0) {
148*fd71220bSRobert Mustacchi int e = errno;
149*fd71220bSRobert Mustacchi return (xpio_error(xpio, XPIO_ERR_OPEN_DEV, e, "failed to open "
150*fd71220bSRobert Mustacchi "DPIO information minor: %s", strerror(e)));
151*fd71220bSRobert Mustacchi }
152*fd71220bSRobert Mustacchi
153*fd71220bSRobert Mustacchi info = calloc(1, sizeof (xpio_dpio_info_t));
154*fd71220bSRobert Mustacchi if (info == NULL) {
155*fd71220bSRobert Mustacchi int e = errno;
156*fd71220bSRobert Mustacchi (void) xpio_error(xpio, XPIO_ERR_NO_MEM, e, "failed to "
157*fd71220bSRobert Mustacchi "allocate memory for a xpio_dpio_info_t: %s", strerror(e));
158*fd71220bSRobert Mustacchi goto err;
159*fd71220bSRobert Mustacchi }
160*fd71220bSRobert Mustacchi
161*fd71220bSRobert Mustacchi (void) memset(&dpi, 0, sizeof (dpi));
162*fd71220bSRobert Mustacchi if (strlcpy(dpi.dpi_dpio, di_minor_name(minor),
163*fd71220bSRobert Mustacchi sizeof (dpi.dpi_dpio)) >= sizeof (dpi.dpi_dpio)) {
164*fd71220bSRobert Mustacchi (void) xpio_error(xpio, XPIO_ERR_INTERNAL, 0, "DPIO name "
165*fd71220bSRobert Mustacchi "somehow exceeded expected system length");
166*fd71220bSRobert Mustacchi goto err;
167*fd71220bSRobert Mustacchi }
168*fd71220bSRobert Mustacchi
169*fd71220bSRobert Mustacchi if (ioctl(fd, DPIO_IOC_INFO, &dpi) != 0) {
170*fd71220bSRobert Mustacchi int e = errno;
171*fd71220bSRobert Mustacchi switch (e) {
172*fd71220bSRobert Mustacchi case ENOENT:
173*fd71220bSRobert Mustacchi (void) xpio_error(xpio, XPIO_ERR_BAD_DPIO_NAME, 0,
174*fd71220bSRobert Mustacchi "DPIO %s does not exist", di_minor_name(minor));
175*fd71220bSRobert Mustacchi goto err;
176*fd71220bSRobert Mustacchi case EFAULT:
177*fd71220bSRobert Mustacchi abort();
178*fd71220bSRobert Mustacchi default:
179*fd71220bSRobert Mustacchi (void) xpio_error(xpio, XPIO_ERR_KGPIO, e, "failed to "
180*fd71220bSRobert Mustacchi "issue dpio information ioctl for dpio %s: %s",
181*fd71220bSRobert Mustacchi di_minor_name(minor), strerror(e));
182*fd71220bSRobert Mustacchi
183*fd71220bSRobert Mustacchi goto err;
184*fd71220bSRobert Mustacchi }
185*fd71220bSRobert Mustacchi }
186*fd71220bSRobert Mustacchi
187*fd71220bSRobert Mustacchi (void) memcpy(info->xdi_dpio, dpi.dpi_dpio, sizeof (dpi.dpi_dpio));
188*fd71220bSRobert Mustacchi (void) memcpy(info->xdi_ctrl, dpi.dpi_ctrl, sizeof (dpi.dpi_ctrl));
189*fd71220bSRobert Mustacchi info->xdi_gpio = dpi.dpi_gpio;
190*fd71220bSRobert Mustacchi info->xdi_caps = dpi.dpi_caps;
191*fd71220bSRobert Mustacchi info->xdi_flags = dpi.dpi_flags;
192*fd71220bSRobert Mustacchi
193*fd71220bSRobert Mustacchi (void) close(fd);
194*fd71220bSRobert Mustacchi *outp = info;
195*fd71220bSRobert Mustacchi return (xpio_success(xpio));
196*fd71220bSRobert Mustacchi
197*fd71220bSRobert Mustacchi err:
198*fd71220bSRobert Mustacchi if (fd >= 0) {
199*fd71220bSRobert Mustacchi (void) close(fd);
200*fd71220bSRobert Mustacchi }
201*fd71220bSRobert Mustacchi free(info);
202*fd71220bSRobert Mustacchi return (false);
203*fd71220bSRobert Mustacchi }
204*fd71220bSRobert Mustacchi
205*fd71220bSRobert Mustacchi bool
xpio_dpio_create(xpio_ctrl_t * ctrl,xpio_gpio_info_t * gi,const char * name,xpio_dpio_features_t feat)206*fd71220bSRobert Mustacchi xpio_dpio_create(xpio_ctrl_t *ctrl, xpio_gpio_info_t *gi, const char *name,
207*fd71220bSRobert Mustacchi xpio_dpio_features_t feat)
208*fd71220bSRobert Mustacchi {
209*fd71220bSRobert Mustacchi kgpio_dpio_create_t create;
210*fd71220bSRobert Mustacchi xpio_t *xpio = ctrl->xc_xpio;
211*fd71220bSRobert Mustacchi const uint32_t all_feats = XPIO_DPIO_F_READ | XPIO_DPIO_F_WRITE |
212*fd71220bSRobert Mustacchi XPIO_DPIO_F_KERNEL;
213*fd71220bSRobert Mustacchi
214*fd71220bSRobert Mustacchi if (gi == NULL) {
215*fd71220bSRobert Mustacchi return (xpio_error(xpio, XPIO_ERR_BAD_PTR, 0, "encountered "
216*fd71220bSRobert Mustacchi "invalid xpio_gpio_info_t pointer: %p", gi));
217*fd71220bSRobert Mustacchi }
218*fd71220bSRobert Mustacchi
219*fd71220bSRobert Mustacchi if (name == NULL) {
220*fd71220bSRobert Mustacchi return (xpio_error(xpio, XPIO_ERR_BAD_PTR, 0, "encountered "
221*fd71220bSRobert Mustacchi "invalid pointer for DPIO name: %p", name));
222*fd71220bSRobert Mustacchi }
223*fd71220bSRobert Mustacchi
224*fd71220bSRobert Mustacchi if ((feat & ~all_feats) != 0) {
225*fd71220bSRobert Mustacchi return (xpio_error(xpio, XPIO_ERR_BAD_DPIO_FEAT, 0, "found "
226*fd71220bSRobert Mustacchi "unknown dpio features specified: 0x%x",
227*fd71220bSRobert Mustacchi feat & ~all_feats));
228*fd71220bSRobert Mustacchi }
229*fd71220bSRobert Mustacchi
230*fd71220bSRobert Mustacchi (void) memset(&create, 0, sizeof (create));
231*fd71220bSRobert Mustacchi if (strlcpy(create.kdc_name, name, sizeof (create.kdc_name)) >=
232*fd71220bSRobert Mustacchi sizeof (create.kdc_name)) {
233*fd71220bSRobert Mustacchi return (xpio_error(xpio, XPIO_ERR_BAD_DPIO_NAME, 0,
234*fd71220bSRobert Mustacchi "requested DPIO name is longer than the maximum supported "
235*fd71220bSRobert Mustacchi "(%zu characters including '\\0')",
236*fd71220bSRobert Mustacchi sizeof (create.kdc_name)));
237*fd71220bSRobert Mustacchi }
238*fd71220bSRobert Mustacchi
239*fd71220bSRobert Mustacchi /*
240*fd71220bSRobert Mustacchi * Right now there is a 1:1 mapping between library and kgpio features.
241*fd71220bSRobert Mustacchi */
242*fd71220bSRobert Mustacchi create.kdc_flags = (kgpio_dpio_flags_t)feat;
243*fd71220bSRobert Mustacchi create.kdc_id = gi->xgi_id;
244*fd71220bSRobert Mustacchi
245*fd71220bSRobert Mustacchi /*
246*fd71220bSRobert Mustacchi * At some point it'd be good for us to take this apart and create much
247*fd71220bSRobert Mustacchi * more useful semantic errors rather than this generic error as it
248*fd71220bSRobert Mustacchi * basically requires someone to go into the code to figure out what
249*fd71220bSRobert Mustacchi * happened. Basically, we want something like with update.
250*fd71220bSRobert Mustacchi */
251*fd71220bSRobert Mustacchi if (ioctl(ctrl->xc_fd, KGPIO_IOC_DPIO_CREATE, &create) != 0) {
252*fd71220bSRobert Mustacchi int e = errno;
253*fd71220bSRobert Mustacchi return (xpio_error(xpio, XPIO_ERR_KGPIO, e, "failed to create "
254*fd71220bSRobert Mustacchi "dpio %s: %s", name, strerror(e)));
255*fd71220bSRobert Mustacchi }
256*fd71220bSRobert Mustacchi
257*fd71220bSRobert Mustacchi return (xpio_success(xpio));
258*fd71220bSRobert Mustacchi }
259*fd71220bSRobert Mustacchi
260*fd71220bSRobert Mustacchi bool
xpio_dpio_destroy(xpio_ctrl_t * ctrl,xpio_gpio_info_t * gi)261*fd71220bSRobert Mustacchi xpio_dpio_destroy(xpio_ctrl_t *ctrl, xpio_gpio_info_t *gi)
262*fd71220bSRobert Mustacchi {
263*fd71220bSRobert Mustacchi kgpio_dpio_destroy_t destroy;
264*fd71220bSRobert Mustacchi xpio_t *xpio = ctrl->xc_xpio;
265*fd71220bSRobert Mustacchi
266*fd71220bSRobert Mustacchi if (gi == NULL) {
267*fd71220bSRobert Mustacchi return (xpio_error(xpio, XPIO_ERR_BAD_PTR, 0, "encountered "
268*fd71220bSRobert Mustacchi "invalid xpio_gpio_info_t pointer: %p", gi));
269*fd71220bSRobert Mustacchi }
270*fd71220bSRobert Mustacchi
271*fd71220bSRobert Mustacchi destroy.kdd_id = gi->xgi_id;
272*fd71220bSRobert Mustacchi
273*fd71220bSRobert Mustacchi
274*fd71220bSRobert Mustacchi /*
275*fd71220bSRobert Mustacchi * At some point it'd be good for us to take this apart and create much
276*fd71220bSRobert Mustacchi * more useful semantic errors rather than this generic error as it
277*fd71220bSRobert Mustacchi * basically requires someone to go into the code to figure out what
278*fd71220bSRobert Mustacchi * happened. Basically, we want something like with update.
279*fd71220bSRobert Mustacchi */
280*fd71220bSRobert Mustacchi if (ioctl(ctrl->xc_fd, KGPIO_IOC_DPIO_DESTROY, &destroy) != 0) {
281*fd71220bSRobert Mustacchi int e = errno;
282*fd71220bSRobert Mustacchi return (xpio_error(xpio, XPIO_ERR_KGPIO, e, "failed to destroy "
283*fd71220bSRobert Mustacchi "dpio %s/%u: %s", ctrl->xc_name, gi->xgi_id, strerror(e)));
284*fd71220bSRobert Mustacchi }
285*fd71220bSRobert Mustacchi return (xpio_success(xpio));
286*fd71220bSRobert Mustacchi }
287