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 */
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_needed, /* 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
_init()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
_fini()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
_info(struct modinfo * modinfop)163 _info(struct modinfo *modinfop)
164 {
165 return (mod_info(&modlinkage, modinfop));
166 }
167
168
169 /*ARGSUSED*/
170 static int
ugen_skel_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)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
ugen_skel_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
ugen_skel_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
ugen_skel_disconnect_ev_cb(dev_info_t * dip)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
ugen_skel_reconnect_ev_cb(dev_info_t * dip)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
ugen_skel_open(dev_t * devp,int flag,int sflag,cred_t * cr)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
ugen_skel_close(dev_t dev,int flag,int otype,cred_t * cr)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
ugen_skel_read(dev_t dev,struct uio * uiop,cred_t * credp)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
ugen_skel_write(dev_t dev,struct uio * uiop,cred_t * credp)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
ugen_skel_poll(dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)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
ugen_skel_power(dev_info_t * dip,int comp,int level)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