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 };
111 
112 static struct modldrv modldrv = {
113 	&mod_driverops,			/* Module type */
114 	"USB Generic driver",	/* Name of the module. */
115 	&ugen_skel_ops,			/* driver ops */
116 };
117 
118 static struct modlinkage modlinkage = {
119 	MODREV_1,
120 	(void *)&modldrv,
121 	NULL
122 };
123 
124 
125 int
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 
146 int
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 
161 int
162 _info(struct modinfo *modinfop)
163 {
164 	return (mod_info(&modlinkage, modinfop));
165 }
166 
167 
168 /*ARGSUSED*/
169 static int
170 ugen_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  */
208 static int
209 ugen_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 
283 fail:
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  */
300 static int
301 ugen_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  */
339 static int
340 ugen_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  */
352 static int
353 ugen_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  */
365 static int
366 ugen_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  */
385 static int
386 ugen_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  */
399 static int
400 ugen_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 
414 static int
415 ugen_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  */
431 static int
432 ugen_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  */
451 static int
452 ugen_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