ugen.c revision 7492:2387323b838f
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 2008 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 */
47static void	*ugen_skel_statep;
48
49/* Prototypes declarations for the entry points */
50static int	ugen_skel_getinfo(dev_info_t *, ddi_info_cmd_t,
51						void *, void **);
52static int	ugen_skel_open(dev_t *, int, int, cred_t *);
53static int	ugen_skel_close(dev_t, int, int, cred_t *);
54static int	ugen_skel_attach(dev_info_t *, ddi_attach_cmd_t);
55static int	ugen_skel_detach(dev_info_t *, ddi_detach_cmd_t);
56static int	ugen_skel_power(dev_info_t *, int, int);
57static int	ugen_skel_read(dev_t, struct uio *, cred_t *);
58static int	ugen_skel_write(dev_t, struct uio *, cred_t *);
59static int	ugen_skel_poll(dev_t, short, int,  short *,
60						struct pollhead **);
61
62static int	ugen_skel_disconnect_ev_cb(dev_info_t *);
63static int	ugen_skel_reconnect_ev_cb(dev_info_t *);
64
65/* event support */
66static 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 */
73static 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 */
98static 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};
111
112static struct modldrv modldrv = {
113	&mod_driverops,			/* Module type */
114	"USB Generic driver",	/* Name of the module. */
115	&ugen_skel_ops,			/* driver ops */
116};
117
118static struct modlinkage modlinkage = {
119	MODREV_1,
120	(void *)&modldrv,
121	NULL
122};
123
124
125int
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
146int
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
161int
162_info(struct modinfo *modinfop)
163{
164	return (mod_info(&modlinkage, modinfop));
165}
166
167
168/*ARGSUSED*/
169static int
170ugen_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 */
208static int
209ugen_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
283fail:
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 */
300static int
301ugen_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 */
339static int
340ugen_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 */
352static int
353ugen_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 */
365static int
366ugen_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 */
385static int
386ugen_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 */
399static int
400ugen_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
414static int
415ugen_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 */
431static int
432ugen_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 */
451static int
452ugen_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}
466