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, NULL } 122 }; 123 124 125 int 126 _init() 127 { 128 int rval; 129 130 if ((rval = ddi_soft_state_init(&ugen_skel_statep, 131 sizeof (ugen_skel_state_t), UGEN_INSTANCES)) != 0) { 132 133 return (rval); 134 } 135 136 if ((rval = mod_install(&modlinkage)) != 0) { 137 ddi_soft_state_fini(&ugen_skel_statep); 138 139 return (rval); 140 } 141 142 return (rval); 143 } 144 145 146 int 147 _fini() 148 { 149 int rval; 150 151 if ((rval = mod_remove(&modlinkage)) != 0) { 152 153 return (rval); 154 } 155 ddi_soft_state_fini(&ugen_skel_statep); 156 157 return (rval); 158 } 159 160 161 int 162 _info(struct modinfo *modinfop) 163 { 164 return (mod_info(&modlinkage, modinfop)); 165 } 166 167 168 /*ARGSUSED*/ 169 static int 170 ugen_skel_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 171 void **result) 172 { 173 int rval = DDI_FAILURE; 174 int instance = 175 UGEN_MINOR_TO_INSTANCE(getminor((dev_t)arg)); 176 ugen_skel_state_t *ugen_skelp; 177 178 switch (infocmd) { 179 case DDI_INFO_DEVT2DEVINFO: 180 ugen_skelp = ddi_get_soft_state(ugen_skel_statep, instance); 181 if (ugen_skelp != NULL) { 182 *result = ugen_skelp->ugen_skel_dip; 183 if (*result != NULL) { 184 rval = DDI_SUCCESS; 185 } 186 } else { 187 *result = NULL; 188 } 189 190 break; 191 case DDI_INFO_DEVT2INSTANCE: 192 *result = (void *)(uintptr_t)instance; 193 rval = DDI_SUCCESS; 194 195 break; 196 default: 197 198 break; 199 } 200 201 return (rval); 202 } 203 204 205 /* 206 * ugen_skel_attach() 207 */ 208 static int 209 ugen_skel_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 210 { 211 ugen_skel_state_t *ugen_skelp; 212 int instance; /* Driver instance number */ 213 int rval; 214 usb_ugen_info_t usb_ugen_info; 215 216 /* Get instance number */ 217 instance = ddi_get_instance(dip); 218 219 switch (cmd) { 220 case DDI_ATTACH: 221 222 break; 223 case DDI_RESUME: 224 ugen_skelp = ddi_get_soft_state(ugen_skel_statep, instance); 225 if (ugen_skelp == NULL) { 226 227 return (DDI_FAILURE); 228 } 229 230 rval = usb_ugen_attach(ugen_skelp->ugen_skel_hdl, cmd); 231 232 return (rval == USB_SUCCESS ? DDI_SUCCESS : DDI_FAILURE); 233 default: 234 235 return (DDI_FAILURE); 236 } 237 238 if (ddi_soft_state_zalloc(ugen_skel_statep, instance) == 239 DDI_SUCCESS) { 240 ugen_skelp = ddi_get_soft_state(ugen_skel_statep, 241 instance); 242 } 243 if (ugen_skelp == NULL) { 244 245 return (DDI_FAILURE); 246 } 247 248 if ((rval = usb_client_attach(dip, USBDRV_VERSION, 0)) != 249 USB_SUCCESS) { 250 251 goto fail; 252 } 253 254 ugen_skelp->ugen_skel_dip = dip; 255 ugen_skelp->ugen_skel_instance = instance; 256 257 /* get a ugen handle */ 258 bzero(&usb_ugen_info, sizeof (usb_ugen_info)); 259 usb_ugen_info.usb_ugen_flags = 260 USB_UGEN_ENABLE_PM | USB_UGEN_REMOVE_CHILDREN; 261 usb_ugen_info.usb_ugen_minor_node_ugen_bits_mask = 262 (dev_t)UGEN_MINOR_UGEN_BITS_MASK; 263 usb_ugen_info.usb_ugen_minor_node_instance_mask = 264 (dev_t)~UGEN_MINOR_UGEN_BITS_MASK; 265 ugen_skelp->ugen_skel_hdl = usb_ugen_get_hdl(dip, 266 &usb_ugen_info); 267 268 if (usb_ugen_attach(ugen_skelp->ugen_skel_hdl, cmd) != USB_SUCCESS) { 269 270 goto fail; 271 } 272 273 /* register for hotplug events */ 274 if (usb_register_event_cbs(dip, &ugen_skel_events, 0) != USB_SUCCESS) { 275 276 goto fail; 277 } 278 279 ddi_report_dev(dip); 280 281 return (DDI_SUCCESS); 282 283 fail: 284 if (ugen_skelp) { 285 usb_unregister_event_cbs(dip, &ugen_skel_events); 286 usb_ugen_release_hdl(ugen_skelp-> 287 ugen_skel_hdl); 288 ddi_soft_state_free(ugen_skel_statep, 289 ugen_skelp->ugen_skel_instance); 290 usb_client_detach(dip, NULL); 291 } 292 293 return (DDI_FAILURE); 294 } 295 296 297 /* 298 * ugen_skel_detach() 299 */ 300 static int 301 ugen_skel_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 302 { 303 int rval = USB_FAILURE; 304 ugen_skel_state_t *ugen_skelp = ddi_get_soft_state(ugen_skel_statep, 305 ddi_get_instance(dip)); 306 307 if (ugen_skelp) { 308 switch (cmd) { 309 case DDI_DETACH: 310 rval = usb_ugen_detach(ugen_skelp->ugen_skel_hdl, cmd); 311 if (rval == USB_SUCCESS) { 312 usb_unregister_event_cbs(dip, 313 &ugen_skel_events); 314 usb_ugen_release_hdl(ugen_skelp-> 315 ugen_skel_hdl); 316 ddi_soft_state_free(ugen_skel_statep, 317 ugen_skelp->ugen_skel_instance); 318 usb_client_detach(dip, NULL); 319 } 320 321 break; 322 case DDI_SUSPEND: 323 rval = usb_ugen_detach(ugen_skelp->ugen_skel_hdl, cmd); 324 325 break; 326 default: 327 328 break; 329 } 330 } 331 332 return (rval == USB_SUCCESS ? DDI_SUCCESS : DDI_FAILURE); 333 } 334 335 336 /* 337 * ugen_skel_disconnect_ev_cb: 338 */ 339 static int 340 ugen_skel_disconnect_ev_cb(dev_info_t *dip) 341 { 342 ugen_skel_state_t *ugen_skelp = ddi_get_soft_state(ugen_skel_statep, 343 ddi_get_instance(dip)); 344 345 return (usb_ugen_disconnect_ev_cb(ugen_skelp->ugen_skel_hdl)); 346 } 347 348 349 /* 350 * ugen_skel_reconnect_ev_cb: 351 */ 352 static int 353 ugen_skel_reconnect_ev_cb(dev_info_t *dip) 354 { 355 ugen_skel_state_t *ugen_skelp = ddi_get_soft_state(ugen_skel_statep, 356 ddi_get_instance(dip)); 357 358 return (usb_ugen_reconnect_ev_cb(ugen_skelp->ugen_skel_hdl)); 359 } 360 361 362 /* 363 * ugen_skel_open: 364 */ 365 static int 366 ugen_skel_open(dev_t *devp, int flag, int sflag, cred_t *cr) 367 { 368 ugen_skel_state_t *ugen_skelp; 369 370 if ((ugen_skelp = ddi_get_soft_state(ugen_skel_statep, 371 UGEN_MINOR_TO_INSTANCE(getminor(*devp)))) == NULL) { 372 /* deferred detach */ 373 374 return (ENXIO); 375 } 376 377 return (usb_ugen_open(ugen_skelp->ugen_skel_hdl, devp, flag, 378 sflag, cr)); 379 } 380 381 382 /* 383 * ugen_skel_close() 384 */ 385 static int 386 ugen_skel_close(dev_t dev, int flag, int otype, cred_t *cr) 387 { 388 ugen_skel_state_t *ugen_skelp = ddi_get_soft_state(ugen_skel_statep, 389 UGEN_MINOR_TO_INSTANCE(getminor(dev))); 390 391 return (usb_ugen_close(ugen_skelp->ugen_skel_hdl, dev, flag, 392 otype, cr)); 393 } 394 395 396 /* 397 * ugen_skel_read/write() 398 */ 399 static int 400 ugen_skel_read(dev_t dev, struct uio *uiop, cred_t *credp) 401 { 402 ugen_skel_state_t *ugen_skelp = ddi_get_soft_state(ugen_skel_statep, 403 UGEN_MINOR_TO_INSTANCE(getminor(dev))); 404 if (ugen_skelp == NULL) { 405 406 return (ENXIO); 407 } 408 409 return (usb_ugen_read(ugen_skelp->ugen_skel_hdl, dev, 410 uiop, credp)); 411 } 412 413 414 static int 415 ugen_skel_write(dev_t dev, struct uio *uiop, cred_t *credp) 416 { 417 ugen_skel_state_t *ugen_skelp = ddi_get_soft_state(ugen_skel_statep, 418 UGEN_MINOR_TO_INSTANCE(getminor(dev))); 419 if (ugen_skelp == NULL) { 420 421 return (ENXIO); 422 } 423 return (usb_ugen_write(ugen_skelp->ugen_skel_hdl, 424 dev, uiop, credp)); 425 } 426 427 428 /* 429 * ugen_skel_poll 430 */ 431 static int 432 ugen_skel_poll(dev_t dev, short events, 433 int anyyet, short *reventsp, struct pollhead **phpp) 434 { 435 ugen_skel_state_t *ugen_skelp = ddi_get_soft_state(ugen_skel_statep, 436 UGEN_MINOR_TO_INSTANCE(getminor(dev))); 437 if (ugen_skelp == NULL) { 438 439 return (ENXIO); 440 } 441 442 return (usb_ugen_poll(ugen_skelp->ugen_skel_hdl, dev, events, 443 anyyet, reventsp, phpp)); 444 } 445 446 447 /* 448 * ugen_skel_power: 449 * PM entry point 450 */ 451 static int 452 ugen_skel_power(dev_info_t *dip, int comp, int level) 453 { 454 int rval; 455 456 ugen_skel_state_t *ugen_skelp = ddi_get_soft_state(ugen_skel_statep, 457 ddi_get_instance(dip)); 458 if (ugen_skelp == NULL) { 459 460 return (DDI_FAILURE); 461 } 462 rval = usb_ugen_power(ugen_skelp->ugen_skel_hdl, comp, level); 463 464 return (rval == USB_SUCCESS ? DDI_SUCCESS : DDI_FAILURE); 465 }