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