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 }