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, 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 }