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 */
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_supported,	/* 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