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 }