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