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