1c77a61ayz/*
2c77a61ayz * CDDL HEADER START
3c77a61ayz *
4c77a61ayz * The contents of this file are subject to the terms of the
5c77a61ayz * Common Development and Distribution License (the "License").
6c77a61ayz * You may not use this file except in compliance with the License.
7c77a61ayz *
8c77a61ayz * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9c77a61ayz * or http://www.opensolaris.org/os/licensing.
10c77a61ayz * See the License for the specific language governing permissions
11c77a61ayz * and limitations under the License.
12c77a61ayz *
13c77a61ayz * When distributing Covered Code, include this CDDL HEADER in each
14c77a61ayz * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15c77a61ayz * If applicable, add the following below this CDDL HEADER, with the
16c77a61ayz * fields enclosed by brackets "[]" replaced with your own identifying
17c77a61ayz * information: Portions Copyright [yyyy] [name of copyright owner]
18c77a61ayz *
19c77a61ayz * CDDL HEADER END
20c77a61ayz */
21c77a61ayz/*
226847c24Raymond Chen * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
23c77a61ayz */
24c77a61ayz
2515c07adJohn Levon/*
2615c07adJohn Levon * Copyright (c) 2018, Joyent, Inc.
2715c07adJohn Levon */
28c77a61ayz
29c77a61ayz/*
30c77a61ayz * USB video class driver (usbvc(7D))
31c77a61ayz *
32c77a61ayz * 1. Overview
33c77a61ayz * ------------
34c77a61ayz *
35c77a61ayz * This driver supports USB video class devices that used to capture video,
36c77a61ayz * e.g., some webcams. It is developed according to "USB Device Class
37c77a61ayz * Definition for Video Devices" spec. This spec defines detail info needed by
38c77a61ayz * designing a USB video device. It is available at:
39c77a61ayz * http://www.usb.org/developers/devclass_docs
40c77a61ayz *
41c77a61ayz * This driver implements:
42c77a61ayz *
43c77a61ayz *   - V4L2 interfaces for applications to communicate with video devices.
44c77a61ayz *     V4L2 is an API that is widely used by video applications, like Ekiga,
45c77a61ayz *     luvcview, etc. The API spec is at:
46c77a61ayz *     http://www.thedirks.org/v4l2/
47c77a61ayz *     This driver is according to V4L2 spec version 0.20
48c77a61ayz *
49c77a61ayz *   - Video capture function. (Video output is not supported by now.)
50c77a61ayz *
51c77a61ayz *   - Isochronous transfer for video data. (Bulk transfer is not supported.)
52c77a61ayz *
53c77a61ayz *   - read & mmap I/O methods for userland video applications to get video
54c77a61ayz *     data. Userland video applications can use read() system call directly,
55c77a61ayz *     it is the simplest way but not the most efficient way. Applications can
56c77a61ayz *     also use mmap() system call to map several bufs (they are linked as a
57c77a61ayz *     buf list), and then use some specific ioctls to start/stop isoc polling,
58c77a61ayz *     to queue/dequeue bufs.
59c77a61ayz *
60c77a61ayz * 2. Source and header files
61c77a61ayz * ---------------------------
62c77a61ayz *
63c77a61ayz * There are two source files and three header files for this driver:
64c77a61ayz *
65c77a61ayz *   - usbvc.c		Main source file, implements usb video class spec.
66c77a61ayz *
67c77a61ayz *   - usbvc_v4l2.c	V4L2 interface specific code.
68c77a61ayz *
69c77a61ayz *   - usbvc_var.h	Main header file, includes soft state structure.
70c77a61ayz *
71c77a61ayz *   - usbvc.h		The descriptors in usb video class spec.
72c77a61ayz *
73c77a61ayz *   - videodev2.h	This header file is included in V4L2 spec. It defines
74c77a61ayz *     ioctls and data structures that used as an interface between video
75c77a61ayz *     applications and video drivers. This is the only header file that
76c77a61ayz *     usbvc driver should export to userland application.
77c77a61ayz *
78c77a61ayz * 3. USB video class devices overview
79c77a61ayz * -----------------------------------
80c77a61ayz * According to UVC spec, there must be one control interface in a UVC device.
81c77a61ayz * Control interface is used to receive control commands from user, all the
82c77a61ayz * commands are sent through default ctrl pipe. usbvc driver implements V4L2
83c77a61ayz * API, so ioctls are implemented to relay user commands to UVC device.
84c77a61ayz *
85c77a61ayz * There can be no or multiple stream interfaces in a UVC device. Stream
86c77a61ayz * interfaces are used to do video data I/O. In practice, if no stream
87c77a61ayz * interface, the video device can do nothing since it has no data I/O.
88c77a61ayz *
89c77a61ayz * usbvc driver parses descriptors of control interface and stream interfaces.
90c77a61ayz * The descriptors tell the function layout and the capability of the device.
91c77a61ayz * During attach, usbvc driver set up some key data structures according to
92c77a61ayz * the descriptors.
93c77a61ayz *
94c77a61ayz * 4. I/O methods
95c77a61ayz * ---------------
96c77a61ayz *
97c77a61ayz * Userland applications use ioctls to set/get video formats of the device,
98c77a61ayz * and control brightness, contrast, image size, etc.
99c77a61ayz *
100c77a61ayz * Besides implementing standard read I/O method to get video data from
101c77a61ayz * the device, usbvc driver also implements some specific ioctls to implement
102c77a61ayz * mmap I/O method.
103c77a61ayz *
104c77a61ayz * A view from userland application: ioctl and mmap flow chart:
105c77a61ayz *
106c77a61ayz * REQBUFS -> QUERYBUF -> mmap() ->
107c77a61ayz *
108c77a61ayz *    -> QBUF -> STREAMON -> DQBUF -> process image -> QBUF
109c77a61ayz *			       ^			|
110c77a61ayz *			       |			|
111c77a61ayz *			       |			v
112c77a61ayz *			       |---<--------------------
113c77a61ayz *
114c77a61ayz * The above queue and dequeue buf operations can be stopped by issuing a
115c77a61ayz * STREAMOFF ioctl.
116c77a61ayz *
117c77a61ayz * 5. Device states
118c77a61ayz * ----------------
119c77a61ayz *
120c77a61ayz * The device has four states (refer to usbai.h):
121c77a61ayz *
122c77a61ayz *	- USB_DEV_ONLINE: In action or ready for action.
123c77a61ayz *
124c77a61ayz *	- USB_DEV_DISCONNECTED: Hotplug removed, or device not present/correct
125c77a61ayz *				on resume (CPR).
126c77a61ayz *
127c77a61ayz *	- USB_DEV_SUSPENDED: Device has been suspended along with the system.
128c77a61ayz *
129c77a61ayz *	- USB_DEV_PWRED_DOWN: Device has been powered down.  (Note that this
130c77a61ayz *		driver supports only two power states, powered down and
131c77a61ayz *		full power.)
132c77a61ayz *
133c77a61ayz * 6. Serialize
134c77a61ayz * -------------
135c77a61ayz * In order to avoid race conditions between driver entry points, access to
136c77a61ayz * the device is serialized. All the ioctls, and read, open/close are
137c77a61ayz * serialized. The functions usbvc_serialize/release_access are implemented
138c77a61ayz * for this purpose.
139c77a61ayz *
140c77a61ayz * 7. PM & CPR
141c77a61ayz * ------------
142c77a61ayz * PM & CPR are supported. pm_busy_component and pm_idle_component mark
143c77a61ayz * the device as busy or idle to the system.
144c77a61ayz */
145c77a61ayz
146c77a61ayz#if defined(lint) && !defined(DEBUG)
147c77a61ayz#define	DEBUG
148c77a61ayz#endif
149c77a61ayz
150c77a61ayz#define	USBDRV_MAJOR_VER	2
151c77a61ayz#define	USBDRV_MINOR_VER	0
152c77a61ayz
153c77a61ayz#include <sys/usb/usba.h>
154c77a61ayz#include <sys/fcntl.h>
155c77a61ayz#include <sys/cmn_err.h>
156c77a61ayz#include <sys/usb/clients/video/usbvc/usbvc_var.h>
157c77a61ayz#include <sys/videodev2.h> /* V4L2 API header file */
158c77a61ayz
159c77a61ayz/* Descriptors according to USB video class spec */
160c77a61ayz#include <sys/usb/clients/video/usbvc/usbvc.h>
161c77a61ayz
162c77a61ayzstatic uint_t	usbvc_errmask		= (uint_t)PRINT_MASK_ALL;
163c77a61ayzstatic uint_t	usbvc_errlevel = 4;
164c77a61ayzstatic uint_t	usbvc_instance_debug = (uint_t)-1;
165c77a61ayz
166c77a61ayzstatic char	*name = "usbvc";	/* Driver name, used all over. */
167c77a61ayz
168c77a61ayz/*
169c77a61ayz * Function Prototypes
170c77a61ayz */
171c77a61ayz
172c77a61ayz/* Entries */
173c77a61ayzstatic int	usbvc_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
174c77a61ayzstatic int	usbvc_attach(dev_info_t *, ddi_attach_cmd_t);
175c77a61ayzstatic int	usbvc_detach(dev_info_t *, ddi_detach_cmd_t);
176c77a61ayzstatic void	usbvc_cleanup(dev_info_t *, usbvc_state_t *);
177c77a61ayzstatic int	usbvc_open(dev_t *, int, int, cred_t *);
178c77a61ayzstatic int	usbvc_close(dev_t, int, int, cred_t *);
179c77a61ayzstatic int	usbvc_read(dev_t, struct uio *uip_p, cred_t *);
180c77a61ayzstatic int	usbvc_strategy(struct buf *);
181c77a61ayzstatic void	usbvc_minphys(struct buf *);
182c77a61ayzstatic int	usbvc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
183c77a61ayzstatic int	usbvc_devmap(dev_t, devmap_cookie_t, offset_t,
184c77a61ayz		    size_t, size_t *, uint_t);
185c77a61ayz
186c77a61ayz/* pm and cpr */
187c77a61ayzstatic int	usbvc_power(dev_info_t *, int, int);
188c77a61ayzstatic void	usbvc_init_power_mgmt(usbvc_state_t *);
189c77a61ayzstatic void	usbvc_destroy_power_mgmt(usbvc_state_t *);
190c77a61ayzstatic void	usbvc_pm_busy_component(usbvc_state_t *);
191c77a61ayzstatic void	usbvc_pm_idle_component(usbvc_state_t *);
192c77a61ayzstatic int	usbvc_pwrlvl0(usbvc_state_t *);
193c77a61ayzstatic int	usbvc_pwrlvl1(usbvc_state_t *);
194c77a61ayzstatic int	usbvc_pwrlvl2(usbvc_state_t *);
195c77a61ayzstatic int	usbvc_pwrlvl3(usbvc_state_t *);
196c77a61ayzstatic void	usbvc_cpr_suspend(dev_info_t *);
197c77a61ayzstatic void	usbvc_cpr_resume(dev_info_t *);
198c77a61ayzstatic void	usbvc_restore_device_state(dev_info_t *, usbvc_state_t *);
199c77a61ayz
200c77a61ayz/* Events */
201c77a61ayzstatic int	usbvc_disconnect_event_cb(dev_info_t *);
202c77a61ayzstatic int	usbvc_reconnect_event_cb(dev_info_t *);
203c77a61ayz
204c77a61ayz/* Sync objs and lists */
205c77a61ayzstatic void	usbvc_init_sync_objs(usbvc_state_t *);
206c77a61ayzstatic void	usbvc_fini_sync_objs(usbvc_state_t *);
207c77a61ayzstatic void	usbvc_init_lists(usbvc_state_t *);
208c77a61ayzstatic void	usbvc_fini_lists(usbvc_state_t *);
209c77a61ayzstatic void	usbvc_free_ctrl_descr(usbvc_state_t *);
210c77a61ayzstatic void	usbvc_free_stream_descr(usbvc_state_t *);
211c77a61ayz
212c77a61ayz/* Parse descriptors */
213c77a61ayzstatic int	usbvc_chk_descr_len(uint8_t, uint8_t, uint8_t,
214c77a61ayz		    usb_cvs_data_t *);
215c77a61ayzstatic usbvc_stream_if_t *usbvc_parse_stream_if(usbvc_state_t *, int);
216c77a61ayzstatic int	usbvc_parse_ctrl_if(usbvc_state_t *);
217c77a61ayzstatic int	usbvc_parse_stream_ifs(usbvc_state_t *);
218c77a61ayzstatic void	usbvc_parse_color_still(usbvc_state_t *,
219c77a61ayz		    usbvc_format_group_t *, usb_cvs_data_t *, uint_t, uint_t);
220c77a61ayzstatic void	usbvc_parse_frames(usbvc_state_t *, usbvc_format_group_t *,
221c77a61ayz		    usb_cvs_data_t *, uint_t, uint_t);
222c77a61ayzstatic int	usbvc_parse_format_group(usbvc_state_t *,
223c77a61ayz		    usbvc_format_group_t *, usb_cvs_data_t *, uint_t, uint_t);
224c77a61ayzstatic int	usbvc_parse_format_groups(usbvc_state_t *, usbvc_stream_if_t *);
225c77a61ayzstatic int	usbvc_parse_stream_header(usbvc_state_t *, usbvc_stream_if_t *);
226c77a61ayz
227c77a61ayz/* read I/O functions */
228c77a61ayzstatic int	usbvc_alloc_read_bufs(usbvc_state_t *, usbvc_stream_if_t *);
229c77a61ayzstatic int	usbvc_read_buf(usbvc_state_t *, struct buf *);
230c77a61ayzstatic void	usbvc_free_read_buf(usbvc_buf_t *);
231c77a61ayzstatic void	usbvc_free_read_bufs(usbvc_state_t *, usbvc_stream_if_t *);
232c77a61ayzstatic void	usbvc_close_isoc_pipe(usbvc_state_t *, usbvc_stream_if_t *);
233c77a61ayz
234c77a61ayz/* callbacks */
235c77a61ayzstatic void	usbvc_isoc_cb(usb_pipe_handle_t, usb_isoc_req_t *);
236c77a61ayzstatic void	usbvc_isoc_exc_cb(usb_pipe_handle_t, usb_isoc_req_t *);
237c77a61ayz
238c77a61ayz/* Others */
239c77a61ayzstatic int	usbvc_set_alt(usbvc_state_t *, usbvc_stream_if_t *);
240c77a61ayzstatic int	usbvc_decode_stream_header(usbvc_state_t *, usbvc_buf_grp_t *,
241c77a61ayz		    mblk_t *, int);
242c77a61ayzstatic int	usbvc_serialize_access(usbvc_state_t *, boolean_t);
243c77a61ayzstatic void	usbvc_release_access(usbvc_state_t *);
244c77a61ayzstatic int		usbvc_set_default_stream_fmt(usbvc_state_t *);
245c77a61ayz
246c77a61ayzstatic usb_event_t usbvc_events = {
247c77a61ayz	usbvc_disconnect_event_cb,
248c77a61ayz	usbvc_reconnect_event_cb,
249c77a61ayz	NULL, NULL
250c77a61ayz};
251c77a61ayz
252c77a61ayz/* module loading stuff */
253c77a61ayzstruct cb_ops usbvc_cb_ops = {
254c77a61ayz	usbvc_open,		/* open  */
255c77a61ayz	usbvc_close,		/* close */
256c77a61ayz	usbvc_strategy,	/* strategy */
257c77a61ayz	nulldev,		/* print */
258c77a61ayz	nulldev,		/* dump */
259c77a61ayz	usbvc_read,		/* read */
260c77a61ayz	nodev,			/* write */
261c77a61ayz	usbvc_ioctl,		/* ioctl */
262c77a61ayz	usbvc_devmap,		/* devmap */
263c77a61ayz	nodev,			/* mmap */
264c77a61ayz	ddi_devmap_segmap,	/* segmap */
265c77a61ayz	nochpoll,		/* poll */
266c77a61ayz	ddi_prop_op,		/* cb_prop_op */
267c77a61ayz	NULL,			/* streamtab  */
268c77a61ayz	D_MP | D_DEVMAP
269c77a61ayz};
270c77a61ayz
271c77a61ayzstatic struct dev_ops usbvc_ops = {
272c77a61ayz	DEVO_REV,		/* devo_rev, */
273c77a61ayz	0,			/* refcnt  */
274c77a61ayz	usbvc_info,		/* info */
275c77a61ayz	nulldev,		/* identify */
276c77a61ayz	nulldev,		/* probe */
277c77a61ayz	usbvc_attach,		/* attach */
278c77a61ayz	usbvc_detach,		/* detach */
279c77a61ayz	nodev,			/* reset */
280c77a61ayz	&usbvc_cb_ops,	/* driver operations */
281c77a61ayz	NULL,			/* bus operations */
2821939740Sherry Moore	usbvc_power,		/* power */
28309dd0d6Raymond Chen	ddi_quiesce_not_needed,	/* quiesce */
284c77a61ayz};
285c77a61ayz
286c77a61ayzstatic struct modldrv usbvc_modldrv =	{
287c77a61ayz	&mod_driverops,
28861b2e29lc	"USB video class driver",
289c77a61ayz	&usbvc_ops
290c77a61ayz};
291c77a61ayz
292c77a61ayzstatic struct modlinkage modlinkage = {
293c77a61ayz	MODREV_1,
294c77a61ayz	&usbvc_modldrv,
295c77a61ayz	NULL
296c77a61ayz};
297c77a61ayz
298c77a61ayz/* Soft state structures */
299c77a61ayz#define	USBVC_INITIAL_SOFT_SPACE	1
300c77a61ayzstatic void *usbvc_statep;
301c77a61ayz
302c77a61ayz
303c77a61ayz/*
304c77a61ayz * Module-wide initialization routine.
305c77a61ayz */
306c77a61ayzint
307c77a61ayz_init(void)
308c77a61ayz{
309c77a61ayz	int rval;
310c77a61ayz
311c77a61ayz	if ((rval = ddi_soft_state_init(&usbvc_statep,
312c77a61ayz	    sizeof (usbvc_state_t), USBVC_INITIAL_SOFT_SPACE)) != 0) {
313c77a61ayz
314c77a61ayz		return (rval);
315c77a61ayz	}
316c77a61ayz
317c77a61ayz	if ((rval = mod_install(&modlinkage)) != 0) {
318c77a61ayz		ddi_soft_state_fini(&usbvc_statep);
319c77a61ayz	}
320c77a61ayz
321c77a61ayz	return (rval);
322c77a61ayz}
323c77a61ayz
324c77a61ayz
325c77a61ayz/*
326c77a61ayz * Module-wide tear-down routine.
327c77a61ayz */
328c77a61ayzint
329c77a61ayz_fini(void)
330c77a61ayz{
331c77a61ayz	int rval;
332c77a61ayz
333c77a61ayz	if ((rval = mod_remove(&modlinkage)) != 0) {
334c77a61ayz
335c77a61ayz		return (rval);
336c77a61ayz	}
337c77a61ayz
338c77a61ayz	ddi_soft_state_fini(&usbvc_statep);
339c77a61ayz
340c77a61ayz	return (rval);
341c77a61ayz}
342c77a61ayz
343c77a61ayz
344c77a61ayzint
345c77a61ayz_info(struct modinfo *modinfop)
346c77a61ayz{
347c77a61ayz	return (mod_info(&modlinkage, modinfop));
348c77a61ayz}
349c77a61ayz
350c77a61ayz
351c77a61ayz/*
352c77a61ayz * usbvc_info:
353c77a61ayz *	Get minor number, soft state structure, etc.
354c77a61ayz */
355c77a61ayz/*ARGSUSED*/
356c77a61ayzstatic int
357c77a61ayzusbvc_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
35811b5d82Toomas Soome    void *arg, void **result)
359c77a61ayz{
360c77a61ayz	usbvc_state_t	*usbvcp;
361c77a61ayz	int error = DDI_FAILURE;
362c77a61ayz
363c77a61ayz	switch (infocmd) {
364c77a61ayz	case DDI_INFO_DEVT2DEVINFO:
365c77a61ayz		if ((usbvcp = ddi_get_soft_state(usbvc_statep,
366c77a61ayz		    getminor((dev_t)arg))) != NULL) {
367c77a61ayz			*result = usbvcp->usbvc_dip;
368c77a61ayz			if (*result != NULL) {
369c77a61ayz				error = DDI_SUCCESS;
370c77a61ayz			}
371c77a61ayz		} else {
372c77a61ayz			*result = NULL;
373c77a61ayz		}
374c77a61ayz		break;
375c77a61ayz	case DDI_INFO_DEVT2INSTANCE:
376c77a61ayz		*result = (void *)(uintptr_t)getminor((dev_t)arg);
377c77a61ayz		error = DDI_SUCCESS;
378c77a61ayz		break;
379c77a61ayz	default:
380c77a61ayz		break;
381c77a61ayz	}
382c77a61ayz
383c77a61ayz	return (error);
384c77a61ayz}
385c77a61ayz
386c77a61ayz
387c77a61ayz/*
388c77a61ayz * Entry functions.
389c77a61ayz */
390c77a61ayz
391c77a61ayz/*
392c77a61ayz * usbvc_attach:
393c77a61ayz *	Attach or resume.
394c77a61ayz *
395c77a61ayz *	For attach, initialize state and device, including:
396c77a61ayz *		state variables, locks, device node
397c77a61ayz *		device registration with system
398c77a61ayz *		power management, hotplugging
399c77a61ayz *	For resume, restore device and state
400c77a61ayz */
401c77a61ayzstatic int
402c77a61ayzusbvc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
403c77a61ayz{
404c77a61ayz	int			instance = ddi_get_instance(dip);
405c77a61ayz	usbvc_state_t		*usbvcp = NULL;
406c77a61ayz	int			status;
407c77a61ayz
408c77a61ayz	switch (cmd) {
409c77a61ayz	case DDI_ATTACH:
410c77a61ayz
411c77a61ayz		break;
412c77a61ayz	case DDI_RESUME:
413c77a61ayz		usbvc_cpr_resume(dip);
414c77a61ayz
415c77a61ayz		return (DDI_SUCCESS);
416c77a61ayz	default:
417c77a61ayz
418c77a61ayz		return (DDI_FAILURE);
419c77a61ayz	}
420c77a61ayz
421c77a61ayz	if (ddi_soft_state_zalloc(usbvc_statep, instance) == DDI_SUCCESS) {
422c77a61ayz		usbvcp = ddi_get_soft_state(usbvc_statep, instance);
423c77a61ayz	}
424c77a61ayz	if (usbvcp == NULL)  {
425c77a61ayz
426c77a61ayz		return (DDI_FAILURE);
427c77a61ayz	}
428c77a61ayz
429c77a61ayz	usbvcp->usbvc_dip = dip;
430c77a61ayz
431c77a61ayz	usbvcp->usbvc_log_handle = usb_alloc_log_hdl(dip,
432ef772e5fb	    "usbvc", &usbvc_errlevel,
433ef772e5fb	    &usbvc_errmask, &usbvc_instance_debug, 0);
434c77a61ayz
435c77a61ayz	USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
436ef772e5fb	    "usbvc_attach: enter");
437c77a61ayz
438c77a61ayz	if ((status = usb_client_attach(dip, USBDRV_VERSION, 0)) !=
439c77a61ayz	    USB_SUCCESS) {
440c77a61ayz		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
441c77a61ayz		    "usbvc_attach: usb_client_attach failed, error code:%d",
442c77a61ayz		    status);
443c77a61ayz
444c77a61ayz		goto fail;
445c77a61ayz	}
446c77a61ayz
447c77a61ayz	if ((status = usb_get_dev_data(dip, &usbvcp->usbvc_reg,
448c77a61ayz	    USB_PARSE_LVL_ALL, 0)) != USB_SUCCESS) {
449c77a61ayz		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
450c77a61ayz		    "usbvc_attach: usb_get_dev_data failed, error code:%d",
451c77a61ayz		    status);
452c77a61ayz
453c77a61ayz		goto fail;
454c77a61ayz	}
455c77a61ayz	usbvc_init_sync_objs(usbvcp);
456c77a61ayz
457c77a61ayz	/* create minor node */
458c77a61ayz	if ((status = ddi_create_minor_node(dip, name, S_IFCHR, instance,
459c77a61ayz	    "usb_video", 0)) != DDI_SUCCESS) {
460c77a61ayz		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
461c77a61ayz		    "usbvc_attach: Error creating minor node, error code:%d",
462c77a61ayz		    status);
463c77a61ayz
464c77a61ayz		goto fail;
465c77a61ayz	}
466c77a61ayz
467c77a61ayz	mutex_enter(&usbvcp->usbvc_mutex);
468c77a61ayz	usbvc_init_lists(usbvcp);
469c77a61ayz
470c77a61ayz	usbvcp->usbvc_default_ph = usbvcp->usbvc_reg->dev_default_ph;
471c77a61ayz
472c77a61ayz	/* Put online before PM init as can get power managed afterward. */
473c77a61ayz	usbvcp->usbvc_dev_state = USB_DEV_ONLINE;
474c77a61ayz	mutex_exit(&usbvcp->usbvc_mutex);
475c77a61ayz
476c77a61ayz	/* initialize power management */
477c77a61ayz	usbvc_init_power_mgmt(usbvcp);
478c77a61ayz
479c77a61ayz	if ((status = usbvc_parse_ctrl_if(usbvcp)) != USB_SUCCESS) {
480c77a61ayz		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
481c77a61ayz		    "usbvc_attach: parse ctrl interface fail, error code:%d",
482c77a61ayz		    status);
483c77a61ayz
484c77a61ayz		goto fail;
485c77a61ayz	}
486c77a61ayz	if ((status = usbvc_parse_stream_ifs(usbvcp)) != USB_SUCCESS) {
487c77a61ayz		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
488c77a61ayz		    "usbvc_attach: parse stream interfaces fail, error code:%d",
489c77a61ayz		    status);
490c77a61ayz
491c77a61ayz		goto fail;
492c77a61ayz	}
493c77a61ayz	(void) usbvc_set_default_stream_fmt(usbvcp);
494c77a61ayz
495c77a61ayz	/* Register for events */
496c77a61ayz	if ((status = usb_register_event_cbs(dip, &usbvc_events, 0)) !=
497c77a61ayz	    USB_SUCCESS) {
498c77a61ayz		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
499c77a61ayz		    "usbvc_attach: register_event_cbs failed, error code:%d",
500c77a61ayz		    status);
501c77a61ayz
502c77a61ayz		goto fail;
503c77a61ayz	}
504c77a61ayz
505c77a61ayz	/* Report device */
506c77a61ayz	ddi_report_dev(dip);
507c77a61ayz
508c77a61ayz	return (DDI_SUCCESS);
509c77a61ayz
510c77a61ayzfail:
511c77a61ayz	if (usbvcp) {
512c77a61ayz		usbvc_cleanup(dip, usbvcp);
513c77a61ayz	}
514c77a61ayz
515c77a61ayz	return (DDI_FAILURE);
516c77a61ayz}
517c77a61ayz
518c77a61ayz
519c77a61ayz/*
520c77a61ayz * usbvc_detach:
521c77a61ayz *	detach or suspend driver instance
522c77a61ayz *
523c77a61ayz * Note: in detach, only contention threads is from pm and disconnnect.
524c77a61ayz */
525c77a61ayzstatic int
526c77a61ayzusbvc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
527c77a61ayz{
528c77a61ayz	int		instance = ddi_get_instance(dip);
529c77a61ayz	usbvc_state_t	*usbvcp = ddi_get_soft_state(usbvc_statep, instance);
530c77a61ayz	int		rval = USB_FAILURE;
531c77a61ayz
532c77a61ayz	switch (cmd) {
533c77a61ayz	case DDI_DETACH:
534c77a61ayz		mutex_enter(&usbvcp->usbvc_mutex);
535c77a61ayz		ASSERT((usbvcp->usbvc_drv_state & USBVC_OPEN) == 0);
536c77a61ayz		mutex_exit(&usbvcp->usbvc_mutex);
537c77a61ayz
538c77a61ayz		USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
539c77a61ayz		    "usbvc_detach: enter for detach");
540c77a61ayz
541c77a61ayz		usbvc_cleanup(dip, usbvcp);
542c77a61ayz		rval = USB_SUCCESS;
543c77a61ayz
544c77a61ayz		break;
545c77a61ayz	case DDI_SUSPEND:
546c77a61ayz		USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
547c77a61ayz		    "usbvc_detach: enter for suspend");
548c77a61ayz
549c77a61ayz		usbvc_cpr_suspend(dip);
550c77a61ayz		rval = USB_SUCCESS;
551c77a61ayz
552c77a61ayz		break;
553c77a61ayz	default:
554c77a61ayz
555c77a61ayz		break;
556c77a61ayz	}
557c77a61ayz
558c77a61ayz	return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
559c77a61ayz}
560c77a61ayz
561c77a61ayz
562c77a61ayz/*
563c77a61ayz * usbvc_cleanup:
564c77a61ayz *	clean up the driver state for detach
565c77a61ayz */
566c77a61ayzstatic void
567c77a61ayzusbvc_cleanup(dev_info_t *dip, usbvc_state_t *usbvcp)
568c77a61ayz{
569c77a61ayz	USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
570c77a61ayz	    "Cleanup: enter");
571c77a61ayz
572c77a61ayz	if (usbvcp->usbvc_locks_initialized) {
573c77a61ayz
574c77a61ayz		/* This must be done 1st to prevent more events from coming. */
575c77a61ayz		usb_unregister_event_cbs(dip, &usbvc_events);
576c77a61ayz
577c77a61ayz		/*
578c77a61ayz		 * At this point, no new activity can be initiated. The driver
579c77a61ayz		 * has disabled hotplug callbacks. The Solaris framework has
580c77a61ayz		 * disabled new opens on a device being detached, and does not
581c77a61ayz		 * allow detaching an open device.
582c77a61ayz		 *
583c77a61ayz		 * The following ensures that all driver activity has drained.
584c77a61ayz		 */
585c77a61ayz		mutex_enter(&usbvcp->usbvc_mutex);
586c77a61ayz		(void) usbvc_serialize_access(usbvcp, USBVC_SER_NOSIG);
587c77a61ayz		usbvc_release_access(usbvcp);
588c77a61ayz		mutex_exit(&usbvcp->usbvc_mutex);
589c77a61ayz
590c77a61ayz		/* All device activity has died down. */
591c77a61ayz		usbvc_destroy_power_mgmt(usbvcp);
592c77a61ayz		mutex_enter(&usbvcp->usbvc_mutex);
593c77a61ayz		usbvc_fini_lists(usbvcp);
594c77a61ayz		mutex_exit(&usbvcp->usbvc_mutex);
595c77a61ayz
596c77a61ayz		ddi_remove_minor_node(dip, NULL);
597c77a61ayz		usbvc_fini_sync_objs(usbvcp);
598c77a61ayz	}
599c77a61ayz
600c77a61ayz	usb_client_detach(dip, usbvcp->usbvc_reg);
601c77a61ayz	usb_free_log_hdl(usbvcp->usbvc_log_handle);
602c77a61ayz	ddi_soft_state_free(usbvc_statep, ddi_get_instance(dip));
603c77a61ayz	ddi_prop_remove_all(dip);
604c77a61ayz}
605c77a61ayz
606c77a61ayz
607c77a61ayz/*ARGSUSED*/
608c77a61ayzstatic int
609c77a61ayzusbvc_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
610c77a61ayz{
611c77a61ayz	usbvc_state_t	*usbvcp =
612ef772e5fb	    ddi_get_soft_state(usbvc_statep, getminor(*devp));
613c77a61ayz
614c77a61ayz	if (usbvcp == NULL) {
615c77a61ayz
616c77a61ayz		return (ENXIO);
617c77a61ayz	}
618c77a61ayz
619c77a61ayz	/*
620c77a61ayz	 * Keep it simple: one client at a time.
621c77a61ayz	 * Exclusive open only
622c77a61ayz	 */
623c77a61ayz	mutex_enter(&usbvcp->usbvc_mutex);
624c77a61ayz	USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
625c77a61ayz	    "usbvc_open: enter, dev_stat=%d", usbvcp->usbvc_dev_state);
626c77a61ayz
627c77a61ayz	if (usbvcp->usbvc_dev_state == USB_DEV_DISCONNECTED) {
628c77a61ayz		mutex_exit(&usbvcp->usbvc_mutex);
629c77a61ayz
630c77a61ayz		return (ENODEV);
631c77a61ayz	}
632c77a61ayz	if (usbvcp->usbvc_dev_state == USB_DEV_SUSPENDED) {
633c77a61ayz		mutex_exit(&usbvcp->usbvc_mutex);
634c77a61ayz
635c77a61ayz		return (EIO);
636c77a61ayz	}
637c77a61ayz	if ((usbvcp->usbvc_drv_state & USBVC_OPEN) != 0) {
638c77a61ayz		mutex_exit(&usbvcp->usbvc_mutex);
639c77a61ayz
640c77a61ayz		return (EBUSY);
641c77a61ayz	}
642c77a61ayz	usbvcp->usbvc_drv_state |= USBVC_OPEN;
643c77a61ayz
644c77a61ayz	if (usbvc_serialize_access(usbvcp, USBVC_SER_SIG) == 0) {
645c77a61ayz		usbvcp->usbvc_drv_state &= ~USBVC_OPEN;
646c77a61ayz		usbvcp->usbvc_serial_inuse = B_FALSE;
647c77a61ayz		mutex_exit(&usbvcp->usbvc_mutex);
648c77a61ayz
649c77a61ayz		return (EINTR);
650c77a61ayz	}
651c77a61ayz
652c77a61ayz	/* raise power */
653c77a61ayz	usbvc_pm_busy_component(usbvcp);
654c77a61ayz	if (usbvcp->usbvc_pm->usbvc_current_power != USB_DEV_OS_FULL_PWR) {
655c77a61ayz		usbvcp->usbvc_pm->usbvc_raise_power = B_TRUE;
656c77a61ayz		mutex_exit(&usbvcp->usbvc_mutex);
657c77a61ayz		(void) pm_raise_power(usbvcp->usbvc_dip,
658ef772e5fb		    0, USB_DEV_OS_FULL_PWR);
659c77a61ayz		mutex_enter(&usbvcp->usbvc_mutex);
660c77a61ayz		usbvcp->usbvc_pm->usbvc_raise_power = B_FALSE;
661c77a61ayz	}
662c77a61ayz
663c77a61ayz	/* Device is idle until it is used. */
664c77a61ayz	usbvc_release_access(usbvcp);
665c77a61ayz	mutex_exit(&usbvcp->usbvc_mutex);
666c77a61ayz
667c77a61ayz	USB_DPRINTF_L4(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
668c77a61ayz	    "usbvc_open: end.");
669c77a61ayz
670c77a61ayz	return (0);
671c77a61ayz}
672c77a61ayz
673c77a61ayz
674c77a61ayz/*ARGSUSED*/
675c77a61ayzstatic int
676c77a61ayzusbvc_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
677c77a61ayz{
678c77a61ayz	usbvc_stream_if_t *strm_if;
679c77a61ayz	int		if_num;
680c77a61ayz	usbvc_state_t	*usbvcp =
681c77a61ayz	    ddi_get_soft_state(usbvc_statep, getminor(dev));
682c77a61ayz
683c77a61ayz	USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
684c77a61ayz	    "close: enter");
685c77a61ayz
686c77a61ayz	mutex_enter(&usbvcp->usbvc_mutex);
687c77a61ayz	(void) usbvc_serialize_access(usbvcp, USBVC_SER_NOSIG);
688c77a61ayz	mutex_exit(&usbvcp->usbvc_mutex);
689c77a61ayz
690c77a61ayz	/* Perform device session cleanup here. */
691c77a61ayz
692c77a61ayz	USB_DPRINTF_L3(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
693c77a61ayz	    "close: cleaning up...");
694c77a61ayz
695c77a61ayz	/*
696c77a61ayz	 * USBA automatically flushes/resets active non-default pipes
697c77a61ayz	 * when they are closed.  We can't reset default pipe, but we
698c77a61ayz	 * can wait for all requests on it from this dip to drain.
699c77a61ayz	 */
700c77a61ayz	(void) usb_pipe_drain_reqs(usbvcp->usbvc_dip,
701c77a61ayz	    usbvcp->usbvc_reg->dev_default_ph, 0,
702c77a61ayz	    USB_FLAGS_SLEEP, NULL, 0);
703c77a61ayz
704c77a61ayz	mutex_enter(&usbvcp->usbvc_mutex);
705c77a61ayz	strm_if = usbvcp->usbvc_curr_strm;
706c77a61ayz	if (strm_if->start_polling == 1) {
707c77a61ayz		mutex_exit(&usbvcp->usbvc_mutex);
708c77a61ayz		usb_pipe_stop_isoc_polling(strm_if->datain_ph, USB_FLAGS_SLEEP);
709c77a61ayz		mutex_enter(&usbvcp->usbvc_mutex);
710c77a61ayz		strm_if->start_polling = 0;
711c77a61ayz	}
71209dd0d6Raymond Chen	strm_if->stream_on = 0;
71309dd0d6Raymond Chen
714c77a61ayz	usbvc_close_isoc_pipe(usbvcp, strm_if);
715c77a61ayz	if_num = strm_if->if_descr->if_alt->altif_descr.bInterfaceNumber;
716c77a61ayz	mutex_exit(&usbvcp->usbvc_mutex);
717c77a61ayz
718c77a61ayz	/* reset alternate to the default one. */
719c77a61ayz	(void) usb_set_alt_if(usbvcp->usbvc_dip, if_num, 0,
720c77a61ayz	    USB_FLAGS_SLEEP, NULL, NULL);
721c77a61ayz	mutex_enter(&usbvcp->usbvc_mutex);
722c77a61ayz
723c77a61ayz	usbvc_free_read_bufs(usbvcp, strm_if);
72461b2e29lc
72561b2e29lc	/* reset the desired read buf number to the default value on close */
72661b2e29lc	strm_if->buf_read_num = USBVC_DEFAULT_READ_BUF_NUM;
72761b2e29lc
728c77a61ayz	usbvc_free_map_bufs(usbvcp, strm_if);
729c77a61ayz	usbvcp->usbvc_drv_state &= ~USBVC_OPEN;
730c77a61ayz
731c77a61ayz	usbvc_release_access(usbvcp);
732c77a61ayz	usbvc_pm_idle_component(usbvcp);
733c77a61ayz	mutex_exit(&usbvcp->usbvc_mutex);
734c77a61ayz
735c77a61ayz	return (0);
736c77a61ayz}
737c77a61ayz
738c77a61ayz
739c77a61ayz/*ARGSUSED*/
740c77a61ayz/* Read isoc data from usb video devices */
741c77a61ayzstatic int
742c77a61ayzusbvc_read(dev_t dev, struct uio *uio_p, cred_t *cred_p)
743c77a61ayz{
744c77a61ayz	int			rval;
745c77a61ayz	usbvc_stream_if_t	*strm_if;
746c77a61ayz	usbvc_state_t	*usbvcp =
747ef772e5fb	    ddi_get_soft_state(usbvc_statep, getminor(dev));
748c77a61ayz
749c77a61ayz	USB_DPRINTF_L4(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
750c77a61ayz	    "usbvc_read: enter");
751c77a61ayz	mutex_enter(&usbvcp->usbvc_mutex);
752c77a61ayz	if (usbvcp->usbvc_dev_state != USB_DEV_ONLINE) {
753c77a61ayz		USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
754c77a61ayz		    "usbvc_read: Device is not available,"
755c77a61ayz		    " dev_stat=%d", usbvcp->usbvc_dev_state);
756c77a61ayz		mutex_exit(&usbvcp->usbvc_mutex);
757c77a61ayz
758c77a61ayz		return (EFAULT);
759c77a61ayz	}
760c77a61ayz	if ((uio_p->uio_fmode & (FNDELAY|FNONBLOCK)) &&
761c77a61ayz	    (usbvcp->usbvc_serial_inuse != B_FALSE)) {
762c77a61ayz		USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
763c77a61ayz		    "usbvc_read: non-blocking read, return fail.");
764c77a61ayz		mutex_exit(&usbvcp->usbvc_mutex);
765c77a61ayz
766c77a61ayz		return (EAGAIN);
767c77a61ayz	}
768c77a61ayz	if (usbvc_serialize_access(usbvcp, USBVC_SER_SIG) <= 0) {
769c77a61ayz		USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
770c77a61ayz		    "usbvc_read: serialize_access failed.");
771c77a61ayz		rval = EFAULT;
772c77a61ayz
773c77a61ayz		goto fail;
774c77a61ayz	}
775c77a61ayz
776c77a61ayz	/* Get the first stream interface */
777c77a61ayz	strm_if = usbvcp->usbvc_curr_strm;
778c77a61ayz	if (!strm_if) {
779c77a61ayz		USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
780c77a61ayz		    "usbvc_read: no stream interfaces");
781c77a61ayz		rval = EFAULT;
782c77a61ayz
783c77a61ayz		goto fail;
784c77a61ayz	}
785c77a61ayz
786c77a61ayz	/*
787c77a61ayz	 * If it is the first read, open isoc pipe and allocate bufs for
788c77a61ayz	 * read I/O method.
789c77a61ayz	 */
790c77a61ayz	if (strm_if->datain_ph == NULL) {
791c77a61ayz		if (usbvc_open_isoc_pipe(usbvcp, strm_if) != USB_SUCCESS) {
792c77a61ayz			USB_DPRINTF_L2(PRINT_MASK_READ,
793c77a61ayz			    usbvcp->usbvc_log_handle,
794c77a61ayz			    "usbvc_read: first read, open pipe fail");
795c77a61ayz			rval = EFAULT;
796c77a61ayz
797c77a61ayz			goto fail;
798c77a61ayz		}
799c77a61ayz		if (usbvc_alloc_read_bufs(usbvcp, strm_if) != USB_SUCCESS) {
800c77a61ayz			USB_DPRINTF_L2(PRINT_MASK_READ,
801c77a61ayz			    usbvcp->usbvc_log_handle,
802c77a61ayz			    "usbvc_read: allocate rw bufs fail");
803c77a61ayz			rval = EFAULT;
804c77a61ayz
805c77a61ayz			goto fail;
806c77a61ayz		}
807c77a61ayz	}
808c77a61ayz
809c77a61ayz	/* start polling if it is not started yet */
810c77a61ayz	if (strm_if->start_polling != 1) {
81111b5d82Toomas Soome		if (usbvc_start_isoc_polling(usbvcp, strm_if, 0) !=
812c77a61ayz		    USB_SUCCESS) {
813c77a61ayz			USB_DPRINTF_L2(PRINT_MASK_READ,
814c77a61ayz			    usbvcp->usbvc_log_handle,
815c77a61ayz			    "usbvc_read: usbvc_start_isoc_polling fail");
816c77a61ayz			rval = EFAULT;
817c77a61ayz
818c77a61ayz			goto fail;
819c77a61ayz		}
820c77a61ayz		strm_if->start_polling = 1;
821c77a61ayz	}
822c77a61ayz
823c77a61ayz	if (list_is_empty(&strm_if->buf_read.uv_buf_done)) {
824c77a61ayz		USB_DPRINTF_L3(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
825c77a61ayz		    "usbvc_read: full buf list is empty.");
826c77a61ayz
827c77a61ayz		if (uio_p->uio_fmode & (FNDELAY | FNONBLOCK)) {
828c77a61ayz			USB_DPRINTF_L2(PRINT_MASK_READ,
829c77a61ayz			    usbvcp->usbvc_log_handle, "usbvc_read: fail, "
830c77a61ayz			    "non-blocking read, done buf is empty.");
831c77a61ayz			rval = EAGAIN;
832c77a61ayz
833c77a61ayz			goto fail;
834c77a61ayz		}
835c77a61ayz
836c77a61ayz		/* no available buffers, block here */
837c77a61ayz		while (list_is_empty(&strm_if->buf_read.uv_buf_done)) {
838c77a61ayz			USB_DPRINTF_L3(PRINT_MASK_READ,
839c77a61ayz			    usbvcp->usbvc_log_handle,
840c77a61ayz			    "usbvc_read: wait for done buf");
841c77a61ayz			if (cv_wait_sig(&usbvcp->usbvc_read_cv,
842c77a61ayz			    &usbvcp->usbvc_mutex) <= 0) {
843c77a61ayz				/* no done buf and cv is signaled */
844c77a61ayz				rval = EINTR;
845c77a61ayz
846c77a61ayz				goto fail;
847c77a61ayz			}
848c77a61ayz			if (usbvcp->usbvc_dev_state != USB_DEV_ONLINE) {
849c77a61ayz
850c77a61ayz				/* Device is disconnected. */
851c77a61ayz				rval = EINTR;
852c77a61ayz
853c77a61ayz				goto fail;
854c77a61ayz			}
855c77a61ayz		}
856c77a61ayz
857c77a61ayz	}
858c77a61ayz
859c77a61ayz	mutex_exit(&usbvcp->usbvc_mutex);
860c77a61ayz	rval = physio(usbvc_strategy, NULL, dev, B_READ,
861ef772e5fb	    usbvc_minphys, uio_p);
862c77a61ayz
863c77a61ayz	mutex_enter(&usbvcp->usbvc_mutex);
864c77a61ayz	usbvc_release_access(usbvcp);
865c77a61ayz	mutex_exit(&usbvcp->usbvc_mutex);
866c77a61ayz
867c77a61ayz	return (rval);
868c77a61ayz
869c77a61ayzfail:
870c77a61ayz	usbvc_release_access(usbvcp);
871c77a61ayz	mutex_exit(&usbvcp->usbvc_mutex);
872c77a61ayz
873c77a61ayz	return (rval);
874c77a61ayz}
875c77a61ayz
876c77a61ayz
877c77a61ayz/*
878c77a61ayz * strategy:
879c77a61ayz *	Called through physio to setup and start the transfer.
880c77a61ayz */
881c77a61ayzstatic int
882c77a61ayzusbvc_strategy(struct buf *bp)
883c77a61ayz{
884c77a61ayz	usbvc_state_t *usbvcp = ddi_get_soft_state(usbvc_statep,
885ef772e5fb	    getminor(bp->b_edev));
886c77a61ayz
887c77a61ayz	USB_DPRINTF_L4(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
888c77a61ayz	    "usbvc_strategy: enter");
889c77a61ayz
890c77a61ayz	/*
891c77a61ayz	 * Initialize residual count here in case transfer doesn't even get
892c77a61ayz	 * started.
893c77a61ayz	 */
894c77a61ayz	bp->b_resid = bp->b_bcount;
895c77a61ayz
896c77a61ayz	/* Needed as this is a character driver. */
897c77a61ayz	if (bp->b_flags & (B_PHYS | B_PAGEIO)) {
898c77a61ayz		bp_mapin(bp);
899c77a61ayz	}
900c77a61ayz
901c77a61ayz	mutex_enter(&usbvcp->usbvc_mutex);
902c77a61ayz
903c77a61ayz	/* Make sure device has not been disconnected. */
904c77a61ayz	if (usbvcp->usbvc_dev_state != USB_DEV_ONLINE) {
905c77a61ayz		USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
906c77a61ayz		    "usbvc_strategy: device can't be accessed");
907c77a61ayz		mutex_exit(&usbvcp->usbvc_mutex);
908c77a61ayz
909c77a61ayz		goto fail;
910c77a61ayz	}
911c77a61ayz
912c77a61ayz	/* read data from uv_buf_done list */
913c77a61ayz	if (usbvc_read_buf(usbvcp, bp) != USB_SUCCESS) {
914c77a61ayz		USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
915c77a61ayz		    "usbvc_strategy: read full buf list fail");
916c77a61ayz		mutex_exit(&usbvcp->usbvc_mutex);
917c77a61ayz
918c77a61ayz		goto fail;
919c77a61ayz	}
920c77a61ayz
921c77a61ayz	mutex_exit(&usbvcp->usbvc_mutex);
922c77a61ayz
923c77a61ayz	biodone(bp);
924c77a61ayz
925c77a61ayz	return (0);
926c77a61ayz
927c77a61ayzfail:
928c77a61ayz	USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
929c77a61ayz	    "usbvc_strategy: strategy fail");
930c77a61ayz	bp->b_private = NULL;
931c77a61ayz
932c77a61ayz	bioerror(bp, EIO);
933c77a61ayz	biodone(bp);
934c77a61ayz
935c77a61ayz	return (0);
936c77a61ayz}
937c77a61ayz
938c77a61ayz
939c77a61ayzstatic void
940c77a61ayzusbvc_minphys(struct buf *bp)
941c77a61ayz{
942c77a61ayz	dev_t			dev = bp->b_edev;
943c77a61ayz	usbvc_stream_if_t	*strm_if;
944c77a61ayz	uint32_t		maxsize;
945ef772e5fb	usbvc_state_t		*usbvcp =
946ef772e5fb	    ddi_get_soft_state(usbvc_statep, getminor(dev));
947c77a61ayz
948c77a61ayz	mutex_enter(&usbvcp->usbvc_mutex);
949c77a61ayz	strm_if = usbvcp->usbvc_curr_strm;
950c77a61ayz	LE_TO_UINT32(strm_if->ctrl_pc.dwMaxVideoFrameSize, 0, maxsize);
951c77a61ayz	USB_DPRINTF_L3(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
952c77a61ayz	    "usbvc_minphys: max read size=%d", maxsize);
953c77a61ayz
954c77a61ayz	if (bp->b_bcount > maxsize) {
955c77a61ayz		bp->b_bcount = maxsize;
956c77a61ayz	}
957c77a61ayz	mutex_exit(&usbvcp->usbvc_mutex);
958c77a61ayz}
959c77a61ayz
960c77a61ayz
961c77a61ayz/*
962c77a61ayz * ioctl entry.
963c77a61ayz */
964c77a61ayz/*ARGSUSED*/
965c77a61ayzstatic int
966c77a61ayzusbvc_ioctl(dev_t dev, int cmd, intptr_t arg,
96711b5d82Toomas Soome    int mode, cred_t *cred_p, int *rval_p)
968c77a61ayz{
969c77a61ayz	int		rv = 0;
970c77a61ayz	usbvc_state_t	*usbvcp =
971c77a61ayz	    ddi_get_soft_state(usbvc_statep, getminor(dev));
972c77a61ayz
973c77a61ayz	if (usbvcp == NULL) {
974c77a61ayz
975c77a61ayz		return (ENXIO);
976c77a61ayz	}
977c77a61ayz	USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
978c77a61ayz	    "ioctl enter, cmd=%x", cmd);
979c77a61ayz	mutex_enter(&usbvcp->usbvc_mutex);
980c77a61ayz	if (usbvcp->usbvc_dev_state != USB_DEV_ONLINE) {
981c77a61ayz		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
982c77a61ayz		    "ioctl: Device is not online,"
983c77a61ayz		    " dev_stat=%d", usbvcp->usbvc_dev_state);
984c77a61ayz		mutex_exit(&usbvcp->usbvc_mutex);
985c77a61ayz
986c77a61ayz		return (EFAULT);
987c77a61ayz	}
988c77a61ayz	if (usbvc_serialize_access(usbvcp, USBVC_SER_SIG) <= 0) {
989c77a61ayz		usbvcp->usbvc_serial_inuse = B_FALSE;
990c77a61ayz		mutex_exit(&usbvcp->usbvc_mutex);
991c77a61ayz		USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
992c77a61ayz		    "serialize_access failed.");
993c77a61ayz
994c77a61ayz		return (EFAULT);
995c77a61ayz	}
996c77a61ayz	mutex_exit(&usbvcp->usbvc_mutex);
997c77a61ayz
998c77a61ayz	rv = usbvc_v4l2_ioctl(usbvcp, cmd, arg, mode);
999c77a61ayz
1000c77a61ayz	mutex_enter(&usbvcp->usbvc_mutex);
1001c77a61ayz	usbvc_release_access(usbvcp);
1002c77a61ayz	mutex_exit(&usbvcp->usbvc_mutex);
1003c77a61ayz
1004c77a61ayz	USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
1005c77a61ayz	    "usbvc_ioctl exit");
1006c77a61ayz
1007c77a61ayz	return (rv);
1008c77a61ayz}
1009c77a61ayz
1010c77a61ayz
1011c77a61ayz/* Entry for mmap system call */
1012c77a61ayzstatic int
1013c77a61ayzusbvc_devmap(dev_t dev, devmap_cookie_t handle, offset_t off,
101411b5d82Toomas Soome    size_t len, size_t *maplen, uint_t model)
1015c77a61ayz{
1016c77a61ayz	usbvc_state_t		*usbvcp;
1017c77a61ayz	int			error, i;
1018c77a61ayz	usbvc_buf_t		*buf = NULL;
1019c77a61ayz	usbvc_stream_if_t	*strm_if;
1020c77a61ayz	usbvc_buf_grp_t		*bufgrp;
1021c77a61ayz
1022c77a61ayz	usbvcp = ddi_get_soft_state(usbvc_statep, getminor(dev));
1023c77a61ayz	if (usbvcp == NULL) {
1024c77a61ayz		USB_DPRINTF_L2(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
1025c77a61ayz		    "usbvc_devmap: usbvcp == NULL");
1026c77a61ayz
1027c77a61ayz		return (ENXIO);
1028c77a61ayz	}
1029c77a61ayz
1030c77a61ayz	USB_DPRINTF_L3(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
1031112116dfb	    "devmap: memory map for instance(%d), off=%llx,"
1032112116dfb	    "len=%ld, maplen=%ld, model=%d", getminor(dev), off,
1033112116dfb	    len, *maplen, model);
1034c77a61ayz
1035c77a61ayz	mutex_enter(&usbvcp->usbvc_mutex);
1036c77a61ayz	(void) usbvc_serialize_access(usbvcp, USBVC_SER_NOSIG);
1037c77a61ayz	strm_if = usbvcp->usbvc_curr_strm;
1038c77a61ayz	if (!strm_if) {
1039c77a61ayz		USB_DPRINTF_L2(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
1040c77a61ayz		    "usbvc_devmap: No current strm if");
1041c77a61ayz		mutex_exit(&usbvcp->usbvc_mutex);
1042c77a61ayz
1043c77a61ayz		return (ENXIO);
1044c77a61ayz	}
1045c77a61ayz	bufgrp = &strm_if->buf_map;
1046c77a61ayz	for (i = 0; i < bufgrp->buf_cnt; i++) {
1047c77a61ayz		if (bufgrp->buf_head[i].v4l2_buf.m.offset == off) {
1048c77a61ayz			buf = &bufgrp->buf_head[i];
1049c77a61ayz
1050c77a61ayz			break;
1051c77a61ayz		}
1052c77a61ayz	}
1053c77a61ayz	USB_DPRINTF_L3(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
1054c77a61ayz	    "usbvc_devmap: idx=%d", i);
1055c77a61ayz	if (buf == NULL) {
1056c77a61ayz		mutex_exit(&usbvcp->usbvc_mutex);
1057c77a61ayz
1058c77a61ayz		return (ENXIO);
1059c77a61ayz	}
1060c77a61ayz	/*
1061c77a61ayz	 * round up len to a multiple of a page size, according to chapter
1062c77a61ayz	 * 10 of "writing device drivers"
1063c77a61ayz	 */
1064c77a61ayz	len = ptob(btopr(len));
1065c77a61ayz	if (len > ptob(btopr(buf->len))) {
1066c77a61ayz		USB_DPRINTF_L2(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
1067112116dfb		    "usbvc_devmap: len=0x%lx", len);
1068c77a61ayz		mutex_exit(&usbvcp->usbvc_mutex);
1069c77a61ayz
1070c77a61ayz		return (ENXIO);
1071c77a61ayz	}
1072c77a61ayz	mutex_exit(&usbvcp->usbvc_mutex);
1073c77a61ayz
1074c77a61ayz	error = devmap_umem_setup(handle, usbvcp->usbvc_dip, NULL,
1075ef772e5fb	    buf->umem_cookie, off, len, PROT_ALL, DEVMAP_DEFAULTS, NULL);
1076c77a61ayz	mutex_enter(&usbvcp->usbvc_mutex);
1077c77a61ayz	*maplen = len;
1078c77a61ayz	if (error == 0 && buf->status == USBVC_BUF_INIT) {
1079c77a61ayz		buf->status = USBVC_BUF_MAPPED;
1080c77a61ayz	} else {
1081c77a61ayz		USB_DPRINTF_L3(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
1082c77a61ayz		    "usbvc_devmap: devmap_umem_setup, err=%d", error);
1083c77a61ayz	}
1084c77a61ayz
1085c77a61ayz	(void) usbvc_release_access(usbvcp);
1086c77a61ayz	mutex_exit(&usbvcp->usbvc_mutex);
1087c77a61ayz
1088c77a61ayz	return (error);
1089c77a61ayz}
1090c77a61ayz
1091c77a61ayz/*
1092c77a61ayz * pm and cpr
1093c77a61ayz */
1094c77a61ayz
1095c77a61ayz/*
1096112116dfb *  usbvc_power :
1097c77a61ayz *	Power entry point, the workhorse behind pm_raise_power, pm_lower_power,
1098c77a61ayz *	usb_req_raise_power and usb_req_lower_power.
1099c77a61ayz */
1100c77a61ayz/* ARGSUSED */
1101c77a61ayzstatic int
1102c77a61ayzusbvc_power(dev_info_t *dip, int comp, int level)
1103c77a61ayz{
1104c77a61ayz	usbvc_state_t	*usbvcp;
1105c77a61ayz	usbvc_power_t	*pm;
1106c77a61ayz	int		rval = USB_FAILURE;
1107c77a61ayz
1108c77a61ayz	usbvcp = ddi_get_soft_state(usbvc_statep, ddi_get_instance(dip));
1109c77a61ayz	mutex_enter(&usbvcp->usbvc_mutex);
1110c77a61ayz	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1111c77a61ayz	    "usbvc_power: enter: level = %d, dev_state: %x",
1112c77a61ayz	    level, usbvcp->usbvc_dev_state);
1113c77a61ayz
1114c77a61ayz	if (usbvcp->usbvc_pm == NULL) {
1115c77a61ayz
1116c77a61ayz		goto done;
1117c77a61ayz	}
1118c77a61ayz
1119c77a61ayz	pm = usbvcp->usbvc_pm;
1120c77a61ayz
1121c77a61ayz	/* Check if we are transitioning to a legal power level */
1122c77a61ayz	if (USB_DEV_PWRSTATE_OK(pm->usbvc_pwr_states, level)) {
1123c77a61ayz		USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
1124c77a61ayz		    "usbvc_power: illegal power level = %d "
1125c77a61ayz		    "pwr_states: %x", level, pm->usbvc_pwr_states);
1126c77a61ayz
1127c77a61ayz		goto done;
1128c77a61ayz	}
1129c77a61ayz	/*
1130c77a61ayz	 * if we are about to raise power and asked to lower power, fail
1131c77a61ayz	 */
1132c77a61ayz	if (pm->usbvc_raise_power && (level < (int)pm->usbvc_current_power)) {
1133c77a61ayz
1134c77a61ayz		goto done;
1135c77a61ayz	}
1136c77a61ayz	switch (level) {
1137c77a61ayz	case USB_DEV_OS_PWR_OFF :
1138c77a61ayz		rval = usbvc_pwrlvl0(usbvcp);
1139c77a61ayz
1140c77a61ayz		break;
1141c77a61ayz	case USB_DEV_OS_PWR_1 :
1142c77a61ayz		rval = usbvc_pwrlvl1(usbvcp);
1143c77a61ayz
1144c77a61ayz		break;
1145c77a61ayz	case USB_DEV_OS_PWR_2 :
1146c77a61ayz		rval = usbvc_pwrlvl2(usbvcp);
1147c77a61ayz
1148c77a61ayz		break;
1149c77a61ayz	case USB_DEV_OS_FULL_PWR :
1150c77a61ayz		rval = usbvc_pwrlvl3(usbvcp);
1151c77a61ayz
1152c77a61ayz		break;
1153c77a61ayz	}
1154c77a61ayz
1155c77a61ayzdone:
1156c77a61ayz	mutex_exit(&usbvcp->usbvc_mutex);
1157c77a61ayz
1158c77a61ayz	return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
1159c77a61ayz}
1160c77a61ayz
1161c77a61ayz
1162c77a61ayz/*
1163c77a61ayz * usbvc_init_power_mgmt:
1164c77a61ayz *	Initialize power management and remote wakeup functionality.
1165c77a61ayz *	No mutex is necessary in this function as it's called only by attach.
1166c77a61ayz */
1167c77a61ayzstatic void
1168c77a61ayzusbvc_init_power_mgmt(usbvc_state_t *usbvcp)
1169c77a61ayz{
1170c77a61ayz	usbvc_power_t	*usbvcpm;
1171c77a61ayz	uint_t		pwr_states;
1172c77a61ayz
1173c77a61ayz	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1174c77a61ayz	    "init_power_mgmt enter");
1175c77a61ayz
1176c77a61ayz	/* Allocate the state structure */
1177c77a61ayz	usbvcpm = kmem_zalloc(sizeof (usbvc_power_t), KM_SLEEP);
1178c77a61ayz	mutex_enter(&usbvcp->usbvc_mutex);
1179c77a61ayz	usbvcp->usbvc_pm = usbvcpm;
1180c77a61ayz	usbvcpm->usbvc_state = usbvcp;
1181c77a61ayz	usbvcpm->usbvc_pm_capabilities = 0;
1182c77a61ayz	usbvcpm->usbvc_current_power = USB_DEV_OS_FULL_PWR;
1183c77a61ayz	mutex_exit(&usbvcp->usbvc_mutex);
1184c77a61ayz
1185c77a61ayz	if (usb_create_pm_components(usbvcp->usbvc_dip, &pwr_states) ==
1186ef772e5fb	    USB_SUCCESS) {
1187c77a61ayz		USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1188c77a61ayz		    "usbvc_init_power_mgmt: created PM components");
1189c77a61ayz
1190c77a61ayz		if (usb_handle_remote_wakeup(usbvcp->usbvc_dip,
1191c77a61ayz		    USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
1192c77a61ayz			usbvcpm->usbvc_wakeup_enabled = 1;
1193c77a61ayz		} else {
1194c77a61ayz			USB_DPRINTF_L2(PRINT_MASK_ATTA,
1195c77a61ayz			    usbvcp->usbvc_log_handle, "usbvc_init_power_mgmt:"
1196c77a61ayz			    " remote wakeup not supported");
1197c77a61ayz		}
1198c77a61ayz
1199c77a61ayz		mutex_enter(&usbvcp->usbvc_mutex);
1200c77a61ayz		usbvcpm->usbvc_pwr_states = (uint8_t)pwr_states;
1201c77a61ayz		usbvc_pm_busy_component(usbvcp);
1202c77a61ayz		usbvcpm->usbvc_raise_power = B_TRUE;
1203c77a61ayz		mutex_exit(&usbvcp->usbvc_mutex);
1204c77a61ayz
1205c77a61ayz		(void) pm_raise_power(
1206c77a61ayz		    usbvcp->usbvc_dip, 0, USB_DEV_OS_FULL_PWR);
1207c77a61ayz
1208c77a61ayz		mutex_enter(&usbvcp->usbvc_mutex);
1209c77a61ayz		usbvcpm->usbvc_raise_power = B_FALSE;
1210c77a61ayz		usbvc_pm_idle_component(usbvcp);
1211c77a61ayz		mutex_exit(&usbvcp->usbvc_mutex);
1212c77a61ayz
1213c77a61ayz	}
1214c77a61ayz	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1215c77a61ayz	    "usbvc_init_power_mgmt: end");
1216c77a61ayz}
1217c77a61ayz
1218c77a61ayz
1219c77a61ayz/*
1220112116dfb *  usbvc_destroy_power_mgmt:
1221c77a61ayz *	Shut down and destroy power management and remote wakeup functionality.
1222c77a61ayz */
1223c77a61ayzstatic void
1224c77a61ayzusbvc_destroy_power_mgmt(usbvc_state_t *usbvcp)
1225c77a61ayz{
1226c77a61ayz	usbvc_power_t	*pm;
1227c77a61ayz	int		rval;
1228c77a61ayz
1229c77a61ayz	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1230c77a61ayz	    "destroy_power_mgmt enter");
1231c77a61ayz	mutex_enter(&usbvcp->usbvc_mutex);
1232c77a61ayz	pm = usbvcp->usbvc_pm;
1233c77a61ayz	if (pm && (usbvcp->usbvc_dev_state != USB_DEV_DISCONNECTED)) {
1234c77a61ayz
1235c77a61ayz		usbvc_pm_busy_component(usbvcp);
1236c77a61ayz		if (pm->usbvc_wakeup_enabled) {
1237c77a61ayz			pm->usbvc_raise_power = B_TRUE;
1238c77a61ayz			mutex_exit(&usbvcp->usbvc_mutex);
1239c77a61ayz
1240c77a61ayz			/* First bring the device to full power */
1241c77a61ayz			(void) pm_raise_power(usbvcp->usbvc_dip, 0,
1242c77a61ayz			    USB_DEV_OS_FULL_PWR);
1243c77a61ayz			if ((rval = usb_handle_remote_wakeup(
1244c77a61ayz			    usbvcp->usbvc_dip,
1245c77a61ayz			    USB_REMOTE_WAKEUP_DISABLE)) !=
1246c77a61ayz			    USB_SUCCESS) {
1247c77a61ayz				USB_DPRINTF_L2(PRINT_MASK_ATTA,
1248c77a61ayz				    usbvcp->usbvc_log_handle,
1249c77a61ayz				    "usbvc_destroy_power_mgmt: "
1250c77a61ayz				    "Error disabling rmt wakeup: rval = %d",
1251c77a61ayz				    rval);
1252c77a61ayz			}
1253c77a61ayz			mutex_enter(&usbvcp->usbvc_mutex);
1254c77a61ayz			pm->usbvc_raise_power = B_FALSE;
1255c77a61ayz
1256c77a61ayz		}
1257c77a61ayz		mutex_exit(&usbvcp->usbvc_mutex);
1258c77a61ayz
1259c77a61ayz		/*
1260c77a61ayz		 * Since remote wakeup is disabled now,
1261c77a61ayz		 * no one can raise power
1262c77a61ayz		 * and get to device once power is lowered here.
1263c77a61ayz		 */
1264c77a61ayz		(void) pm_lower_power(usbvcp->usbvc_dip, 0, USB_DEV_OS_PWR_OFF);
1265c77a61ayz		mutex_enter(&usbvcp->usbvc_mutex);
1266c77a61ayz		usbvc_pm_idle_component(usbvcp);
1267c77a61ayz	}
1268c77a61ayz
1269c77a61ayz	if (pm) {
1270c77a61ayz		kmem_free(pm, sizeof (usbvc_power_t));
1271c77a61ayz		usbvcp->usbvc_pm = NULL;
1272c77a61ayz	}
1273c77a61ayz	mutex_exit(&usbvcp->usbvc_mutex);
1274c77a61ayz}
1275c77a61ayz
1276c77a61ayz
1277c77a61ayzstatic void
1278c77a61ayzusbvc_pm_busy_component(usbvc_state_t *usbvcp)
1279c77a61ayz{
1280c77a61ayz	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
1281c77a61ayz	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1282c77a61ayz	    "usbvc_pm_busy_component: enter");
1283c77a61ayz
1284c77a61ayz	usbvcp->usbvc_pm->usbvc_pm_busy++;
1285c77a61ayz	mutex_exit(&usbvcp->usbvc_mutex);
1286c77a61ayz
1287c77a61ayz	if (pm_busy_component(usbvcp->usbvc_dip, 0) !=
1288c77a61ayz	    DDI_SUCCESS) {
1289c77a61ayz		mutex_enter(&usbvcp->usbvc_mutex);
1290c77a61ayz		USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1291c77a61ayz		    "usbvc_pm_busy_component: pm busy fail, usbvc_pm_busy=%d",
1292c77a61ayz		    usbvcp->usbvc_pm->usbvc_pm_busy);
1293c77a61ayz
1294c77a61ayz		usbvcp->usbvc_pm->usbvc_pm_busy--;
1295c77a61ayz		mutex_exit(&usbvcp->usbvc_mutex);
1296c77a61ayz	}
1297c77a61ayz	mutex_enter(&usbvcp->usbvc_mutex);
1298c77a61ayz	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1299c77a61ayz	    "usbvc_pm_busy_component: exit");
1300c77a61ayz}
1301c77a61ayz
1302c77a61ayz
1303c77a61ayzstatic void
1304c77a61ayzusbvc_pm_idle_component(usbvc_state_t *usbvcp)
1305c77a61ayz{
1306c77a61ayz	ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
1307c77a61ayz	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1308c77a61ayz	    "usbvc_pm_idle_component: enter");
1309c77a61ayz
1310c77a61ayz	if (usbvcp->usbvc_pm != NULL) {
1311c77a61ayz		mutex_exit(&usbvcp->usbvc_mutex);
1312c77a61ayz		if (pm_idle_component(usbvcp->usbvc_dip, 0) ==
1313c77a61ayz		    DDI_SUCCESS) {
1314c77a61ayz			mutex_enter(&usbvcp->usbvc_mutex);
1315c77a61ayz			ASSERT(usbvcp->usbvc_pm->usbvc_pm_busy > 0);
1316c77a61ayz			usbvcp->usbvc_pm->usbvc_pm_busy--;
1317c77a61ayz			mutex_exit(&usbvcp->usbvc_mutex);
1318c77a61ayz		}
1319c77a61ayz		mutex_enter(&usbvcp->usbvc_mutex);
1320c77a61ayz		USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1321c77a61ayz		    "usbvc_pm_idle_component: %d",
1322c77a61ayz		    usbvcp->usbvc_pm->usbvc_pm_busy);
1323c77a61ayz	}
1324c77a61ayz}
1325c77a61ayz
1326c77a61ayz
1327c77a61ayz/*
1328c77a61ayz * usbvc_pwrlvl0:
1329c77a61ayz * Functions to handle power transition for OS levels 0 -> 3
1330c77a61ayz */
1331c77a61ayzstatic int
1332c77a61ayzusbvc_pwrlvl0(usbvc_state_t *usbvcp)
1333c77a61ayz{
1334c77a61ayz	int rval;
1335c77a61ayz
1336c77a61ayz	USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1337c77a61ayz	    "usbvc_pwrlvl0, dev_state: %x", usbvcp->usbvc_dev_state);
1338c77a61ayz
1339c77a61ayz	switch (usbvcp->usbvc_dev_state) {
1340c77a61ayz	case USB_DEV_ONLINE:
1341c77a61ayz		/* Deny the powerdown request if the device is busy */
1342c77a61ayz		if (usbvcp->usbvc_pm->usbvc_pm_busy != 0) {
134315c07adJohn Levon			USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
134415c07adJohn Levon			    "usbvc_pwrlvl0: usbvc_pm_busy");
1345c77a61ayz
1346c77a61ayz			return (USB_FAILURE);
1347c77a61ayz		}
1348c77a61ayz
1349c77a61ayz		/* Issue USB D3 command to the device here */
1350c77a61ayz		rval = usb_set_device_pwrlvl3(usbvcp->usbvc_dip);
1351c77a61ayz		ASSERT(rval == USB_SUCCESS);
1352c77a61ayz
1353c77a61ayz		usbvcp->usbvc_dev_state = USB_DEV_PWRED_DOWN;
1354ef772e5fb		usbvcp->usbvc_pm->usbvc_current_power = USB_DEV_OS_PWR_OFF;
1355ef772e5fb
1356c77a61ayz		/* FALLTHRU */
1357c77a61ayz	case USB_DEV_DISCONNECTED:
1358c77a61ayz	case USB_DEV_SUSPENDED:
1359c77a61ayz		/* allow a disconnect/cpr'ed device to go to lower power */
1360c77a61ayz
1361c77a61ayz		return (USB_SUCCESS);
1362c77a61ayz	case USB_DEV_PWRED_DOWN:
1363c77a61ayz	default:
1364c77a61ayz		USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1365