Home | History | Annotate | Download | only in ugen
      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 }
    467