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