1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 * 21 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 22 * Use is subject to license terms. 23 * Copyright (c) 2016 by Delphix. All rights reserved. 24 */ 25 26 27 /* 28 * UGEN: USB Generic Driver 29 * 30 * The "Universal Generic Driver" (UGEN) for USB devices provides interfaces 31 * to talk to USB devices. This is very useful for Point of Sale sale 32 * devices and other simple devices like USB scanner, USB palm pilot. 33 * The UGEN provides a system call interface to USB devices enabling 34 * a USB device vendor to write an application for their 35 * device instead of writing a driver. This facilitates the vendor to write 36 * device management s/w quickly in userland. 37 * 38 * UGEN supports read/write/poll entry points. An application can be written 39 * using read/write/aioread/aiowrite/poll system calls to communicate 40 * with the device. 41 */ 42 #include <sys/usb/usba/usbai_version.h> 43 #include <sys/usb/usba.h> 44 #include <sys/usb/usba/usba_ugen.h> 45 #include <sys/usb/clients/ugen/ugend.h> 46 47 /* Global variables */ 48 static void *ugen_skel_statep; 49 50 /* Prototypes declarations for the entry points */ 51 static int ugen_skel_getinfo(dev_info_t *, ddi_info_cmd_t, 52 void *, void **); 53 static int ugen_skel_open(dev_t *, int, int, cred_t *); 54 static int ugen_skel_close(dev_t, int, int, cred_t *); 55 static int ugen_skel_attach(dev_info_t *, ddi_attach_cmd_t); 56 static int ugen_skel_detach(dev_info_t *, ddi_detach_cmd_t); 57 static int ugen_skel_power(dev_info_t *, int, int); 58 static int ugen_skel_read(dev_t, struct uio *, cred_t *); 59 static int ugen_skel_write(dev_t, struct uio *, cred_t *); 60 static int ugen_skel_poll(dev_t, short, int, short *, 61 struct pollhead **); 62 63 static int ugen_skel_disconnect_ev_cb(dev_info_t *); 64 static int ugen_skel_reconnect_ev_cb(dev_info_t *); 65 66 /* event support */ 67 static usb_event_t ugen_skel_events = { 68 ugen_skel_disconnect_ev_cb, 69 ugen_skel_reconnect_ev_cb, 70 NULL, NULL 71 }; 72 73 /* Driver cb_ops structure */ 74 static struct cb_ops ugen_skel_cb_ops = { 75 ugen_skel_open, /* open */ 76 ugen_skel_close, /* close */ 77 nodev, /* strategy */ 78 nodev, /* print */ 79 nodev, /* dump */ 80 ugen_skel_read, /* read */ 81 ugen_skel_write, /* write */ 82 nodev, /* ioctl */ 83 nodev, /* devmap */ 84 nodev, /* mmap */ 85 nodev, /* segmap */ 86 ugen_skel_poll, /* poll */ 87 ddi_prop_op, /* cb_prop_op */ 88 0, /* streamtab */ 89 D_MP, /* Driver compatibility flag */ 90 CB_REV, /* revision */ 91 nodev, /* aread */ 92 nodev /* awrite */ 93 }; 94 95 /* 96 * Modloading support 97 * driver dev_ops structure 98 */ 99 static struct dev_ops ugen_skel_ops = { 100 DEVO_REV, /* devo_rev, */ 101 0, /* refct */ 102 ugen_skel_getinfo, /* info */ 103 nulldev, /* indetify */ 104 nulldev, /* probe */ 105 ugen_skel_attach, /* attach */ 106 ugen_skel_detach, /* detach */ 107 nodev, /* reset */ 108 &ugen_skel_cb_ops, /* driver operations */ 109 NULL, /* bus operations */ 110 ugen_skel_power, /* power */ 111 ddi_quiesce_not_needed, /* devo_quiesce */ 112 }; 113 114 static struct modldrv modldrv = { 115 &mod_driverops, /* Module type */ 116 "USB Generic driver", /* Name of the module. */ 117 &ugen_skel_ops, /* driver ops */ 118 }; 119 120 static struct modlinkage modlinkage = { 121 MODREV_1, 122 (void *)&modldrv, 123 NULL 124 }; 125 126 127 int 128 _init() 129 { 130 int rval; 131 132 if ((rval = ddi_soft_state_init(&ugen_skel_statep, 133 sizeof (ugen_skel_state_t), UGEN_INSTANCES)) != 0) { 134 135 return (rval); 136 } 137 138 if ((rval = mod_install(&modlinkage)) != 0) { 139 ddi_soft_state_fini(&ugen_skel_statep); 140 141 return (rval); 142 } 143 144 return (rval); 145 } 146 147 148 int 149 _fini() 150 { 151 int rval; 152 153 if ((rval = mod_remove(&modlinkage)) != 0) { 154 155 return (rval); 156 } 157 ddi_soft_state_fini(&ugen_skel_statep); 158 159 return (rval); 160 } 161 162 163 int 164 _info(struct modinfo *modinfop) 165 { 166 return (mod_info(&modlinkage, modinfop)); 167 } 168 169 170 /*ARGSUSED*/ 171 static int 172 ugen_skel_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 173 void **result) 174 { 175 int rval = DDI_FAILURE; 176 int instance = 177 UGEN_MINOR_TO_INSTANCE(getminor((dev_t)arg)); 178 ugen_skel_state_t *ugen_skelp; 179 180 switch (infocmd) { 181 case DDI_INFO_DEVT2DEVINFO: 182 ugen_skelp = ddi_get_soft_state(ugen_skel_statep, instance); 183 if (ugen_skelp != NULL) { 184 *result = ugen_skelp->ugen_skel_dip; 185 if (*result != NULL) { 186 rval = DDI_SUCCESS; 187 } 188 } else { 189 *result = NULL; 190 } 191 192 break; 193 case DDI_INFO_DEVT2INSTANCE: 194 *result = (void *)(uintptr_t)instance; 195 rval = DDI_SUCCESS; 196 197 break; 198 default: 199 200 break; 201 } 202 203 return (rval); 204 } 205 206 207 /* 208 * ugen_skel_attach() 209 */ 210 static int 211 ugen_skel_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 212 { 213 ugen_skel_state_t *ugen_skelp; 214 int instance; /* Driver instance number */ 215 int rval; 216 usb_ugen_info_t usb_ugen_info; 217 218 /* Get instance number */ 219 instance = ddi_get_instance(dip); 220 221 switch (cmd) { 222 case DDI_ATTACH: 223 224 break; 225 case DDI_RESUME: 226 ugen_skelp = ddi_get_soft_state(ugen_skel_statep, instance); 227 if (ugen_skelp == NULL) { 228 229 return (DDI_FAILURE); 230 } 231 232 rval = usb_ugen_attach(ugen_skelp->ugen_skel_hdl, cmd); 233 234 return (rval == USB_SUCCESS ? DDI_SUCCESS : DDI_FAILURE); 235 default: 236 237 return (DDI_FAILURE); 238 } 239 240 if (ddi_soft_state_zalloc(ugen_skel_statep, instance) == 241 DDI_SUCCESS) { 242 ugen_skelp = ddi_get_soft_state(ugen_skel_statep, 243 instance); 244 } 245 if (ugen_skelp == NULL) { 246 247 return (DDI_FAILURE); 248 } 249 250 if ((rval = usb_client_attach(dip, USBDRV_VERSION, 0)) != 251 USB_SUCCESS) { 252 253 goto fail; 254 } 255 256 ugen_skelp->ugen_skel_dip = dip; 257 ugen_skelp->ugen_skel_instance = instance; 258 259 /* get a ugen handle */ 260 bzero(&usb_ugen_info, sizeof (usb_ugen_info)); 261 usb_ugen_info.usb_ugen_flags = 262 USB_UGEN_ENABLE_PM | USB_UGEN_REMOVE_CHILDREN; 263 usb_ugen_info.usb_ugen_minor_node_ugen_bits_mask = 264 (dev_t)UGEN_MINOR_UGEN_BITS_MASK; 265 usb_ugen_info.usb_ugen_minor_node_instance_mask = 266 (dev_t)~UGEN_MINOR_UGEN_BITS_MASK; 267 ugen_skelp->ugen_skel_hdl = usb_ugen_get_hdl(dip, 268 &usb_ugen_info); 269 270 if (usb_ugen_attach(ugen_skelp->ugen_skel_hdl, cmd) != USB_SUCCESS) { 271 272 goto fail; 273 } 274 275 /* register for hotplug events */ 276 if (usb_register_event_cbs(dip, &ugen_skel_events, 0) != USB_SUCCESS) { 277 278 goto fail; 279 } 280 281 ddi_report_dev(dip); 282 283 return (DDI_SUCCESS); 284 285 fail: 286 if (ugen_skelp) { 287 usb_unregister_event_cbs(dip, &ugen_skel_events); 288 usb_ugen_release_hdl(ugen_skelp-> 289 ugen_skel_hdl); 290 ddi_soft_state_free(ugen_skel_statep, 291 ugen_skelp->ugen_skel_instance); 292 usb_client_detach(dip, NULL); 293 } 294 295 return (DDI_FAILURE); 296 } 297 298 299 /* 300 * ugen_skel_detach() 301 */ 302 static int 303 ugen_skel_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 304 { 305 int rval = USB_FAILURE; 306 ugen_skel_state_t *ugen_skelp = ddi_get_soft_state(ugen_skel_statep, 307 ddi_get_instance(dip)); 308 309 if (ugen_skelp) { 310 switch (cmd) { 311 case DDI_DETACH: 312 rval = usb_ugen_detach(ugen_skelp->ugen_skel_hdl, cmd); 313 if (rval == USB_SUCCESS) { 314 usb_unregister_event_cbs(dip, 315 &ugen_skel_events); 316 usb_ugen_release_hdl(ugen_skelp-> 317 ugen_skel_hdl); 318 ddi_soft_state_free(ugen_skel_statep, 319 ugen_skelp->ugen_skel_instance); 320 usb_client_detach(dip, NULL); 321 } 322 323 break; 324 case DDI_SUSPEND: 325 rval = usb_ugen_detach(ugen_skelp->ugen_skel_hdl, cmd); 326 327 break; 328 default: 329 330 break; 331 } 332 } 333 334 return (rval == USB_SUCCESS ? DDI_SUCCESS : DDI_FAILURE); 335 } 336 337 338 /* 339 * ugen_skel_disconnect_ev_cb: 340 */ 341 static int 342 ugen_skel_disconnect_ev_cb(dev_info_t *dip) 343 { 344 ugen_skel_state_t *ugen_skelp = ddi_get_soft_state(ugen_skel_statep, 345 ddi_get_instance(dip)); 346 347 return (usb_ugen_disconnect_ev_cb(ugen_skelp->ugen_skel_hdl)); 348 } 349 350 351 /* 352 * ugen_skel_reconnect_ev_cb: 353 */ 354 static int 355 ugen_skel_reconnect_ev_cb(dev_info_t *dip) 356 { 357 ugen_skel_state_t *ugen_skelp = ddi_get_soft_state(ugen_skel_statep, 358 ddi_get_instance(dip)); 359 360 return (usb_ugen_reconnect_ev_cb(ugen_skelp->ugen_skel_hdl)); 361 } 362 363 364 /* 365 * ugen_skel_open: 366 */ 367 static int 368 ugen_skel_open(dev_t *devp, int flag, int sflag, cred_t *cr) 369 { 370 ugen_skel_state_t *ugen_skelp; 371 372 if ((ugen_skelp = ddi_get_soft_state(ugen_skel_statep, 373 UGEN_MINOR_TO_INSTANCE(getminor(*devp)))) == NULL) { 374 /* deferred detach */ 375 376 return (ENXIO); 377 } 378 379 return (usb_ugen_open(ugen_skelp->ugen_skel_hdl, devp, flag, 380 sflag, cr)); 381 } 382 383 384 /* 385 * ugen_skel_close() 386 */ 387 static int 388 ugen_skel_close(dev_t dev, int flag, int otype, cred_t *cr) 389 { 390 ugen_skel_state_t *ugen_skelp = ddi_get_soft_state(ugen_skel_statep, 391 UGEN_MINOR_TO_INSTANCE(getminor(dev))); 392 393 return (usb_ugen_close(ugen_skelp->ugen_skel_hdl, dev, flag, 394 otype, cr)); 395 } 396 397 398 /* 399 * ugen_skel_read/write() 400 */ 401 static int 402 ugen_skel_read(dev_t dev, struct uio *uiop, cred_t *credp) 403 { 404 ugen_skel_state_t *ugen_skelp = ddi_get_soft_state(ugen_skel_statep, 405 UGEN_MINOR_TO_INSTANCE(getminor(dev))); 406 if (ugen_skelp == NULL) { 407 408 return (ENXIO); 409 } 410 411 return (usb_ugen_read(ugen_skelp->ugen_skel_hdl, dev, 412 uiop, credp)); 413 } 414 415 416 static int 417 ugen_skel_write(dev_t dev, struct uio *uiop, cred_t *credp) 418 { 419 ugen_skel_state_t *ugen_skelp = ddi_get_soft_state(ugen_skel_statep, 420 UGEN_MINOR_TO_INSTANCE(getminor(dev))); 421 if (ugen_skelp == NULL) { 422 423 return (ENXIO); 424 } 425 return (usb_ugen_write(ugen_skelp->ugen_skel_hdl, 426 dev, uiop, credp)); 427 } 428 429 430 /* 431 * ugen_skel_poll 432 */ 433 static int 434 ugen_skel_poll(dev_t dev, short events, 435 int anyyet, short *reventsp, struct pollhead **phpp) 436 { 437 ugen_skel_state_t *ugen_skelp = ddi_get_soft_state(ugen_skel_statep, 438 UGEN_MINOR_TO_INSTANCE(getminor(dev))); 439 if (ugen_skelp == NULL) { 440 441 return (ENXIO); 442 } 443 444 return (usb_ugen_poll(ugen_skelp->ugen_skel_hdl, dev, events, 445 anyyet, reventsp, phpp)); 446 } 447 448 449 /* 450 * ugen_skel_power: 451 * PM entry point 452 */ 453 static int 454 ugen_skel_power(dev_info_t *dip, int comp, int level) 455 { 456 int rval; 457 458 ugen_skel_state_t *ugen_skelp = ddi_get_soft_state(ugen_skel_statep, 459 ddi_get_instance(dip)); 460 if (ugen_skelp == NULL) { 461 462 return (DDI_FAILURE); 463 } 464 rval = usb_ugen_power(ugen_skelp->ugen_skel_hdl, comp, level); 465 466 return (rval == USB_SUCCESS ? DDI_SUCCESS : DDI_FAILURE); 467 } 468