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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  *
22  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * UGEN: USB Generic Driver
30  *
31  * The "Universal Generic Driver"  (UGEN) for USB devices provides interfaces
32  * to  talk to	USB  devices.  This is	very  useful for  Point of Sale sale
33  * devices and other simple  devices like  USB	scanner, USB palm  pilot.
34  * The UGEN provides a system call interface to USB  devices  enabling
35  * a USB device vendor to  write an  application for his
36  * device instead of  writing a driver. This facilitates the vendor to write
37  * device management s/w quickly in userland.
38  *
39  * UGEN supports read/write/poll entry points. An application can be written
40  * using  read/write/aioread/aiowrite/poll  system calls to communicate
41  * with the device.
42  */
43 #include <sys/usb/usba/usbai_version.h>
44 #include <sys/usb/usba.h>
45 #include <sys/usb/usba/usba_ugen.h>
46 #include <sys/usb/clients/ugen/ugend.h>
47 
48 /* Global variables */
49 static void	*ugen_skel_statep;
50 
51 /* Prototypes declarations for the entry points */
52 static int	ugen_skel_getinfo(dev_info_t *, ddi_info_cmd_t,
53 						void *, void **);
54 static int	ugen_skel_open(dev_t *, int, int, cred_t *);
55 static int	ugen_skel_close(dev_t, int, int, cred_t *);
56 static int	ugen_skel_attach(dev_info_t *, ddi_attach_cmd_t);
57 static int	ugen_skel_detach(dev_info_t *, ddi_detach_cmd_t);
58 static int	ugen_skel_power(dev_info_t *, int, int);
59 static int	ugen_skel_strategy(struct buf *);
60 static int	ugen_skel_read(dev_t, struct uio *, cred_t *);
61 static int	ugen_skel_write(dev_t, struct uio *, cred_t *);
62 static int	ugen_skel_poll(dev_t, short, int,  short *,
63 						struct pollhead **);
64 
65 static int	ugen_skel_disconnect_ev_cb(dev_info_t *);
66 static int	ugen_skel_reconnect_ev_cb(dev_info_t *);
67 
68 /* event support */
69 static usb_event_t ugen_skel_events = {
70 	ugen_skel_disconnect_ev_cb,
71 	ugen_skel_reconnect_ev_cb,
72 	NULL, NULL
73 };
74 
75 /* Driver cb_ops structure */
76 static struct cb_ops ugen_skel_cb_ops = {
77 	ugen_skel_open,			/* open */
78 	ugen_skel_close,		/* close */
79 	nodev,				/* strategy */
80 	nodev,				/* print */
81 	nodev,				/* dump */
82 	ugen_skel_read,			/* read */
83 	ugen_skel_write,		/* write */
84 	nodev,				/* ioctl */
85 	nodev,				/* devmap */
86 	nodev,				/* mmap */
87 	nodev,				/* segmap */
88 	ugen_skel_poll,			/* poll */
89 	ddi_prop_op,			/* cb_prop_op */
90 	0,				/* streamtab  */
91 	D_MP,				/* Driver compatibility flag */
92 	CB_REV,				/* revision */
93 	nodev,				/* aread */
94 	nodev				/* awrite */
95 };
96 
97 /*
98  * Modloading support
99  *	driver dev_ops structure
100  */
101 static struct dev_ops ugen_skel_ops = {
102 	DEVO_REV,			/* devo_rev, */
103 	0,				/* refct  */
104 	ugen_skel_getinfo,		/* info */
105 	nulldev,			/* indetify */
106 	nulldev,			/* probe */
107 	ugen_skel_attach,		/* attach */
108 	ugen_skel_detach,		/* detach */
109 	nodev,				/* reset */
110 	&ugen_skel_cb_ops,		/* driver operations */
111 	NULL,				/* bus operations */
112 	ugen_skel_power			/* power */
113 };
114 
115 static struct modldrv modldrv = {
116 	&mod_driverops,			/* Module type */
117 	"USB Generic driver %I%",	/* Name of the module. */
118 	&ugen_skel_ops,			/* driver ops */
119 };
120 
121 static struct modlinkage modlinkage = {
122 	MODREV_1,
123 	(void *)&modldrv,
124 	NULL
125 };
126 
127 
128 int
129 _init()
130 {
131 	int	rval;
132 
133 	if ((rval = ddi_soft_state_init(&ugen_skel_statep,
134 		sizeof (ugen_skel_state_t), UGEN_INSTANCES)) != 0) {
135 
136 		return (rval);
137 	}
138 
139 	if ((rval = mod_install(&modlinkage)) != 0) {
140 		ddi_soft_state_fini(&ugen_skel_statep);
141 
142 		return (rval);
143 	}
144 
145 	return (rval);
146 }
147 
148 
149 int
150 _fini()
151 {
152 	int rval;
153 
154 	if ((rval = mod_remove(&modlinkage)) != 0) {
155 
156 		return (rval);
157 	}
158 	ddi_soft_state_fini(&ugen_skel_statep);
159 
160 	return (rval);
161 }
162 
163 
164 int
165 _info(struct modinfo *modinfop)
166 {
167 	return (mod_info(&modlinkage, modinfop));
168 }
169 
170 
171 /*ARGSUSED*/
172 static int
173 ugen_skel_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
174     void **result)
175 {
176 	int		rval = DDI_FAILURE;
177 	int		instance =
178 			UGEN_MINOR_TO_INSTANCE(getminor((dev_t)arg));
179 	ugen_skel_state_t *ugen_skelp;
180 
181 	switch (infocmd) {
182 	case DDI_INFO_DEVT2DEVINFO:
183 		ugen_skelp = ddi_get_soft_state(ugen_skel_statep, instance);
184 		if (ugen_skelp != NULL) {
185 			*result = ugen_skelp->ugen_skel_dip;
186 			if (*result != NULL) {
187 				rval = DDI_SUCCESS;
188 			}
189 		} else {
190 			*result = NULL;
191 		}
192 
193 		break;
194 	case DDI_INFO_DEVT2INSTANCE:
195 		*result = (void *)(uintptr_t)instance;
196 		rval = DDI_SUCCESS;
197 
198 		break;
199 	default:
200 
201 		break;
202 	}
203 
204 	return (rval);
205 }
206 
207 
208 /*
209  * ugen_skel_attach()
210  */
211 static int
212 ugen_skel_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
213 {
214 	ugen_skel_state_t	*ugen_skelp;
215 	int			instance;	/* Driver instance number */
216 	int			rval;
217 	usb_ugen_info_t		usb_ugen_info;
218 
219 	/* Get instance number */
220 	instance = ddi_get_instance(dip);
221 
222 	switch (cmd) {
223 	case DDI_ATTACH:
224 
225 		break;
226 	case DDI_RESUME:
227 		ugen_skelp = ddi_get_soft_state(ugen_skel_statep, instance);
228 		if (ugen_skelp == NULL) {
229 
230 			return (DDI_FAILURE);
231 		}
232 
233 		rval = usb_ugen_attach(ugen_skelp->ugen_skel_hdl, cmd);
234 
235 		return (rval == USB_SUCCESS ? DDI_SUCCESS : DDI_FAILURE);
236 	default:
237 
238 		return (DDI_FAILURE);
239 	}
240 
241 	if (ddi_soft_state_zalloc(ugen_skel_statep, instance) ==
242 	    DDI_SUCCESS) {
243 		ugen_skelp = ddi_get_soft_state(ugen_skel_statep,
244 							instance);
245 	}
246 	if (ugen_skelp == NULL) {
247 
248 		return (DDI_FAILURE);
249 	}
250 
251 	if ((rval = usb_client_attach(dip, USBDRV_VERSION, 0)) !=
252 	    USB_SUCCESS) {
253 
254 		goto fail;
255 	}
256 
257 	ugen_skelp->ugen_skel_dip	= dip;
258 	ugen_skelp->ugen_skel_instance	= instance;
259 
260 	/* get a ugen handle */
261 	bzero(&usb_ugen_info, sizeof (usb_ugen_info));
262 	usb_ugen_info.usb_ugen_flags =
263 			USB_UGEN_ENABLE_PM | USB_UGEN_REMOVE_CHILDREN;
264 	usb_ugen_info.usb_ugen_minor_node_ugen_bits_mask =
265 				(dev_t)UGEN_MINOR_UGEN_BITS_MASK;
266 	usb_ugen_info.usb_ugen_minor_node_instance_mask =
267 				(dev_t)~UGEN_MINOR_UGEN_BITS_MASK;
268 	ugen_skelp->ugen_skel_hdl = usb_ugen_get_hdl(dip,
269 				&usb_ugen_info);
270 
271 	if (usb_ugen_attach(ugen_skelp->ugen_skel_hdl, cmd) != USB_SUCCESS) {
272 
273 		    goto fail;
274 	}
275 
276 	/* register for hotplug events */
277 	if (usb_register_event_cbs(dip, &ugen_skel_events, 0) != USB_SUCCESS) {
278 
279 		    goto fail;
280 	}
281 
282 	ddi_report_dev(dip);
283 
284 	return (DDI_SUCCESS);
285 
286 fail:
287 	if (ugen_skelp) {
288 		usb_unregister_event_cbs(dip, &ugen_skel_events);
289 		usb_ugen_release_hdl(ugen_skelp->
290 					ugen_skel_hdl);
291 		ddi_soft_state_free(ugen_skel_statep,
292 				    ugen_skelp->ugen_skel_instance);
293 		usb_client_detach(dip, NULL);
294 	}
295 
296 	return (DDI_FAILURE);
297 }
298 
299 
300 /*
301  * ugen_skel_detach()
302  */
303 static int
304 ugen_skel_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
305 {
306 	int		rval = USB_FAILURE;
307 	ugen_skel_state_t *ugen_skelp = ddi_get_soft_state(ugen_skel_statep,
308 				    ddi_get_instance(dip));
309 
310 	if (ugen_skelp) {
311 		switch (cmd) {
312 		case DDI_DETACH:
313 			rval = usb_ugen_detach(ugen_skelp->ugen_skel_hdl, cmd);
314 			if (rval == USB_SUCCESS) {
315 				usb_unregister_event_cbs(dip,
316 							&ugen_skel_events);
317 				usb_ugen_release_hdl(ugen_skelp->
318 							ugen_skel_hdl);
319 				ddi_soft_state_free(ugen_skel_statep,
320 				    ugen_skelp->ugen_skel_instance);
321 				usb_client_detach(dip, NULL);
322 			}
323 
324 			break;
325 		case DDI_SUSPEND:
326 			rval = usb_ugen_detach(ugen_skelp->ugen_skel_hdl, cmd);
327 
328 			break;
329 		default:
330 
331 			break;
332 		}
333 	}
334 
335 	return (rval == USB_SUCCESS ? DDI_SUCCESS : DDI_FAILURE);
336 }
337 
338 
339 /*
340  * ugen_skel_disconnect_ev_cb:
341  */
342 static int
343 ugen_skel_disconnect_ev_cb(dev_info_t *dip)
344 {
345 	ugen_skel_state_t *ugen_skelp = ddi_get_soft_state(ugen_skel_statep,
346 						ddi_get_instance(dip));
347 
348 	return (usb_ugen_disconnect_ev_cb(ugen_skelp->ugen_skel_hdl));
349 }
350 
351 
352 /*
353  * ugen_skel_reconnect_ev_cb:
354  */
355 static int
356 ugen_skel_reconnect_ev_cb(dev_info_t *dip)
357 {
358 	ugen_skel_state_t *ugen_skelp = ddi_get_soft_state(ugen_skel_statep,
359 						ddi_get_instance(dip));
360 
361 	return (usb_ugen_reconnect_ev_cb(ugen_skelp->ugen_skel_hdl));
362 }
363 
364 
365 /*
366  * ugen_skel_open:
367  */
368 static int
369 ugen_skel_open(dev_t *devp, int flag, int sflag, cred_t *cr)
370 {
371 	ugen_skel_state_t *ugen_skelp;
372 
373 	if ((ugen_skelp = ddi_get_soft_state(ugen_skel_statep,
374 	    UGEN_MINOR_TO_INSTANCE(getminor(*devp)))) == NULL) {
375 		/* deferred detach */
376 
377 		return (ENXIO);
378 	}
379 
380 	return (usb_ugen_open(ugen_skelp->ugen_skel_hdl, devp, flag,
381 		sflag, cr));
382 }
383 
384 
385 /*
386  * ugen_skel_close()
387  */
388 static int
389 ugen_skel_close(dev_t dev, int flag, int otype, cred_t *cr)
390 {
391 	ugen_skel_state_t *ugen_skelp = ddi_get_soft_state(ugen_skel_statep,
392 			UGEN_MINOR_TO_INSTANCE(getminor(dev)));
393 
394 	return (usb_ugen_close(ugen_skelp->ugen_skel_hdl, dev, flag,
395 		otype, cr));
396 }
397 
398 
399 /*
400  * ugen_skel_read/write()
401  */
402 static int
403 ugen_skel_read(dev_t dev, struct uio *uiop, cred_t *credp)
404 {
405 	ugen_skel_state_t *ugen_skelp = ddi_get_soft_state(ugen_skel_statep,
406 			UGEN_MINOR_TO_INSTANCE(getminor(dev)));
407 	if (ugen_skelp == NULL) {
408 
409 		return (ENXIO);
410 	}
411 
412 	return (usb_ugen_read(ugen_skelp->ugen_skel_hdl, dev,
413 					uiop, credp));
414 }
415 
416 
417 static int
418 ugen_skel_write(dev_t dev, struct uio *uiop, cred_t *credp)
419 {
420 	ugen_skel_state_t *ugen_skelp = ddi_get_soft_state(ugen_skel_statep,
421 			UGEN_MINOR_TO_INSTANCE(getminor(dev)));
422 	if (ugen_skelp == NULL) {
423 
424 		return (ENXIO);
425 	}
426 	return (usb_ugen_write(ugen_skelp->ugen_skel_hdl,
427 					dev, uiop, credp));
428 }
429 
430 
431 /*
432  * ugen_skel_poll
433  */
434 static int
435 ugen_skel_poll(dev_t dev, short events,
436     int anyyet,  short *reventsp, struct pollhead **phpp)
437 {
438 	ugen_skel_state_t *ugen_skelp = ddi_get_soft_state(ugen_skel_statep,
439 			UGEN_MINOR_TO_INSTANCE(getminor(dev)));
440 	if (ugen_skelp == NULL) {
441 
442 		return (ENXIO);
443 	}
444 
445 	return (usb_ugen_poll(ugen_skelp->ugen_skel_hdl, dev, events,
446 					anyyet, reventsp, phpp));
447 }
448 
449 
450 /*
451  * ugen_skel_power:
452  *	PM entry point
453  */
454 static int
455 ugen_skel_power(dev_info_t *dip, int comp, int level)
456 {
457 	int rval;
458 
459 	ugen_skel_state_t *ugen_skelp = ddi_get_soft_state(ugen_skel_statep,
460 						ddi_get_instance(dip));
461 	if (ugen_skelp == NULL) {
462 
463 		return (DDI_FAILURE);
464 	}
465 	rval = usb_ugen_power(ugen_skelp->ugen_skel_hdl, comp, level);
466 
467 	return (rval == USB_SUCCESS ? DDI_SUCCESS : DDI_FAILURE);
468 }
469