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