1c77a61a7Syz /*
2c77a61a7Syz * CDDL HEADER START
3c77a61a7Syz *
4c77a61a7Syz * The contents of this file are subject to the terms of the
5c77a61a7Syz * Common Development and Distribution License (the "License").
6c77a61a7Syz * You may not use this file except in compliance with the License.
7c77a61a7Syz *
8c77a61a7Syz * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9c77a61a7Syz * or http://www.opensolaris.org/os/licensing.
10c77a61a7Syz * See the License for the specific language governing permissions
11c77a61a7Syz * and limitations under the License.
12c77a61a7Syz *
13c77a61a7Syz * When distributing Covered Code, include this CDDL HEADER in each
14c77a61a7Syz * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15c77a61a7Syz * If applicable, add the following below this CDDL HEADER, with the
16c77a61a7Syz * fields enclosed by brackets "[]" replaced with your own identifying
17c77a61a7Syz * information: Portions Copyright [yyyy] [name of copyright owner]
18c77a61a7Syz *
19c77a61a7Syz * CDDL HEADER END
20c77a61a7Syz */
21c77a61a7Syz /*
226847c243SRaymond Chen * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
23c77a61a7Syz */
24c77a61a7Syz
2515c07adcSJohn Levon /*
2615c07adcSJohn Levon * Copyright (c) 2018, Joyent, Inc.
2715c07adcSJohn Levon */
28c77a61a7Syz
29c77a61a7Syz /*
30*bbf21555SRichard Lowe * USB video class driver (usbvc(4D))
31c77a61a7Syz *
32c77a61a7Syz * 1. Overview
33c77a61a7Syz * ------------
34c77a61a7Syz *
35c77a61a7Syz * This driver supports USB video class devices that used to capture video,
36c77a61a7Syz * e.g., some webcams. It is developed according to "USB Device Class
37c77a61a7Syz * Definition for Video Devices" spec. This spec defines detail info needed by
38c77a61a7Syz * designing a USB video device. It is available at:
39c77a61a7Syz * http://www.usb.org/developers/devclass_docs
40c77a61a7Syz *
41c77a61a7Syz * This driver implements:
42c77a61a7Syz *
43c77a61a7Syz * - V4L2 interfaces for applications to communicate with video devices.
44c77a61a7Syz * V4L2 is an API that is widely used by video applications, like Ekiga,
45c77a61a7Syz * luvcview, etc. The API spec is at:
46c77a61a7Syz * http://www.thedirks.org/v4l2/
47c77a61a7Syz * This driver is according to V4L2 spec version 0.20
48c77a61a7Syz *
49c77a61a7Syz * - Video capture function. (Video output is not supported by now.)
50c77a61a7Syz *
51c77a61a7Syz * - Isochronous transfer for video data. (Bulk transfer is not supported.)
52c77a61a7Syz *
53c77a61a7Syz * - read & mmap I/O methods for userland video applications to get video
54c77a61a7Syz * data. Userland video applications can use read() system call directly,
55c77a61a7Syz * it is the simplest way but not the most efficient way. Applications can
56c77a61a7Syz * also use mmap() system call to map several bufs (they are linked as a
57c77a61a7Syz * buf list), and then use some specific ioctls to start/stop isoc polling,
58c77a61a7Syz * to queue/dequeue bufs.
59c77a61a7Syz *
60c77a61a7Syz * 2. Source and header files
61c77a61a7Syz * ---------------------------
62c77a61a7Syz *
63c77a61a7Syz * There are two source files and three header files for this driver:
64c77a61a7Syz *
65c77a61a7Syz * - usbvc.c Main source file, implements usb video class spec.
66c77a61a7Syz *
67c77a61a7Syz * - usbvc_v4l2.c V4L2 interface specific code.
68c77a61a7Syz *
69c77a61a7Syz * - usbvc_var.h Main header file, includes soft state structure.
70c77a61a7Syz *
71c77a61a7Syz * - usbvc.h The descriptors in usb video class spec.
72c77a61a7Syz *
73c77a61a7Syz * - videodev2.h This header file is included in V4L2 spec. It defines
74c77a61a7Syz * ioctls and data structures that used as an interface between video
75c77a61a7Syz * applications and video drivers. This is the only header file that
76c77a61a7Syz * usbvc driver should export to userland application.
77c77a61a7Syz *
78c77a61a7Syz * 3. USB video class devices overview
79c77a61a7Syz * -----------------------------------
80c77a61a7Syz * According to UVC spec, there must be one control interface in a UVC device.
81c77a61a7Syz * Control interface is used to receive control commands from user, all the
82c77a61a7Syz * commands are sent through default ctrl pipe. usbvc driver implements V4L2
83c77a61a7Syz * API, so ioctls are implemented to relay user commands to UVC device.
84c77a61a7Syz *
85c77a61a7Syz * There can be no or multiple stream interfaces in a UVC device. Stream
86c77a61a7Syz * interfaces are used to do video data I/O. In practice, if no stream
87c77a61a7Syz * interface, the video device can do nothing since it has no data I/O.
88c77a61a7Syz *
89c77a61a7Syz * usbvc driver parses descriptors of control interface and stream interfaces.
90c77a61a7Syz * The descriptors tell the function layout and the capability of the device.
91c77a61a7Syz * During attach, usbvc driver set up some key data structures according to
92c77a61a7Syz * the descriptors.
93c77a61a7Syz *
94c77a61a7Syz * 4. I/O methods
95c77a61a7Syz * ---------------
96c77a61a7Syz *
97c77a61a7Syz * Userland applications use ioctls to set/get video formats of the device,
98c77a61a7Syz * and control brightness, contrast, image size, etc.
99c77a61a7Syz *
100c77a61a7Syz * Besides implementing standard read I/O method to get video data from
101c77a61a7Syz * the device, usbvc driver also implements some specific ioctls to implement
102c77a61a7Syz * mmap I/O method.
103c77a61a7Syz *
104c77a61a7Syz * A view from userland application: ioctl and mmap flow chart:
105c77a61a7Syz *
106c77a61a7Syz * REQBUFS -> QUERYBUF -> mmap() ->
107c77a61a7Syz *
108c77a61a7Syz * -> QBUF -> STREAMON -> DQBUF -> process image -> QBUF
109c77a61a7Syz * ^ |
110c77a61a7Syz * | |
111c77a61a7Syz * | v
112c77a61a7Syz * |---<--------------------
113c77a61a7Syz *
114c77a61a7Syz * The above queue and dequeue buf operations can be stopped by issuing a
115c77a61a7Syz * STREAMOFF ioctl.
116c77a61a7Syz *
117c77a61a7Syz * 5. Device states
118c77a61a7Syz * ----------------
119c77a61a7Syz *
120c77a61a7Syz * The device has four states (refer to usbai.h):
121c77a61a7Syz *
122c77a61a7Syz * - USB_DEV_ONLINE: In action or ready for action.
123c77a61a7Syz *
124c77a61a7Syz * - USB_DEV_DISCONNECTED: Hotplug removed, or device not present/correct
125c77a61a7Syz * on resume (CPR).
126c77a61a7Syz *
127c77a61a7Syz * - USB_DEV_SUSPENDED: Device has been suspended along with the system.
128c77a61a7Syz *
129c77a61a7Syz * - USB_DEV_PWRED_DOWN: Device has been powered down. (Note that this
130c77a61a7Syz * driver supports only two power states, powered down and
131c77a61a7Syz * full power.)
132c77a61a7Syz *
133c77a61a7Syz * 6. Serialize
134c77a61a7Syz * -------------
135c77a61a7Syz * In order to avoid race conditions between driver entry points, access to
136c77a61a7Syz * the device is serialized. All the ioctls, and read, open/close are
137c77a61a7Syz * serialized. The functions usbvc_serialize/release_access are implemented
138c77a61a7Syz * for this purpose.
139c77a61a7Syz *
140c77a61a7Syz * 7. PM & CPR
141c77a61a7Syz * ------------
142c77a61a7Syz * PM & CPR are supported. pm_busy_component and pm_idle_component mark
143c77a61a7Syz * the device as busy or idle to the system.
144c77a61a7Syz */
145c77a61a7Syz
146c77a61a7Syz #if defined(lint) && !defined(DEBUG)
147c77a61a7Syz #define DEBUG
148c77a61a7Syz #endif
149c77a61a7Syz
150c77a61a7Syz #define USBDRV_MAJOR_VER 2
151c77a61a7Syz #define USBDRV_MINOR_VER 0
152c77a61a7Syz
153c77a61a7Syz #include <sys/usb/usba.h>
154c77a61a7Syz #include <sys/fcntl.h>
155c77a61a7Syz #include <sys/cmn_err.h>
156c77a61a7Syz #include <sys/usb/clients/video/usbvc/usbvc_var.h>
157c77a61a7Syz #include <sys/videodev2.h> /* V4L2 API header file */
158c77a61a7Syz
159c77a61a7Syz /* Descriptors according to USB video class spec */
160c77a61a7Syz #include <sys/usb/clients/video/usbvc/usbvc.h>
161c77a61a7Syz
162c77a61a7Syz static uint_t usbvc_errmask = (uint_t)PRINT_MASK_ALL;
163c77a61a7Syz static uint_t usbvc_errlevel = 4;
164c77a61a7Syz static uint_t usbvc_instance_debug = (uint_t)-1;
165c77a61a7Syz
166c77a61a7Syz static char *name = "usbvc"; /* Driver name, used all over. */
167c77a61a7Syz
168c77a61a7Syz /*
169c77a61a7Syz * Function Prototypes
170c77a61a7Syz */
171c77a61a7Syz
172c77a61a7Syz /* Entries */
173c77a61a7Syz static int usbvc_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
174c77a61a7Syz static int usbvc_attach(dev_info_t *, ddi_attach_cmd_t);
175c77a61a7Syz static int usbvc_detach(dev_info_t *, ddi_detach_cmd_t);
176c77a61a7Syz static void usbvc_cleanup(dev_info_t *, usbvc_state_t *);
177c77a61a7Syz static int usbvc_open(dev_t *, int, int, cred_t *);
178c77a61a7Syz static int usbvc_close(dev_t, int, int, cred_t *);
179c77a61a7Syz static int usbvc_read(dev_t, struct uio *uip_p, cred_t *);
180c77a61a7Syz static int usbvc_strategy(struct buf *);
181c77a61a7Syz static void usbvc_minphys(struct buf *);
182c77a61a7Syz static int usbvc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
183c77a61a7Syz static int usbvc_devmap(dev_t, devmap_cookie_t, offset_t,
184c77a61a7Syz size_t, size_t *, uint_t);
185c77a61a7Syz
186c77a61a7Syz /* pm and cpr */
187c77a61a7Syz static int usbvc_power(dev_info_t *, int, int);
188c77a61a7Syz static void usbvc_init_power_mgmt(usbvc_state_t *);
189c77a61a7Syz static void usbvc_destroy_power_mgmt(usbvc_state_t *);
190c77a61a7Syz static void usbvc_pm_busy_component(usbvc_state_t *);
191c77a61a7Syz static void usbvc_pm_idle_component(usbvc_state_t *);
192c77a61a7Syz static int usbvc_pwrlvl0(usbvc_state_t *);
193c77a61a7Syz static int usbvc_pwrlvl1(usbvc_state_t *);
194c77a61a7Syz static int usbvc_pwrlvl2(usbvc_state_t *);
195c77a61a7Syz static int usbvc_pwrlvl3(usbvc_state_t *);
196c77a61a7Syz static void usbvc_cpr_suspend(dev_info_t *);
197c77a61a7Syz static void usbvc_cpr_resume(dev_info_t *);
198c77a61a7Syz static void usbvc_restore_device_state(dev_info_t *, usbvc_state_t *);
199c77a61a7Syz
200c77a61a7Syz /* Events */
201c77a61a7Syz static int usbvc_disconnect_event_cb(dev_info_t *);
202c77a61a7Syz static int usbvc_reconnect_event_cb(dev_info_t *);
203c77a61a7Syz
204c77a61a7Syz /* Sync objs and lists */
205c77a61a7Syz static void usbvc_init_sync_objs(usbvc_state_t *);
206c77a61a7Syz static void usbvc_fini_sync_objs(usbvc_state_t *);
207c77a61a7Syz static void usbvc_init_lists(usbvc_state_t *);
208c77a61a7Syz static void usbvc_fini_lists(usbvc_state_t *);
209c77a61a7Syz static void usbvc_free_ctrl_descr(usbvc_state_t *);
210c77a61a7Syz static void usbvc_free_stream_descr(usbvc_state_t *);
211c77a61a7Syz
212c77a61a7Syz /* Parse descriptors */
213c77a61a7Syz static int usbvc_chk_descr_len(uint8_t, uint8_t, uint8_t,
214c77a61a7Syz usb_cvs_data_t *);
215c77a61a7Syz static usbvc_stream_if_t *usbvc_parse_stream_if(usbvc_state_t *, int);
216c77a61a7Syz static int usbvc_parse_ctrl_if(usbvc_state_t *);
217c77a61a7Syz static int usbvc_parse_stream_ifs(usbvc_state_t *);
218c77a61a7Syz static void usbvc_parse_color_still(usbvc_state_t *,
219c77a61a7Syz usbvc_format_group_t *, usb_cvs_data_t *, uint_t, uint_t);
220c77a61a7Syz static void usbvc_parse_frames(usbvc_state_t *, usbvc_format_group_t *,
221c77a61a7Syz usb_cvs_data_t *, uint_t, uint_t);
222c77a61a7Syz static int usbvc_parse_format_group(usbvc_state_t *,
223c77a61a7Syz usbvc_format_group_t *, usb_cvs_data_t *, uint_t, uint_t);
224c77a61a7Syz static int usbvc_parse_format_groups(usbvc_state_t *, usbvc_stream_if_t *);
225c77a61a7Syz static int usbvc_parse_stream_header(usbvc_state_t *, usbvc_stream_if_t *);
226c77a61a7Syz
227c77a61a7Syz /* read I/O functions */
228c77a61a7Syz static int usbvc_alloc_read_bufs(usbvc_state_t *, usbvc_stream_if_t *);
229c77a61a7Syz static int usbvc_read_buf(usbvc_state_t *, struct buf *);
230c77a61a7Syz static void usbvc_free_read_buf(usbvc_buf_t *);
231c77a61a7Syz static void usbvc_free_read_bufs(usbvc_state_t *, usbvc_stream_if_t *);
232c77a61a7Syz static void usbvc_close_isoc_pipe(usbvc_state_t *, usbvc_stream_if_t *);
233c77a61a7Syz
234c77a61a7Syz /* callbacks */
235c77a61a7Syz static void usbvc_isoc_cb(usb_pipe_handle_t, usb_isoc_req_t *);
236c77a61a7Syz static void usbvc_isoc_exc_cb(usb_pipe_handle_t, usb_isoc_req_t *);
237c77a61a7Syz
238c77a61a7Syz /* Others */
239c77a61a7Syz static int usbvc_set_alt(usbvc_state_t *, usbvc_stream_if_t *);
240c77a61a7Syz static int usbvc_decode_stream_header(usbvc_state_t *, usbvc_buf_grp_t *,
241c77a61a7Syz mblk_t *, int);
242c77a61a7Syz static int usbvc_serialize_access(usbvc_state_t *, boolean_t);
243c77a61a7Syz static void usbvc_release_access(usbvc_state_t *);
244c77a61a7Syz static int usbvc_set_default_stream_fmt(usbvc_state_t *);
245c77a61a7Syz
246c77a61a7Syz static usb_event_t usbvc_events = {
247c77a61a7Syz usbvc_disconnect_event_cb,
248c77a61a7Syz usbvc_reconnect_event_cb,
249c77a61a7Syz NULL, NULL
250c77a61a7Syz };
251c77a61a7Syz
252c77a61a7Syz /* module loading stuff */
253c77a61a7Syz struct cb_ops usbvc_cb_ops = {
254c77a61a7Syz usbvc_open, /* open */
255c77a61a7Syz usbvc_close, /* close */
256c77a61a7Syz usbvc_strategy, /* strategy */
257c77a61a7Syz nulldev, /* print */
258c77a61a7Syz nulldev, /* dump */
259c77a61a7Syz usbvc_read, /* read */
260c77a61a7Syz nodev, /* write */
261c77a61a7Syz usbvc_ioctl, /* ioctl */
262c77a61a7Syz usbvc_devmap, /* devmap */
263c77a61a7Syz nodev, /* mmap */
264c77a61a7Syz ddi_devmap_segmap, /* segmap */
265c77a61a7Syz nochpoll, /* poll */
266c77a61a7Syz ddi_prop_op, /* cb_prop_op */
267c77a61a7Syz NULL, /* streamtab */
268c77a61a7Syz D_MP | D_DEVMAP
269c77a61a7Syz };
270c77a61a7Syz
271c77a61a7Syz static struct dev_ops usbvc_ops = {
272c77a61a7Syz DEVO_REV, /* devo_rev, */
273c77a61a7Syz 0, /* refcnt */
274c77a61a7Syz usbvc_info, /* info */
275c77a61a7Syz nulldev, /* identify */
276c77a61a7Syz nulldev, /* probe */
277c77a61a7Syz usbvc_attach, /* attach */
278c77a61a7Syz usbvc_detach, /* detach */
279c77a61a7Syz nodev, /* reset */
280c77a61a7Syz &usbvc_cb_ops, /* driver operations */
281c77a61a7Syz NULL, /* bus operations */
28219397407SSherry Moore usbvc_power, /* power */
28309dd0d6cSRaymond Chen ddi_quiesce_not_needed, /* quiesce */
284c77a61a7Syz };
285c77a61a7Syz
286c77a61a7Syz static struct modldrv usbvc_modldrv = {
287c77a61a7Syz &mod_driverops,
28861b2e298Slc "USB video class driver",
289c77a61a7Syz &usbvc_ops
290c77a61a7Syz };
291c77a61a7Syz
292c77a61a7Syz static struct modlinkage modlinkage = {
293c77a61a7Syz MODREV_1,
294c77a61a7Syz &usbvc_modldrv,
295c77a61a7Syz NULL
296c77a61a7Syz };
297c77a61a7Syz
298c77a61a7Syz /* Soft state structures */
299c77a61a7Syz #define USBVC_INITIAL_SOFT_SPACE 1
300c77a61a7Syz static void *usbvc_statep;
301c77a61a7Syz
302c77a61a7Syz
303c77a61a7Syz /*
304c77a61a7Syz * Module-wide initialization routine.
305c77a61a7Syz */
306c77a61a7Syz int
_init(void)307c77a61a7Syz _init(void)
308c77a61a7Syz {
309c77a61a7Syz int rval;
310c77a61a7Syz
311c77a61a7Syz if ((rval = ddi_soft_state_init(&usbvc_statep,
312c77a61a7Syz sizeof (usbvc_state_t), USBVC_INITIAL_SOFT_SPACE)) != 0) {
313c77a61a7Syz
314c77a61a7Syz return (rval);
315c77a61a7Syz }
316c77a61a7Syz
317c77a61a7Syz if ((rval = mod_install(&modlinkage)) != 0) {
318c77a61a7Syz ddi_soft_state_fini(&usbvc_statep);
319c77a61a7Syz }
320c77a61a7Syz
321c77a61a7Syz return (rval);
322c77a61a7Syz }
323c77a61a7Syz
324c77a61a7Syz
325c77a61a7Syz /*
326c77a61a7Syz * Module-wide tear-down routine.
327c77a61a7Syz */
328c77a61a7Syz int
_fini(void)329c77a61a7Syz _fini(void)
330c77a61a7Syz {
331c77a61a7Syz int rval;
332c77a61a7Syz
333c77a61a7Syz if ((rval = mod_remove(&modlinkage)) != 0) {
334c77a61a7Syz
335c77a61a7Syz return (rval);
336c77a61a7Syz }
337c77a61a7Syz
338c77a61a7Syz ddi_soft_state_fini(&usbvc_statep);
339c77a61a7Syz
340c77a61a7Syz return (rval);
341c77a61a7Syz }
342c77a61a7Syz
343c77a61a7Syz
344c77a61a7Syz int
_info(struct modinfo * modinfop)345c77a61a7Syz _info(struct modinfo *modinfop)
346c77a61a7Syz {
347c77a61a7Syz return (mod_info(&modlinkage, modinfop));
348c77a61a7Syz }
349c77a61a7Syz
350c77a61a7Syz
351c77a61a7Syz /*
352c77a61a7Syz * usbvc_info:
353c77a61a7Syz * Get minor number, soft state structure, etc.
354c77a61a7Syz */
355c77a61a7Syz /*ARGSUSED*/
356c77a61a7Syz static int
usbvc_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)357c77a61a7Syz usbvc_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
35811b5d82fSToomas Soome void *arg, void **result)
359c77a61a7Syz {
360c77a61a7Syz usbvc_state_t *usbvcp;
361c77a61a7Syz int error = DDI_FAILURE;
362c77a61a7Syz
363c77a61a7Syz switch (infocmd) {
364c77a61a7Syz case DDI_INFO_DEVT2DEVINFO:
365c77a61a7Syz if ((usbvcp = ddi_get_soft_state(usbvc_statep,
366c77a61a7Syz getminor((dev_t)arg))) != NULL) {
367c77a61a7Syz *result = usbvcp->usbvc_dip;
368c77a61a7Syz if (*result != NULL) {
369c77a61a7Syz error = DDI_SUCCESS;
370c77a61a7Syz }
371c77a61a7Syz } else {
372c77a61a7Syz *result = NULL;
373c77a61a7Syz }
374c77a61a7Syz break;
375c77a61a7Syz case DDI_INFO_DEVT2INSTANCE:
376c77a61a7Syz *result = (void *)(uintptr_t)getminor((dev_t)arg);
377c77a61a7Syz error = DDI_SUCCESS;
378c77a61a7Syz break;
379c77a61a7Syz default:
380c77a61a7Syz break;
381c77a61a7Syz }
382c77a61a7Syz
383c77a61a7Syz return (error);
384c77a61a7Syz }
385c77a61a7Syz
386c77a61a7Syz
387c77a61a7Syz /*
388c77a61a7Syz * Entry functions.
389c77a61a7Syz */
390c77a61a7Syz
391c77a61a7Syz /*
392c77a61a7Syz * usbvc_attach:
393c77a61a7Syz * Attach or resume.
394c77a61a7Syz *
395c77a61a7Syz * For attach, initialize state and device, including:
396c77a61a7Syz * state variables, locks, device node
397c77a61a7Syz * device registration with system
398c77a61a7Syz * power management, hotplugging
399c77a61a7Syz * For resume, restore device and state
400c77a61a7Syz */
401c77a61a7Syz static int
usbvc_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)402c77a61a7Syz usbvc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
403c77a61a7Syz {
404c77a61a7Syz int instance = ddi_get_instance(dip);
405c77a61a7Syz usbvc_state_t *usbvcp = NULL;
406c77a61a7Syz int status;
407c77a61a7Syz
408c77a61a7Syz switch (cmd) {
409c77a61a7Syz case DDI_ATTACH:
410c77a61a7Syz
411c77a61a7Syz break;
412c77a61a7Syz case DDI_RESUME:
413c77a61a7Syz usbvc_cpr_resume(dip);
414c77a61a7Syz
415c77a61a7Syz return (DDI_SUCCESS);
416c77a61a7Syz default:
417c77a61a7Syz
418c77a61a7Syz return (DDI_FAILURE);
419c77a61a7Syz }
420c77a61a7Syz
421c77a61a7Syz if (ddi_soft_state_zalloc(usbvc_statep, instance) == DDI_SUCCESS) {
422c77a61a7Syz usbvcp = ddi_get_soft_state(usbvc_statep, instance);
423c77a61a7Syz }
424c77a61a7Syz if (usbvcp == NULL) {
425c77a61a7Syz
426c77a61a7Syz return (DDI_FAILURE);
427c77a61a7Syz }
428c77a61a7Syz
429c77a61a7Syz usbvcp->usbvc_dip = dip;
430c77a61a7Syz
431c77a61a7Syz usbvcp->usbvc_log_handle = usb_alloc_log_hdl(dip,
432ef772e54Sfb "usbvc", &usbvc_errlevel,
433ef772e54Sfb &usbvc_errmask, &usbvc_instance_debug, 0);
434c77a61a7Syz
435c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
436ef772e54Sfb "usbvc_attach: enter");
437c77a61a7Syz
438c77a61a7Syz if ((status = usb_client_attach(dip, USBDRV_VERSION, 0)) !=
439c77a61a7Syz USB_SUCCESS) {
440c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
441c77a61a7Syz "usbvc_attach: usb_client_attach failed, error code:%d",
442c77a61a7Syz status);
443c77a61a7Syz
444c77a61a7Syz goto fail;
445c77a61a7Syz }
446c77a61a7Syz
447c77a61a7Syz if ((status = usb_get_dev_data(dip, &usbvcp->usbvc_reg,
448c77a61a7Syz USB_PARSE_LVL_ALL, 0)) != USB_SUCCESS) {
449c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
450c77a61a7Syz "usbvc_attach: usb_get_dev_data failed, error code:%d",
451c77a61a7Syz status);
452c77a61a7Syz
453c77a61a7Syz goto fail;
454c77a61a7Syz }
455c77a61a7Syz usbvc_init_sync_objs(usbvcp);
456c77a61a7Syz
457c77a61a7Syz /* create minor node */
458c77a61a7Syz if ((status = ddi_create_minor_node(dip, name, S_IFCHR, instance,
459c77a61a7Syz "usb_video", 0)) != DDI_SUCCESS) {
460c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
461c77a61a7Syz "usbvc_attach: Error creating minor node, error code:%d",
462c77a61a7Syz status);
463c77a61a7Syz
464c77a61a7Syz goto fail;
465c77a61a7Syz }
466c77a61a7Syz
467c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
468c77a61a7Syz usbvc_init_lists(usbvcp);
469c77a61a7Syz
470c77a61a7Syz usbvcp->usbvc_default_ph = usbvcp->usbvc_reg->dev_default_ph;
471c77a61a7Syz
472c77a61a7Syz /* Put online before PM init as can get power managed afterward. */
473c77a61a7Syz usbvcp->usbvc_dev_state = USB_DEV_ONLINE;
474c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
475c77a61a7Syz
476c77a61a7Syz /* initialize power management */
477c77a61a7Syz usbvc_init_power_mgmt(usbvcp);
478c77a61a7Syz
479c77a61a7Syz if ((status = usbvc_parse_ctrl_if(usbvcp)) != USB_SUCCESS) {
480c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
481c77a61a7Syz "usbvc_attach: parse ctrl interface fail, error code:%d",
482c77a61a7Syz status);
483c77a61a7Syz
484c77a61a7Syz goto fail;
485c77a61a7Syz }
486c77a61a7Syz if ((status = usbvc_parse_stream_ifs(usbvcp)) != USB_SUCCESS) {
487c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
488c77a61a7Syz "usbvc_attach: parse stream interfaces fail, error code:%d",
489c77a61a7Syz status);
490c77a61a7Syz
491c77a61a7Syz goto fail;
492c77a61a7Syz }
493c77a61a7Syz (void) usbvc_set_default_stream_fmt(usbvcp);
494c77a61a7Syz
495c77a61a7Syz /* Register for events */
496c77a61a7Syz if ((status = usb_register_event_cbs(dip, &usbvc_events, 0)) !=
497c77a61a7Syz USB_SUCCESS) {
498c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
499c77a61a7Syz "usbvc_attach: register_event_cbs failed, error code:%d",
500c77a61a7Syz status);
501c77a61a7Syz
502c77a61a7Syz goto fail;
503c77a61a7Syz }
504c77a61a7Syz
505c77a61a7Syz /* Report device */
506c77a61a7Syz ddi_report_dev(dip);
507c77a61a7Syz
508c77a61a7Syz return (DDI_SUCCESS);
509c77a61a7Syz
510c77a61a7Syz fail:
511c77a61a7Syz if (usbvcp) {
512c77a61a7Syz usbvc_cleanup(dip, usbvcp);
513c77a61a7Syz }
514c77a61a7Syz
515c77a61a7Syz return (DDI_FAILURE);
516c77a61a7Syz }
517c77a61a7Syz
518c77a61a7Syz
519c77a61a7Syz /*
520c77a61a7Syz * usbvc_detach:
521c77a61a7Syz * detach or suspend driver instance
522c77a61a7Syz *
523c77a61a7Syz * Note: in detach, only contention threads is from pm and disconnnect.
524c77a61a7Syz */
525c77a61a7Syz static int
usbvc_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)526c77a61a7Syz usbvc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
527c77a61a7Syz {
528c77a61a7Syz int instance = ddi_get_instance(dip);
529c77a61a7Syz usbvc_state_t *usbvcp = ddi_get_soft_state(usbvc_statep, instance);
530c77a61a7Syz int rval = USB_FAILURE;
531c77a61a7Syz
532c77a61a7Syz switch (cmd) {
533c77a61a7Syz case DDI_DETACH:
534c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
535c77a61a7Syz ASSERT((usbvcp->usbvc_drv_state & USBVC_OPEN) == 0);
536c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
537c77a61a7Syz
538c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
539c77a61a7Syz "usbvc_detach: enter for detach");
540c77a61a7Syz
541c77a61a7Syz usbvc_cleanup(dip, usbvcp);
542c77a61a7Syz rval = USB_SUCCESS;
543c77a61a7Syz
544c77a61a7Syz break;
545c77a61a7Syz case DDI_SUSPEND:
546c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
547c77a61a7Syz "usbvc_detach: enter for suspend");
548c77a61a7Syz
549c77a61a7Syz usbvc_cpr_suspend(dip);
550c77a61a7Syz rval = USB_SUCCESS;
551c77a61a7Syz
552c77a61a7Syz break;
553c77a61a7Syz default:
554c77a61a7Syz
555c77a61a7Syz break;
556c77a61a7Syz }
557c77a61a7Syz
558c77a61a7Syz return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
559c77a61a7Syz }
560c77a61a7Syz
561c77a61a7Syz
562c77a61a7Syz /*
563c77a61a7Syz * usbvc_cleanup:
564c77a61a7Syz * clean up the driver state for detach
565c77a61a7Syz */
566c77a61a7Syz static void
usbvc_cleanup(dev_info_t * dip,usbvc_state_t * usbvcp)567c77a61a7Syz usbvc_cleanup(dev_info_t *dip, usbvc_state_t *usbvcp)
568c77a61a7Syz {
569c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
570c77a61a7Syz "Cleanup: enter");
571c77a61a7Syz
572c77a61a7Syz if (usbvcp->usbvc_locks_initialized) {
573c77a61a7Syz
574c77a61a7Syz /* This must be done 1st to prevent more events from coming. */
575c77a61a7Syz usb_unregister_event_cbs(dip, &usbvc_events);
576c77a61a7Syz
577c77a61a7Syz /*
578c77a61a7Syz * At this point, no new activity can be initiated. The driver
579c77a61a7Syz * has disabled hotplug callbacks. The Solaris framework has
580c77a61a7Syz * disabled new opens on a device being detached, and does not
581c77a61a7Syz * allow detaching an open device.
582c77a61a7Syz *
583c77a61a7Syz * The following ensures that all driver activity has drained.
584c77a61a7Syz */
585c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
586c77a61a7Syz (void) usbvc_serialize_access(usbvcp, USBVC_SER_NOSIG);
587c77a61a7Syz usbvc_release_access(usbvcp);
588c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
589c77a61a7Syz
590c77a61a7Syz /* All device activity has died down. */
591c77a61a7Syz usbvc_destroy_power_mgmt(usbvcp);
592c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
593c77a61a7Syz usbvc_fini_lists(usbvcp);
594c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
595c77a61a7Syz
596c77a61a7Syz ddi_remove_minor_node(dip, NULL);
597c77a61a7Syz usbvc_fini_sync_objs(usbvcp);
598c77a61a7Syz }
599c77a61a7Syz
600c77a61a7Syz usb_client_detach(dip, usbvcp->usbvc_reg);
601c77a61a7Syz usb_free_log_hdl(usbvcp->usbvc_log_handle);
602c77a61a7Syz ddi_soft_state_free(usbvc_statep, ddi_get_instance(dip));
603c77a61a7Syz ddi_prop_remove_all(dip);
604c77a61a7Syz }
605c77a61a7Syz
606c77a61a7Syz
607c77a61a7Syz /*ARGSUSED*/
608c77a61a7Syz static int
usbvc_open(dev_t * devp,int flag,int otyp,cred_t * cred_p)609c77a61a7Syz usbvc_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
610c77a61a7Syz {
611c77a61a7Syz usbvc_state_t *usbvcp =
612ef772e54Sfb ddi_get_soft_state(usbvc_statep, getminor(*devp));
613c77a61a7Syz
614c77a61a7Syz if (usbvcp == NULL) {
615c77a61a7Syz
616c77a61a7Syz return (ENXIO);
617c77a61a7Syz }
618c77a61a7Syz
619c77a61a7Syz /*
620c77a61a7Syz * Keep it simple: one client at a time.
621c77a61a7Syz * Exclusive open only
622c77a61a7Syz */
623c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
624c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
625c77a61a7Syz "usbvc_open: enter, dev_stat=%d", usbvcp->usbvc_dev_state);
626c77a61a7Syz
627c77a61a7Syz if (usbvcp->usbvc_dev_state == USB_DEV_DISCONNECTED) {
628c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
629c77a61a7Syz
630c77a61a7Syz return (ENODEV);
631c77a61a7Syz }
632c77a61a7Syz if (usbvcp->usbvc_dev_state == USB_DEV_SUSPENDED) {
633c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
634c77a61a7Syz
635c77a61a7Syz return (EIO);
636c77a61a7Syz }
637c77a61a7Syz if ((usbvcp->usbvc_drv_state & USBVC_OPEN) != 0) {
638c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
639c77a61a7Syz
640c77a61a7Syz return (EBUSY);
641c77a61a7Syz }
642c77a61a7Syz usbvcp->usbvc_drv_state |= USBVC_OPEN;
643c77a61a7Syz
644c77a61a7Syz if (usbvc_serialize_access(usbvcp, USBVC_SER_SIG) == 0) {
645c77a61a7Syz usbvcp->usbvc_drv_state &= ~USBVC_OPEN;
646c77a61a7Syz usbvcp->usbvc_serial_inuse = B_FALSE;
647c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
648c77a61a7Syz
649c77a61a7Syz return (EINTR);
650c77a61a7Syz }
651c77a61a7Syz
652c77a61a7Syz /* raise power */
653c77a61a7Syz usbvc_pm_busy_component(usbvcp);
654c77a61a7Syz if (usbvcp->usbvc_pm->usbvc_current_power != USB_DEV_OS_FULL_PWR) {
655c77a61a7Syz usbvcp->usbvc_pm->usbvc_raise_power = B_TRUE;
656c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
657c77a61a7Syz (void) pm_raise_power(usbvcp->usbvc_dip,
658ef772e54Sfb 0, USB_DEV_OS_FULL_PWR);
659c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
660c77a61a7Syz usbvcp->usbvc_pm->usbvc_raise_power = B_FALSE;
661c77a61a7Syz }
662c77a61a7Syz
663c77a61a7Syz /* Device is idle until it is used. */
664c77a61a7Syz usbvc_release_access(usbvcp);
665c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
666c77a61a7Syz
667c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
668c77a61a7Syz "usbvc_open: end.");
669c77a61a7Syz
670c77a61a7Syz return (0);
671c77a61a7Syz }
672c77a61a7Syz
673c77a61a7Syz
674c77a61a7Syz /*ARGSUSED*/
675c77a61a7Syz static int
usbvc_close(dev_t dev,int flag,int otyp,cred_t * cred_p)676c77a61a7Syz usbvc_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
677c77a61a7Syz {
678c77a61a7Syz usbvc_stream_if_t *strm_if;
679c77a61a7Syz int if_num;
680c77a61a7Syz usbvc_state_t *usbvcp =
681c77a61a7Syz ddi_get_soft_state(usbvc_statep, getminor(dev));
682c77a61a7Syz
683c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
684c77a61a7Syz "close: enter");
685c77a61a7Syz
686c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
687c77a61a7Syz (void) usbvc_serialize_access(usbvcp, USBVC_SER_NOSIG);
688c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
689c77a61a7Syz
690c77a61a7Syz /* Perform device session cleanup here. */
691c77a61a7Syz
692c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
693c77a61a7Syz "close: cleaning up...");
694c77a61a7Syz
695c77a61a7Syz /*
696c77a61a7Syz * USBA automatically flushes/resets active non-default pipes
697c77a61a7Syz * when they are closed. We can't reset default pipe, but we
698c77a61a7Syz * can wait for all requests on it from this dip to drain.
699c77a61a7Syz */
700c77a61a7Syz (void) usb_pipe_drain_reqs(usbvcp->usbvc_dip,
701c77a61a7Syz usbvcp->usbvc_reg->dev_default_ph, 0,
702c77a61a7Syz USB_FLAGS_SLEEP, NULL, 0);
703c77a61a7Syz
704c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
705c77a61a7Syz strm_if = usbvcp->usbvc_curr_strm;
706c77a61a7Syz if (strm_if->start_polling == 1) {
707c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
708c77a61a7Syz usb_pipe_stop_isoc_polling(strm_if->datain_ph, USB_FLAGS_SLEEP);
709c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
710c77a61a7Syz strm_if->start_polling = 0;
711c77a61a7Syz }
71209dd0d6cSRaymond Chen strm_if->stream_on = 0;
71309dd0d6cSRaymond Chen
714c77a61a7Syz usbvc_close_isoc_pipe(usbvcp, strm_if);
715c77a61a7Syz if_num = strm_if->if_descr->if_alt->altif_descr.bInterfaceNumber;
716c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
717c77a61a7Syz
718c77a61a7Syz /* reset alternate to the default one. */
719c77a61a7Syz (void) usb_set_alt_if(usbvcp->usbvc_dip, if_num, 0,
720c77a61a7Syz USB_FLAGS_SLEEP, NULL, NULL);
721c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
722c77a61a7Syz
723c77a61a7Syz usbvc_free_read_bufs(usbvcp, strm_if);
72461b2e298Slc
72561b2e298Slc /* reset the desired read buf number to the default value on close */
72661b2e298Slc strm_if->buf_read_num = USBVC_DEFAULT_READ_BUF_NUM;
72761b2e298Slc
728c77a61a7Syz usbvc_free_map_bufs(usbvcp, strm_if);
729c77a61a7Syz usbvcp->usbvc_drv_state &= ~USBVC_OPEN;
730c77a61a7Syz
731c77a61a7Syz usbvc_release_access(usbvcp);
732c77a61a7Syz usbvc_pm_idle_component(usbvcp);
733c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
734c77a61a7Syz
735c77a61a7Syz return (0);
736c77a61a7Syz }
737c77a61a7Syz
738c77a61a7Syz
739c77a61a7Syz /*ARGSUSED*/
740c77a61a7Syz /* Read isoc data from usb video devices */
741c77a61a7Syz static int
usbvc_read(dev_t dev,struct uio * uio_p,cred_t * cred_p)742c77a61a7Syz usbvc_read(dev_t dev, struct uio *uio_p, cred_t *cred_p)
743c77a61a7Syz {
744c77a61a7Syz int rval;
745c77a61a7Syz usbvc_stream_if_t *strm_if;
746c77a61a7Syz usbvc_state_t *usbvcp =
747ef772e54Sfb ddi_get_soft_state(usbvc_statep, getminor(dev));
748c77a61a7Syz
749c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
750c77a61a7Syz "usbvc_read: enter");
751c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
752c77a61a7Syz if (usbvcp->usbvc_dev_state != USB_DEV_ONLINE) {
753c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
754c77a61a7Syz "usbvc_read: Device is not available,"
755c77a61a7Syz " dev_stat=%d", usbvcp->usbvc_dev_state);
756c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
757c77a61a7Syz
758c77a61a7Syz return (EFAULT);
759c77a61a7Syz }
760c77a61a7Syz if ((uio_p->uio_fmode & (FNDELAY|FNONBLOCK)) &&
761c77a61a7Syz (usbvcp->usbvc_serial_inuse != B_FALSE)) {
762c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
763c77a61a7Syz "usbvc_read: non-blocking read, return fail.");
764c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
765c77a61a7Syz
766c77a61a7Syz return (EAGAIN);
767c77a61a7Syz }
768c77a61a7Syz if (usbvc_serialize_access(usbvcp, USBVC_SER_SIG) <= 0) {
769c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
770c77a61a7Syz "usbvc_read: serialize_access failed.");
771c77a61a7Syz rval = EFAULT;
772c77a61a7Syz
773c77a61a7Syz goto fail;
774c77a61a7Syz }
775c77a61a7Syz
776c77a61a7Syz /* Get the first stream interface */
777c77a61a7Syz strm_if = usbvcp->usbvc_curr_strm;
778c77a61a7Syz if (!strm_if) {
779c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
780c77a61a7Syz "usbvc_read: no stream interfaces");
781c77a61a7Syz rval = EFAULT;
782c77a61a7Syz
783c77a61a7Syz goto fail;
784c77a61a7Syz }
785c77a61a7Syz
786c77a61a7Syz /*
787c77a61a7Syz * If it is the first read, open isoc pipe and allocate bufs for
788c77a61a7Syz * read I/O method.
789c77a61a7Syz */
790c77a61a7Syz if (strm_if->datain_ph == NULL) {
791c77a61a7Syz if (usbvc_open_isoc_pipe(usbvcp, strm_if) != USB_SUCCESS) {
792c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_READ,
793c77a61a7Syz usbvcp->usbvc_log_handle,
794c77a61a7Syz "usbvc_read: first read, open pipe fail");
795c77a61a7Syz rval = EFAULT;
796c77a61a7Syz
797c77a61a7Syz goto fail;
798c77a61a7Syz }
799c77a61a7Syz if (usbvc_alloc_read_bufs(usbvcp, strm_if) != USB_SUCCESS) {
800c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_READ,
801c77a61a7Syz usbvcp->usbvc_log_handle,
802c77a61a7Syz "usbvc_read: allocate rw bufs fail");
803c77a61a7Syz rval = EFAULT;
804c77a61a7Syz
805c77a61a7Syz goto fail;
806c77a61a7Syz }
807c77a61a7Syz }
808c77a61a7Syz
809c77a61a7Syz /* start polling if it is not started yet */
810c77a61a7Syz if (strm_if->start_polling != 1) {
81111b5d82fSToomas Soome if (usbvc_start_isoc_polling(usbvcp, strm_if, 0) !=
812c77a61a7Syz USB_SUCCESS) {
813c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_READ,
814c77a61a7Syz usbvcp->usbvc_log_handle,
815c77a61a7Syz "usbvc_read: usbvc_start_isoc_polling fail");
816c77a61a7Syz rval = EFAULT;
817c77a61a7Syz
818c77a61a7Syz goto fail;
819c77a61a7Syz }
820c77a61a7Syz strm_if->start_polling = 1;
821c77a61a7Syz }
822c77a61a7Syz
823c77a61a7Syz if (list_is_empty(&strm_if->buf_read.uv_buf_done)) {
824c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
825c77a61a7Syz "usbvc_read: full buf list is empty.");
826c77a61a7Syz
827c77a61a7Syz if (uio_p->uio_fmode & (FNDELAY | FNONBLOCK)) {
828c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_READ,
829c77a61a7Syz usbvcp->usbvc_log_handle, "usbvc_read: fail, "
830c77a61a7Syz "non-blocking read, done buf is empty.");
831c77a61a7Syz rval = EAGAIN;
832c77a61a7Syz
833c77a61a7Syz goto fail;
834c77a61a7Syz }
835c77a61a7Syz
836c77a61a7Syz /* no available buffers, block here */
837c77a61a7Syz while (list_is_empty(&strm_if->buf_read.uv_buf_done)) {
838c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_READ,
839c77a61a7Syz usbvcp->usbvc_log_handle,
840c77a61a7Syz "usbvc_read: wait for done buf");
841c77a61a7Syz if (cv_wait_sig(&usbvcp->usbvc_read_cv,
842c77a61a7Syz &usbvcp->usbvc_mutex) <= 0) {
843c77a61a7Syz /* no done buf and cv is signaled */
844c77a61a7Syz rval = EINTR;
845c77a61a7Syz
846c77a61a7Syz goto fail;
847c77a61a7Syz }
848c77a61a7Syz if (usbvcp->usbvc_dev_state != USB_DEV_ONLINE) {
849c77a61a7Syz
850c77a61a7Syz /* Device is disconnected. */
851c77a61a7Syz rval = EINTR;
852c77a61a7Syz
853c77a61a7Syz goto fail;
854c77a61a7Syz }
855c77a61a7Syz }
856c77a61a7Syz
857c77a61a7Syz }
858c77a61a7Syz
859c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
860c77a61a7Syz rval = physio(usbvc_strategy, NULL, dev, B_READ,
861ef772e54Sfb usbvc_minphys, uio_p);
862c77a61a7Syz
863c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
864c77a61a7Syz usbvc_release_access(usbvcp);
865c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
866c77a61a7Syz
867c77a61a7Syz return (rval);
868c77a61a7Syz
869c77a61a7Syz fail:
870c77a61a7Syz usbvc_release_access(usbvcp);
871c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
872c77a61a7Syz
873c77a61a7Syz return (rval);
874c77a61a7Syz }
875c77a61a7Syz
876c77a61a7Syz
877c77a61a7Syz /*
878c77a61a7Syz * strategy:
879c77a61a7Syz * Called through physio to setup and start the transfer.
880c77a61a7Syz */
881c77a61a7Syz static int
usbvc_strategy(struct buf * bp)882c77a61a7Syz usbvc_strategy(struct buf *bp)
883c77a61a7Syz {
884c77a61a7Syz usbvc_state_t *usbvcp = ddi_get_soft_state(usbvc_statep,
885ef772e54Sfb getminor(bp->b_edev));
886c77a61a7Syz
887c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
888c77a61a7Syz "usbvc_strategy: enter");
889c77a61a7Syz
890c77a61a7Syz /*
891c77a61a7Syz * Initialize residual count here in case transfer doesn't even get
892c77a61a7Syz * started.
893c77a61a7Syz */
894c77a61a7Syz bp->b_resid = bp->b_bcount;
895c77a61a7Syz
896c77a61a7Syz /* Needed as this is a character driver. */
897c77a61a7Syz if (bp->b_flags & (B_PHYS | B_PAGEIO)) {
898c77a61a7Syz bp_mapin(bp);
899c77a61a7Syz }
900c77a61a7Syz
901c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
902c77a61a7Syz
903c77a61a7Syz /* Make sure device has not been disconnected. */
904c77a61a7Syz if (usbvcp->usbvc_dev_state != USB_DEV_ONLINE) {
905c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
906c77a61a7Syz "usbvc_strategy: device can't be accessed");
907c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
908c77a61a7Syz
909c77a61a7Syz goto fail;
910c77a61a7Syz }
911c77a61a7Syz
912c77a61a7Syz /* read data from uv_buf_done list */
913c77a61a7Syz if (usbvc_read_buf(usbvcp, bp) != USB_SUCCESS) {
914c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
915c77a61a7Syz "usbvc_strategy: read full buf list fail");
916c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
917c77a61a7Syz
918c77a61a7Syz goto fail;
919c77a61a7Syz }
920c77a61a7Syz
921c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
922c77a61a7Syz
923c77a61a7Syz biodone(bp);
924c77a61a7Syz
925c77a61a7Syz return (0);
926c77a61a7Syz
927c77a61a7Syz fail:
928c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
929c77a61a7Syz "usbvc_strategy: strategy fail");
930c77a61a7Syz bp->b_private = NULL;
931c77a61a7Syz
932c77a61a7Syz bioerror(bp, EIO);
933c77a61a7Syz biodone(bp);
934c77a61a7Syz
935c77a61a7Syz return (0);
936c77a61a7Syz }
937c77a61a7Syz
938c77a61a7Syz
939c77a61a7Syz static void
usbvc_minphys(struct buf * bp)940c77a61a7Syz usbvc_minphys(struct buf *bp)
941c77a61a7Syz {
942c77a61a7Syz dev_t dev = bp->b_edev;
943c77a61a7Syz usbvc_stream_if_t *strm_if;
944c77a61a7Syz uint32_t maxsize;
945ef772e54Sfb usbvc_state_t *usbvcp =
946ef772e54Sfb ddi_get_soft_state(usbvc_statep, getminor(dev));
947c77a61a7Syz
948c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
949c77a61a7Syz strm_if = usbvcp->usbvc_curr_strm;
950c77a61a7Syz LE_TO_UINT32(strm_if->ctrl_pc.dwMaxVideoFrameSize, 0, maxsize);
951c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
952c77a61a7Syz "usbvc_minphys: max read size=%d", maxsize);
953c77a61a7Syz
954c77a61a7Syz if (bp->b_bcount > maxsize) {
955c77a61a7Syz bp->b_bcount = maxsize;
956c77a61a7Syz }
957c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
958c77a61a7Syz }
959c77a61a7Syz
960c77a61a7Syz
961c77a61a7Syz /*
962c77a61a7Syz * ioctl entry.
963c77a61a7Syz */
964c77a61a7Syz /*ARGSUSED*/
965c77a61a7Syz static int
usbvc_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cred_p,int * rval_p)966c77a61a7Syz usbvc_ioctl(dev_t dev, int cmd, intptr_t arg,
96711b5d82fSToomas Soome int mode, cred_t *cred_p, int *rval_p)
968c77a61a7Syz {
969c77a61a7Syz int rv = 0;
970c77a61a7Syz usbvc_state_t *usbvcp =
971c77a61a7Syz ddi_get_soft_state(usbvc_statep, getminor(dev));
972c77a61a7Syz
973c77a61a7Syz if (usbvcp == NULL) {
974c77a61a7Syz
975c77a61a7Syz return (ENXIO);
976c77a61a7Syz }
977c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
978c77a61a7Syz "ioctl enter, cmd=%x", cmd);
979c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
980c77a61a7Syz if (usbvcp->usbvc_dev_state != USB_DEV_ONLINE) {
981c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
982c77a61a7Syz "ioctl: Device is not online,"
983c77a61a7Syz " dev_stat=%d", usbvcp->usbvc_dev_state);
984c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
985c77a61a7Syz
986c77a61a7Syz return (EFAULT);
987c77a61a7Syz }
988c77a61a7Syz if (usbvc_serialize_access(usbvcp, USBVC_SER_SIG) <= 0) {
989c77a61a7Syz usbvcp->usbvc_serial_inuse = B_FALSE;
990c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
991c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
992c77a61a7Syz "serialize_access failed.");
993c77a61a7Syz
994c77a61a7Syz return (EFAULT);
995c77a61a7Syz }
996c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
997c77a61a7Syz
998c77a61a7Syz rv = usbvc_v4l2_ioctl(usbvcp, cmd, arg, mode);
999c77a61a7Syz
1000c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
1001c77a61a7Syz usbvc_release_access(usbvcp);
1002c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
1003c77a61a7Syz
1004c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
1005c77a61a7Syz "usbvc_ioctl exit");
1006c77a61a7Syz
1007c77a61a7Syz return (rv);
1008c77a61a7Syz }
1009c77a61a7Syz
1010c77a61a7Syz
1011c77a61a7Syz /* Entry for mmap system call */
1012c77a61a7Syz static int
usbvc_devmap(dev_t dev,devmap_cookie_t handle,offset_t off,size_t len,size_t * maplen,uint_t model)1013c77a61a7Syz usbvc_devmap(dev_t dev, devmap_cookie_t handle, offset_t off,
101411b5d82fSToomas Soome size_t len, size_t *maplen, uint_t model)
1015c77a61a7Syz {
1016c77a61a7Syz usbvc_state_t *usbvcp;
1017c77a61a7Syz int error, i;
1018c77a61a7Syz usbvc_buf_t *buf = NULL;
1019c77a61a7Syz usbvc_stream_if_t *strm_if;
1020c77a61a7Syz usbvc_buf_grp_t *bufgrp;
1021c77a61a7Syz
1022c77a61a7Syz usbvcp = ddi_get_soft_state(usbvc_statep, getminor(dev));
1023c77a61a7Syz if (usbvcp == NULL) {
1024c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
1025c77a61a7Syz "usbvc_devmap: usbvcp == NULL");
1026c77a61a7Syz
1027c77a61a7Syz return (ENXIO);
1028c77a61a7Syz }
1029c77a61a7Syz
1030c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
1031112116d8Sfb "devmap: memory map for instance(%d), off=%llx,"
1032112116d8Sfb "len=%ld, maplen=%ld, model=%d", getminor(dev), off,
1033112116d8Sfb len, *maplen, model);
1034c77a61a7Syz
1035c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
1036c77a61a7Syz (void) usbvc_serialize_access(usbvcp, USBVC_SER_NOSIG);
1037c77a61a7Syz strm_if = usbvcp->usbvc_curr_strm;
1038c77a61a7Syz if (!strm_if) {
1039c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
1040c77a61a7Syz "usbvc_devmap: No current strm if");
1041c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
1042c77a61a7Syz
1043c77a61a7Syz return (ENXIO);
1044c77a61a7Syz }
1045c77a61a7Syz bufgrp = &strm_if->buf_map;
1046c77a61a7Syz for (i = 0; i < bufgrp->buf_cnt; i++) {
1047c77a61a7Syz if (bufgrp->buf_head[i].v4l2_buf.m.offset == off) {
1048c77a61a7Syz buf = &bufgrp->buf_head[i];
1049c77a61a7Syz
1050c77a61a7Syz break;
1051c77a61a7Syz }
1052c77a61a7Syz }
1053c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
1054c77a61a7Syz "usbvc_devmap: idx=%d", i);
1055c77a61a7Syz if (buf == NULL) {
1056c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
1057c77a61a7Syz
1058c77a61a7Syz return (ENXIO);
1059c77a61a7Syz }
1060c77a61a7Syz /*
1061c77a61a7Syz * round up len to a multiple of a page size, according to chapter
1062c77a61a7Syz * 10 of "writing device drivers"
1063c77a61a7Syz */
1064c77a61a7Syz len = ptob(btopr(len));
1065c77a61a7Syz if (len > ptob(btopr(buf->len))) {
1066c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
1067112116d8Sfb "usbvc_devmap: len=0x%lx", len);
1068c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
1069c77a61a7Syz
1070c77a61a7Syz return (ENXIO);
1071c77a61a7Syz }
1072c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
1073c77a61a7Syz
1074c77a61a7Syz error = devmap_umem_setup(handle, usbvcp->usbvc_dip, NULL,
1075ef772e54Sfb buf->umem_cookie, off, len, PROT_ALL, DEVMAP_DEFAULTS, NULL);
1076c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
1077c77a61a7Syz *maplen = len;
1078c77a61a7Syz if (error == 0 && buf->status == USBVC_BUF_INIT) {
1079c77a61a7Syz buf->status = USBVC_BUF_MAPPED;
1080c77a61a7Syz } else {
1081c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_DEVMAP, usbvcp->usbvc_log_handle,
1082c77a61a7Syz "usbvc_devmap: devmap_umem_setup, err=%d", error);
1083c77a61a7Syz }
1084c77a61a7Syz
1085c77a61a7Syz (void) usbvc_release_access(usbvcp);
1086c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
1087c77a61a7Syz
1088c77a61a7Syz return (error);
1089c77a61a7Syz }
1090c77a61a7Syz
1091c77a61a7Syz /*
1092c77a61a7Syz * pm and cpr
1093c77a61a7Syz */
1094c77a61a7Syz
1095c77a61a7Syz /*
1096112116d8Sfb * usbvc_power :
1097c77a61a7Syz * Power entry point, the workhorse behind pm_raise_power, pm_lower_power,
1098c77a61a7Syz * usb_req_raise_power and usb_req_lower_power.
1099c77a61a7Syz */
1100c77a61a7Syz /* ARGSUSED */
1101c77a61a7Syz static int
usbvc_power(dev_info_t * dip,int comp,int level)1102c77a61a7Syz usbvc_power(dev_info_t *dip, int comp, int level)
1103c77a61a7Syz {
1104c77a61a7Syz usbvc_state_t *usbvcp;
1105c77a61a7Syz usbvc_power_t *pm;
1106c77a61a7Syz int rval = USB_FAILURE;
1107c77a61a7Syz
1108c77a61a7Syz usbvcp = ddi_get_soft_state(usbvc_statep, ddi_get_instance(dip));
1109c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
1110c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1111c77a61a7Syz "usbvc_power: enter: level = %d, dev_state: %x",
1112c77a61a7Syz level, usbvcp->usbvc_dev_state);
1113c77a61a7Syz
1114c77a61a7Syz if (usbvcp->usbvc_pm == NULL) {
1115c77a61a7Syz
1116c77a61a7Syz goto done;
1117c77a61a7Syz }
1118c77a61a7Syz
1119c77a61a7Syz pm = usbvcp->usbvc_pm;
1120c77a61a7Syz
1121c77a61a7Syz /* Check if we are transitioning to a legal power level */
1122c77a61a7Syz if (USB_DEV_PWRSTATE_OK(pm->usbvc_pwr_states, level)) {
1123c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
1124c77a61a7Syz "usbvc_power: illegal power level = %d "
1125c77a61a7Syz "pwr_states: %x", level, pm->usbvc_pwr_states);
1126c77a61a7Syz
1127c77a61a7Syz goto done;
1128c77a61a7Syz }
1129c77a61a7Syz /*
1130c77a61a7Syz * if we are about to raise power and asked to lower power, fail
1131c77a61a7Syz */
1132c77a61a7Syz if (pm->usbvc_raise_power && (level < (int)pm->usbvc_current_power)) {
1133c77a61a7Syz
1134c77a61a7Syz goto done;
1135c77a61a7Syz }
1136c77a61a7Syz switch (level) {
1137c77a61a7Syz case USB_DEV_OS_PWR_OFF :
1138c77a61a7Syz rval = usbvc_pwrlvl0(usbvcp);
1139c77a61a7Syz
1140c77a61a7Syz break;
1141c77a61a7Syz case USB_DEV_OS_PWR_1 :
1142c77a61a7Syz rval = usbvc_pwrlvl1(usbvcp);
1143c77a61a7Syz
1144c77a61a7Syz break;
1145c77a61a7Syz case USB_DEV_OS_PWR_2 :
1146c77a61a7Syz rval = usbvc_pwrlvl2(usbvcp);
1147c77a61a7Syz
1148c77a61a7Syz break;
1149c77a61a7Syz case USB_DEV_OS_FULL_PWR :
1150c77a61a7Syz rval = usbvc_pwrlvl3(usbvcp);
1151c77a61a7Syz
1152c77a61a7Syz break;
1153c77a61a7Syz }
1154c77a61a7Syz
1155c77a61a7Syz done:
1156c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
1157c77a61a7Syz
1158c77a61a7Syz return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
1159c77a61a7Syz }
1160c77a61a7Syz
1161c77a61a7Syz
1162c77a61a7Syz /*
1163c77a61a7Syz * usbvc_init_power_mgmt:
1164c77a61a7Syz * Initialize power management and remote wakeup functionality.
1165c77a61a7Syz * No mutex is necessary in this function as it's called only by attach.
1166c77a61a7Syz */
1167c77a61a7Syz static void
usbvc_init_power_mgmt(usbvc_state_t * usbvcp)1168c77a61a7Syz usbvc_init_power_mgmt(usbvc_state_t *usbvcp)
1169c77a61a7Syz {
1170c77a61a7Syz usbvc_power_t *usbvcpm;
1171c77a61a7Syz uint_t pwr_states;
1172c77a61a7Syz
1173c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1174c77a61a7Syz "init_power_mgmt enter");
1175c77a61a7Syz
1176c77a61a7Syz /* Allocate the state structure */
1177c77a61a7Syz usbvcpm = kmem_zalloc(sizeof (usbvc_power_t), KM_SLEEP);
1178c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
1179c77a61a7Syz usbvcp->usbvc_pm = usbvcpm;
1180c77a61a7Syz usbvcpm->usbvc_state = usbvcp;
1181c77a61a7Syz usbvcpm->usbvc_pm_capabilities = 0;
1182c77a61a7Syz usbvcpm->usbvc_current_power = USB_DEV_OS_FULL_PWR;
1183c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
1184c77a61a7Syz
1185c77a61a7Syz if (usb_create_pm_components(usbvcp->usbvc_dip, &pwr_states) ==
1186ef772e54Sfb USB_SUCCESS) {
1187c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1188c77a61a7Syz "usbvc_init_power_mgmt: created PM components");
1189c77a61a7Syz
1190c77a61a7Syz if (usb_handle_remote_wakeup(usbvcp->usbvc_dip,
1191c77a61a7Syz USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
1192c77a61a7Syz usbvcpm->usbvc_wakeup_enabled = 1;
1193c77a61a7Syz } else {
1194c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_ATTA,
1195c77a61a7Syz usbvcp->usbvc_log_handle, "usbvc_init_power_mgmt:"
1196c77a61a7Syz " remote wakeup not supported");
1197c77a61a7Syz }
1198c77a61a7Syz
1199c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
1200c77a61a7Syz usbvcpm->usbvc_pwr_states = (uint8_t)pwr_states;
1201c77a61a7Syz usbvc_pm_busy_component(usbvcp);
1202c77a61a7Syz usbvcpm->usbvc_raise_power = B_TRUE;
1203c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
1204c77a61a7Syz
1205c77a61a7Syz (void) pm_raise_power(
1206c77a61a7Syz usbvcp->usbvc_dip, 0, USB_DEV_OS_FULL_PWR);
1207c77a61a7Syz
1208c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
1209c77a61a7Syz usbvcpm->usbvc_raise_power = B_FALSE;
1210c77a61a7Syz usbvc_pm_idle_component(usbvcp);
1211c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
1212c77a61a7Syz
1213c77a61a7Syz }
1214c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1215c77a61a7Syz "usbvc_init_power_mgmt: end");
1216c77a61a7Syz }
1217c77a61a7Syz
1218c77a61a7Syz
1219c77a61a7Syz /*
1220112116d8Sfb * usbvc_destroy_power_mgmt:
1221c77a61a7Syz * Shut down and destroy power management and remote wakeup functionality.
1222c77a61a7Syz */
1223c77a61a7Syz static void
usbvc_destroy_power_mgmt(usbvc_state_t * usbvcp)1224c77a61a7Syz usbvc_destroy_power_mgmt(usbvc_state_t *usbvcp)
1225c77a61a7Syz {
1226c77a61a7Syz usbvc_power_t *pm;
1227c77a61a7Syz int rval;
1228c77a61a7Syz
1229c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1230c77a61a7Syz "destroy_power_mgmt enter");
1231c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
1232c77a61a7Syz pm = usbvcp->usbvc_pm;
1233c77a61a7Syz if (pm && (usbvcp->usbvc_dev_state != USB_DEV_DISCONNECTED)) {
1234c77a61a7Syz
1235c77a61a7Syz usbvc_pm_busy_component(usbvcp);
1236c77a61a7Syz if (pm->usbvc_wakeup_enabled) {
1237c77a61a7Syz pm->usbvc_raise_power = B_TRUE;
1238c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
1239c77a61a7Syz
1240c77a61a7Syz /* First bring the device to full power */
1241c77a61a7Syz (void) pm_raise_power(usbvcp->usbvc_dip, 0,
1242c77a61a7Syz USB_DEV_OS_FULL_PWR);
1243c77a61a7Syz if ((rval = usb_handle_remote_wakeup(
1244c77a61a7Syz usbvcp->usbvc_dip,
1245c77a61a7Syz USB_REMOTE_WAKEUP_DISABLE)) !=
1246c77a61a7Syz USB_SUCCESS) {
1247c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_ATTA,
1248c77a61a7Syz usbvcp->usbvc_log_handle,
1249c77a61a7Syz "usbvc_destroy_power_mgmt: "
1250c77a61a7Syz "Error disabling rmt wakeup: rval = %d",
1251c77a61a7Syz rval);
1252c77a61a7Syz }
1253c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
1254c77a61a7Syz pm->usbvc_raise_power = B_FALSE;
1255c77a61a7Syz
1256c77a61a7Syz }
1257c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
1258c77a61a7Syz
1259c77a61a7Syz /*
1260c77a61a7Syz * Since remote wakeup is disabled now,
1261c77a61a7Syz * no one can raise power
1262c77a61a7Syz * and get to device once power is lowered here.
1263c77a61a7Syz */
1264c77a61a7Syz (void) pm_lower_power(usbvcp->usbvc_dip, 0, USB_DEV_OS_PWR_OFF);
1265c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
1266c77a61a7Syz usbvc_pm_idle_component(usbvcp);
1267c77a61a7Syz }
1268c77a61a7Syz
1269c77a61a7Syz if (pm) {
1270c77a61a7Syz kmem_free(pm, sizeof (usbvc_power_t));
1271c77a61a7Syz usbvcp->usbvc_pm = NULL;
1272c77a61a7Syz }
1273c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
1274c77a61a7Syz }
1275c77a61a7Syz
1276c77a61a7Syz
1277c77a61a7Syz static void
usbvc_pm_busy_component(usbvc_state_t * usbvcp)1278c77a61a7Syz usbvc_pm_busy_component(usbvc_state_t *usbvcp)
1279c77a61a7Syz {
1280c77a61a7Syz ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
1281c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1282c77a61a7Syz "usbvc_pm_busy_component: enter");
1283c77a61a7Syz
1284c77a61a7Syz usbvcp->usbvc_pm->usbvc_pm_busy++;
1285c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
1286c77a61a7Syz
1287c77a61a7Syz if (pm_busy_component(usbvcp->usbvc_dip, 0) !=
1288c77a61a7Syz DDI_SUCCESS) {
1289c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
1290c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1291c77a61a7Syz "usbvc_pm_busy_component: pm busy fail, usbvc_pm_busy=%d",
1292c77a61a7Syz usbvcp->usbvc_pm->usbvc_pm_busy);
1293c77a61a7Syz
1294c77a61a7Syz usbvcp->usbvc_pm->usbvc_pm_busy--;
1295c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
1296c77a61a7Syz }
1297c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
1298c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1299c77a61a7Syz "usbvc_pm_busy_component: exit");
1300c77a61a7Syz }
1301c77a61a7Syz
1302c77a61a7Syz
1303c77a61a7Syz static void
usbvc_pm_idle_component(usbvc_state_t * usbvcp)1304c77a61a7Syz usbvc_pm_idle_component(usbvc_state_t *usbvcp)
1305c77a61a7Syz {
1306c77a61a7Syz ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
1307c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1308c77a61a7Syz "usbvc_pm_idle_component: enter");
1309c77a61a7Syz
1310c77a61a7Syz if (usbvcp->usbvc_pm != NULL) {
1311c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
1312c77a61a7Syz if (pm_idle_component(usbvcp->usbvc_dip, 0) ==
1313c77a61a7Syz DDI_SUCCESS) {
1314c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
1315c77a61a7Syz ASSERT(usbvcp->usbvc_pm->usbvc_pm_busy > 0);
1316c77a61a7Syz usbvcp->usbvc_pm->usbvc_pm_busy--;
1317c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
1318c77a61a7Syz }
1319c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
1320c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1321c77a61a7Syz "usbvc_pm_idle_component: %d",
1322c77a61a7Syz usbvcp->usbvc_pm->usbvc_pm_busy);
1323c77a61a7Syz }
1324c77a61a7Syz }
1325c77a61a7Syz
1326c77a61a7Syz
1327c77a61a7Syz /*
1328c77a61a7Syz * usbvc_pwrlvl0:
1329c77a61a7Syz * Functions to handle power transition for OS levels 0 -> 3
1330c77a61a7Syz */
1331c77a61a7Syz static int
usbvc_pwrlvl0(usbvc_state_t * usbvcp)1332c77a61a7Syz usbvc_pwrlvl0(usbvc_state_t *usbvcp)
1333c77a61a7Syz {
1334c77a61a7Syz int rval;
1335c77a61a7Syz
1336c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1337c77a61a7Syz "usbvc_pwrlvl0, dev_state: %x", usbvcp->usbvc_dev_state);
1338c77a61a7Syz
1339c77a61a7Syz switch (usbvcp->usbvc_dev_state) {
1340c77a61a7Syz case USB_DEV_ONLINE:
1341c77a61a7Syz /* Deny the powerdown request if the device is busy */
1342c77a61a7Syz if (usbvcp->usbvc_pm->usbvc_pm_busy != 0) {
134315c07adcSJohn Levon USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
134415c07adcSJohn Levon "usbvc_pwrlvl0: usbvc_pm_busy");
1345c77a61a7Syz
1346c77a61a7Syz return (USB_FAILURE);
1347c77a61a7Syz }
1348c77a61a7Syz
1349c77a61a7Syz /* Issue USB D3 command to the device here */
1350c77a61a7Syz rval = usb_set_device_pwrlvl3(usbvcp->usbvc_dip);
1351c77a61a7Syz ASSERT(rval == USB_SUCCESS);
1352c77a61a7Syz
1353c77a61a7Syz usbvcp->usbvc_dev_state = USB_DEV_PWRED_DOWN;
1354ef772e54Sfb usbvcp->usbvc_pm->usbvc_current_power = USB_DEV_OS_PWR_OFF;
1355ef772e54Sfb
1356c77a61a7Syz /* FALLTHRU */
1357c77a61a7Syz case USB_DEV_DISCONNECTED:
1358c77a61a7Syz case USB_DEV_SUSPENDED:
1359c77a61a7Syz /* allow a disconnect/cpr'ed device to go to lower power */
1360c77a61a7Syz
1361c77a61a7Syz return (USB_SUCCESS);
1362c77a61a7Syz case USB_DEV_PWRED_DOWN:
1363c77a61a7Syz default:
1364c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1365c77a61a7Syz "usbvc_pwrlvl0: illegal dev state");
1366c77a61a7Syz
1367c77a61a7Syz return (USB_FAILURE);
1368c77a61a7Syz }
1369c77a61a7Syz }
1370c77a61a7Syz
1371c77a61a7Syz
1372c77a61a7Syz /*
1373c77a61a7Syz * usbvc_pwrlvl1:
1374c77a61a7Syz * Functions to handle power transition to OS levels -> 2
1375c77a61a7Syz */
1376c77a61a7Syz static int
usbvc_pwrlvl1(usbvc_state_t * usbvcp)1377c77a61a7Syz usbvc_pwrlvl1(usbvc_state_t *usbvcp)
1378c77a61a7Syz {
1379c77a61a7Syz int rval;
1380c77a61a7Syz
1381c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1382c77a61a7Syz "usbvc_pwrlvl1");
1383c77a61a7Syz
1384c77a61a7Syz /* Issue USB D2 command to the device here */
1385c77a61a7Syz rval = usb_set_device_pwrlvl2(usbvcp->usbvc_dip);
1386c77a61a7Syz ASSERT(rval == USB_SUCCESS);
1387c77a61a7Syz
1388c77a61a7Syz return (USB_FAILURE);
1389c77a61a7Syz }
1390c77a61a7Syz
1391c77a61a7Syz
1392c77a61a7Syz /*
1393c77a61a7Syz * usbvc_pwrlvl2:
1394c77a61a7Syz * Functions to handle power transition to OS levels -> 1
1395c77a61a7Syz */
1396c77a61a7Syz static int
usbvc_pwrlvl2(usbvc_state_t * usbvcp)1397c77a61a7Syz usbvc_pwrlvl2(usbvc_state_t *usbvcp)
1398c77a61a7Syz {
1399c77a61a7Syz int rval;
1400c77a61a7Syz
1401c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1402c77a61a7Syz "usbvc_pwrlvl2");
1403c77a61a7Syz
1404c77a61a7Syz /* Issue USB D1 command to the device here */
1405c77a61a7Syz rval = usb_set_device_pwrlvl1(usbvcp->usbvc_dip);
1406c77a61a7Syz ASSERT(rval == USB_SUCCESS);
1407c77a61a7Syz
1408c77a61a7Syz return (USB_FAILURE);
1409c77a61a7Syz }
1410c77a61a7Syz
1411c77a61a7Syz
1412c77a61a7Syz /*
1413c77a61a7Syz * usbvc_pwrlvl3:
1414c77a61a7Syz * Functions to handle power transition to OS level -> 0
1415c77a61a7Syz */
1416c77a61a7Syz static int
usbvc_pwrlvl3(usbvc_state_t * usbvcp)1417c77a61a7Syz usbvc_pwrlvl3(usbvc_state_t *usbvcp)
1418c77a61a7Syz {
1419c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1420c77a61a7Syz "usbvc_pwrlvl3, dev_stat=%d", usbvcp->usbvc_dev_state);
1421c77a61a7Syz
1422c77a61a7Syz switch (usbvcp->usbvc_dev_state) {
1423c77a61a7Syz case USB_DEV_PWRED_DOWN:
1424c77a61a7Syz /* Issue USB D0 command to the device here */
1425c77a61a7Syz (void) usb_set_device_pwrlvl0(usbvcp->usbvc_dip);
1426c77a61a7Syz
1427c77a61a7Syz usbvcp->usbvc_dev_state = USB_DEV_ONLINE;
1428c77a61a7Syz usbvcp->usbvc_pm->usbvc_current_power =
1429c77a61a7Syz USB_DEV_OS_FULL_PWR;
1430c77a61a7Syz
1431c77a61a7Syz /* FALLTHRU */
1432c77a61a7Syz case USB_DEV_ONLINE:
1433c77a61a7Syz /* we are already in full power */
1434c77a61a7Syz /* FALLTHRU */
1435c77a61a7Syz case USB_DEV_DISCONNECTED:
1436c77a61a7Syz case USB_DEV_SUSPENDED:
1437c77a61a7Syz /*
1438c77a61a7Syz * PM framework tries to put us in full power
1439c77a61a7Syz * during system shutdown. If we are disconnected/cpr'ed
1440c77a61a7Syz * return success anyways
1441c77a61a7Syz */
1442c77a61a7Syz
1443c77a61a7Syz return (USB_SUCCESS);
1444c77a61a7Syz default:
1445c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1446c77a61a7Syz "usbvc_pwrlvl3: illegal dev state");
1447c77a61a7Syz
1448c77a61a7Syz return (USB_FAILURE);
1449c77a61a7Syz }
1450c77a61a7Syz }
1451c77a61a7Syz
1452c77a61a7Syz
1453c77a61a7Syz /*
1454c77a61a7Syz * usbvc_cpr_suspend:
1455c77a61a7Syz * Clean up device.
1456c77a61a7Syz * Wait for any IO to finish, then close pipes.
1457c77a61a7Syz * Quiesce device.
1458c77a61a7Syz */
1459c77a61a7Syz static void
usbvc_cpr_suspend(dev_info_t * dip)1460c77a61a7Syz usbvc_cpr_suspend(dev_info_t *dip)
1461c77a61a7Syz {
1462c77a61a7Syz int instance = ddi_get_instance(dip);
1463ef772e54Sfb usbvc_state_t *usbvcp = ddi_get_soft_state(usbvc_statep, instance);
1464c77a61a7Syz
1465c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1466c77a61a7Syz "usbvc_cpr_suspend enter");
1467c77a61a7Syz
1468c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
1469c77a61a7Syz
1470c77a61a7Syz /*
1471c77a61a7Syz * Set dev_state to suspended so other driver threads don't start any
1472c77a61a7Syz * new I/O.
1473c77a61a7Syz */
1474c77a61a7Syz usbvcp->usbvc_dev_state = USB_DEV_SUSPENDED;
1475c77a61a7Syz
1476c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
1477c77a61a7Syz
1478c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
1479c77a61a7Syz "usbvc_cpr_suspend: return");
1480c77a61a7Syz }
1481c77a61a7Syz
1482c77a61a7Syz
148309dd0d6cSRaymond Chen /*
148409dd0d6cSRaymond Chen * If the polling has been stopped due to some exceptional errors,
148509dd0d6cSRaymond Chen * we reconfigure the device and start polling again. Only for S/R
148609dd0d6cSRaymond Chen * resume or hotplug reconnect operations.
148709dd0d6cSRaymond Chen */
148809dd0d6cSRaymond Chen static int
usbvc_resume_operation(usbvc_state_t * usbvcp)148909dd0d6cSRaymond Chen usbvc_resume_operation(usbvc_state_t *usbvcp)
149009dd0d6cSRaymond Chen {
149109dd0d6cSRaymond Chen usbvc_stream_if_t *strm_if;
149209dd0d6cSRaymond Chen int rv = USB_SUCCESS;
149309dd0d6cSRaymond Chen
149409dd0d6cSRaymond Chen USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
149509dd0d6cSRaymond Chen "usbvc_resume_operation: enter");
149609dd0d6cSRaymond Chen
149709dd0d6cSRaymond Chen mutex_enter(&usbvcp->usbvc_mutex);
149809dd0d6cSRaymond Chen strm_if = usbvcp->usbvc_curr_strm;
149909dd0d6cSRaymond Chen if (!strm_if) {
150009dd0d6cSRaymond Chen mutex_exit(&usbvcp->usbvc_mutex);
150109dd0d6cSRaymond Chen rv = USB_FAILURE;
150209dd0d6cSRaymond Chen
150309dd0d6cSRaymond Chen return (rv);
150409dd0d6cSRaymond Chen }
150509dd0d6cSRaymond Chen
150609dd0d6cSRaymond Chen /*
150709dd0d6cSRaymond Chen * 1) if application has not started STREAMON ioctl yet,
150809dd0d6cSRaymond Chen * just return
150909dd0d6cSRaymond Chen * 2) if application use READ mode, return immediately
151009dd0d6cSRaymond Chen */
151109dd0d6cSRaymond Chen if (strm_if->stream_on == 0) {
151209dd0d6cSRaymond Chen mutex_exit(&usbvcp->usbvc_mutex);
151309dd0d6cSRaymond Chen
151409dd0d6cSRaymond Chen return (rv);
151509dd0d6cSRaymond Chen }
151609dd0d6cSRaymond Chen
151709dd0d6cSRaymond Chen /* isoc pipe is expected to be opened already if (stream_on==1) */
151809dd0d6cSRaymond Chen if (!strm_if->datain_ph) {
151909dd0d6cSRaymond Chen mutex_exit(&usbvcp->usbvc_mutex);
152009dd0d6cSRaymond Chen rv = USB_FAILURE;
152109dd0d6cSRaymond Chen
152209dd0d6cSRaymond Chen return (rv);
152309dd0d6cSRaymond Chen }
152409dd0d6cSRaymond Chen
152509dd0d6cSRaymond Chen mutex_exit(&usbvcp->usbvc_mutex);
152609dd0d6cSRaymond Chen
152709dd0d6cSRaymond Chen /* first commit the parameters negotiated and saved during S_FMT */
152809dd0d6cSRaymond Chen if ((rv = usbvc_vs_set_probe_commit(usbvcp, strm_if,
152909dd0d6cSRaymond Chen &strm_if->ctrl_pc, VS_COMMIT_CONTROL)) != USB_SUCCESS) {
153009dd0d6cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_IOCTL,
153109dd0d6cSRaymond Chen usbvcp->usbvc_log_handle,
153209dd0d6cSRaymond Chen "usbvc_resume_operation: set probe failed, rv=%d", rv);
153309dd0d6cSRaymond Chen
153409dd0d6cSRaymond Chen return (rv);
153509dd0d6cSRaymond Chen }
153609dd0d6cSRaymond Chen
153709dd0d6cSRaymond Chen mutex_enter(&usbvcp->usbvc_mutex);
153809dd0d6cSRaymond Chen
153909dd0d6cSRaymond Chen /* Set alt interfaces, must be after probe_commit according to spec */
154009dd0d6cSRaymond Chen if ((rv = usbvc_set_alt(usbvcp, strm_if)) != USB_SUCCESS) {
154109dd0d6cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_IOCTL,
154209dd0d6cSRaymond Chen usbvcp->usbvc_log_handle,
154309dd0d6cSRaymond Chen "usbvc_resume_operation: set alt failed");
154409dd0d6cSRaymond Chen mutex_exit(&usbvcp->usbvc_mutex);
154509dd0d6cSRaymond Chen
154609dd0d6cSRaymond Chen return (rv);
154709dd0d6cSRaymond Chen }
154809dd0d6cSRaymond Chen
154909dd0d6cSRaymond Chen /*
155009dd0d6cSRaymond Chen * The isoc polling could be stopped by isoc_exc_cb
155109dd0d6cSRaymond Chen * during suspend or hotplug. Restart it.
155209dd0d6cSRaymond Chen */
155309dd0d6cSRaymond Chen if (usbvc_start_isoc_polling(usbvcp, strm_if, V4L2_MEMORY_MMAP)
155409dd0d6cSRaymond Chen != USB_SUCCESS) {
155509dd0d6cSRaymond Chen rv = USB_FAILURE;
155609dd0d6cSRaymond Chen mutex_exit(&usbvcp->usbvc_mutex);
155709dd0d6cSRaymond Chen
155809dd0d6cSRaymond Chen return (rv);
155909dd0d6cSRaymond Chen }
156009dd0d6cSRaymond Chen
156109dd0d6cSRaymond Chen strm_if->start_polling = 1;
156209dd0d6cSRaymond Chen
156309dd0d6cSRaymond Chen mutex_exit(&usbvcp->usbvc_mutex);
156409dd0d6cSRaymond Chen
156509dd0d6cSRaymond Chen return (rv);
156609dd0d6cSRaymond Chen }
156709dd0d6cSRaymond Chen
1568c77a61a7Syz /*
1569c77a61a7Syz * usbvc_cpr_resume:
1570c77a61a7Syz *
1571c77a61a7Syz * usbvc_restore_device_state marks success by putting device back online
1572c77a61a7Syz */
1573c77a61a7Syz static void
usbvc_cpr_resume(dev_info_t * dip)1574c77a61a7Syz usbvc_cpr_resume(dev_info_t *dip)
1575c77a61a7Syz {
1576c77a61a7Syz int instance = ddi_get_instance(dip);
1577ef772e54Sfb usbvc_state_t *usbvcp = ddi_get_soft_state(usbvc_statep, instance);
1578c77a61a7Syz
1579c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
1580c77a61a7Syz "resume: enter");
1581c77a61a7Syz
1582c77a61a7Syz /*
1583c77a61a7Syz * NOTE: A pm_raise_power in usbvc_restore_device_state will bring
1584c77a61a7Syz * the power-up state of device into synch with the system.
1585c77a61a7Syz */
1586c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
1587c77a61a7Syz usbvc_restore_device_state(dip, usbvcp);
1588c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
1589c77a61a7Syz }
1590c77a61a7Syz
1591c77a61a7Syz
1592c77a61a7Syz /*
1593112116d8Sfb * usbvc_restore_device_state:
1594c77a61a7Syz * Called during hotplug-reconnect and resume.
1595c77a61a7Syz * reenable power management
1596c77a61a7Syz * Verify the device is the same as before the disconnect/suspend.
1597c77a61a7Syz * Restore device state
1598c77a61a7Syz * Thaw any IO which was frozen.
1599c77a61a7Syz * Quiesce device. (Other routines will activate if thawed IO.)
1600c77a61a7Syz * Set device online.
1601c77a61a7Syz * Leave device disconnected if there are problems.
1602c77a61a7Syz */
1603c77a61a7Syz static void
usbvc_restore_device_state(dev_info_t * dip,usbvc_state_t * usbvcp)1604c77a61a7Syz usbvc_restore_device_state(dev_info_t *dip, usbvc_state_t *usbvcp)
1605c77a61a7Syz {
1606c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1607c77a61a7Syz "usbvc_restore_device_state: enter");
1608c77a61a7Syz
1609c77a61a7Syz ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
1610c77a61a7Syz
1611c77a61a7Syz ASSERT((usbvcp->usbvc_dev_state == USB_DEV_DISCONNECTED) ||
1612c77a61a7Syz (usbvcp->usbvc_dev_state == USB_DEV_SUSPENDED));
1613c77a61a7Syz
1614c77a61a7Syz usbvc_pm_busy_component(usbvcp);
1615c77a61a7Syz usbvcp->usbvc_pm->usbvc_raise_power = B_TRUE;
1616c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
1617c77a61a7Syz (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
1618c77a61a7Syz
1619c77a61a7Syz /* Check if we are talking to the same device */
1620c77a61a7Syz if (usb_check_same_device(dip, usbvcp->usbvc_log_handle,
1621c77a61a7Syz USB_LOG_L0, PRINT_MASK_ALL,
1622c77a61a7Syz USB_CHK_BASIC|USB_CHK_CFG, NULL) != USB_SUCCESS) {
1623c77a61a7Syz
1624c77a61a7Syz goto fail;
1625c77a61a7Syz }
1626c77a61a7Syz
1627c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
1628c77a61a7Syz usbvcp->usbvc_pm->usbvc_raise_power = B_FALSE;
1629c77a61a7Syz usbvcp->usbvc_dev_state = USB_DEV_ONLINE;
1630c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
1631c77a61a7Syz
1632c77a61a7Syz if (usbvcp->usbvc_pm->usbvc_wakeup_enabled) {
1633c77a61a7Syz
1634c77a61a7Syz /* Failure here means device disappeared again. */
1635c77a61a7Syz if (usb_handle_remote_wakeup(dip, USB_REMOTE_WAKEUP_ENABLE) !=
1636c77a61a7Syz USB_SUCCESS) {
1637c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_ATTA,
1638c77a61a7Syz usbvcp->usbvc_log_handle,
1639c77a61a7Syz "device may or may not be accessible. "
1640c77a61a7Syz "Please verify reconnection");
1641c77a61a7Syz }
1642c77a61a7Syz }
164309dd0d6cSRaymond Chen
164409dd0d6cSRaymond Chen if (usbvc_resume_operation(usbvcp) != USB_SUCCESS) {
164509dd0d6cSRaymond Chen USB_DPRINTF_L2(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
164609dd0d6cSRaymond Chen "usbvc_restore_device_state: can't resume operation");
164709dd0d6cSRaymond Chen
164809dd0d6cSRaymond Chen goto fail;
164909dd0d6cSRaymond Chen }
165009dd0d6cSRaymond Chen
1651c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
1652c77a61a7Syz
1653c77a61a7Syz usbvc_pm_idle_component(usbvcp);
1654c77a61a7Syz
1655c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_PM, usbvcp->usbvc_log_handle,
1656c77a61a7Syz "usbvc_restore_device_state: end");
1657c77a61a7Syz
1658c77a61a7Syz return;
1659c77a61a7Syz
1660c77a61a7Syz fail:
1661c77a61a7Syz /* change the device state from suspended to disconnected */
1662c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
1663c77a61a7Syz usbvcp->usbvc_dev_state = USB_DEV_DISCONNECTED;
1664c77a61a7Syz usbvc_pm_idle_component(usbvcp);
1665c77a61a7Syz }
1666c77a61a7Syz
1667c77a61a7Syz
1668c77a61a7Syz /* Events */
1669c77a61a7Syz
1670c77a61a7Syz /*
1671c77a61a7Syz * usbvc_disconnect_event_cb:
1672c77a61a7Syz * Called when device hotplug-removed.
1673c77a61a7Syz * Close pipes. (This does not attempt to contact device.)
1674c77a61a7Syz * Set state to DISCONNECTED
1675c77a61a7Syz */
1676c77a61a7Syz static int
usbvc_disconnect_event_cb(dev_info_t * dip)1677c77a61a7Syz usbvc_disconnect_event_cb(dev_info_t *dip)
1678c77a61a7Syz {
1679c77a61a7Syz int instance = ddi_get_instance(dip);
1680ef772e54Sfb usbvc_state_t *usbvcp = ddi_get_soft_state(usbvc_statep, instance);
1681c77a61a7Syz
1682c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_HOTPLUG, usbvcp->usbvc_log_handle,
1683c77a61a7Syz "disconnect: enter");
1684c77a61a7Syz
1685c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
1686c77a61a7Syz /*
1687c77a61a7Syz * Save any state of device or IO in progress required by
1688c77a61a7Syz * usbvc_restore_device_state for proper device "thawing" later.
1689c77a61a7Syz */
1690c77a61a7Syz usbvcp->usbvc_dev_state = USB_DEV_DISCONNECTED;
1691c77a61a7Syz
1692c77a61a7Syz /*
1693c77a61a7Syz * wake up the read threads in case there are any threads are blocking,
1694c77a61a7Syz * after being waked up, those threads will quit fail immediately since
1695c77a61a7Syz * we have changed the dev_stat.
1696c77a61a7Syz */
1697c77a61a7Syz if (usbvcp->usbvc_io_type == V4L2_MEMORY_MMAP) {
1698c77a61a7Syz cv_broadcast(&usbvcp->usbvc_mapio_cv);
1699c77a61a7Syz } else {
1700c77a61a7Syz cv_broadcast(&usbvcp->usbvc_read_cv);
1701c77a61a7Syz }
1702c77a61a7Syz /* Wait for the other threads to quit */
1703c77a61a7Syz (void) usbvc_serialize_access(usbvcp, USBVC_SER_SIG);
1704c77a61a7Syz usbvc_release_access(usbvcp);
1705c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
1706c77a61a7Syz
1707c77a61a7Syz return (USB_SUCCESS);
1708c77a61a7Syz }
1709c77a61a7Syz
1710c77a61a7Syz
1711c77a61a7Syz /*
1712c77a61a7Syz * usbvc_reconnect_event_cb:
1713c77a61a7Syz * Called with device hotplug-inserted
1714c77a61a7Syz * Restore state
1715c77a61a7Syz */
1716c77a61a7Syz static int
usbvc_reconnect_event_cb(dev_info_t * dip)1717c77a61a7Syz usbvc_reconnect_event_cb(dev_info_t *dip)
1718c77a61a7Syz {
1719c77a61a7Syz int instance = ddi_get_instance(dip);
1720c77a61a7Syz usbvc_state_t *usbvcp = ddi_get_soft_state(usbvc_statep, instance);
1721c77a61a7Syz
1722c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_HOTPLUG, usbvcp->usbvc_log_handle,
1723c77a61a7Syz "reconnect: enter");
1724c77a61a7Syz
1725c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
1726c77a61a7Syz (void) usbvc_serialize_access(usbvcp, USBVC_SER_SIG);
1727c77a61a7Syz usbvc_restore_device_state(dip, usbvcp);
1728c77a61a7Syz usbvc_release_access(usbvcp);
1729c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
1730c77a61a7Syz
1731c77a61a7Syz return (USB_SUCCESS);
1732c77a61a7Syz }
1733c77a61a7Syz
1734c77a61a7Syz /* Sync objs and lists */
1735c77a61a7Syz
1736c77a61a7Syz /*
1737c77a61a7Syz * init/fini sync objects during attach
1738c77a61a7Syz */
1739c77a61a7Syz static void
usbvc_init_sync_objs(usbvc_state_t * usbvcp)1740c77a61a7Syz usbvc_init_sync_objs(usbvc_state_t *usbvcp)
1741c77a61a7Syz {
1742c77a61a7Syz mutex_init(&usbvcp->usbvc_mutex, NULL, MUTEX_DRIVER,
1743ef772e54Sfb usbvcp->usbvc_reg->dev_iblock_cookie);
1744c77a61a7Syz
1745c77a61a7Syz cv_init(&usbvcp->usbvc_serial_cv, NULL, CV_DRIVER, NULL);
1746c77a61a7Syz cv_init(&usbvcp->usbvc_read_cv, NULL, CV_DRIVER, NULL);
1747c77a61a7Syz cv_init(&usbvcp->usbvc_mapio_cv, NULL, CV_DRIVER, NULL);
1748c77a61a7Syz
1749c77a61a7Syz usbvcp->usbvc_serial_inuse = B_FALSE;
1750c77a61a7Syz
1751c77a61a7Syz usbvcp->usbvc_locks_initialized = B_TRUE;
1752c77a61a7Syz }
1753c77a61a7Syz
1754c77a61a7Syz
1755c77a61a7Syz static void
usbvc_fini_sync_objs(usbvc_state_t * usbvcp)1756c77a61a7Syz usbvc_fini_sync_objs(usbvc_state_t *usbvcp)
1757c77a61a7Syz {
1758c77a61a7Syz cv_destroy(&usbvcp->usbvc_serial_cv);
1759c77a61a7Syz cv_destroy(&usbvcp->usbvc_read_cv);
1760c77a61a7Syz cv_destroy(&usbvcp->usbvc_mapio_cv);
1761c77a61a7Syz
1762c77a61a7Syz mutex_destroy(&usbvcp->usbvc_mutex);
1763c77a61a7Syz }
1764c77a61a7Syz
1765c77a61a7Syz
1766c77a61a7Syz static void
usbvc_init_lists(usbvc_state_t * usbvcp)1767c77a61a7Syz usbvc_init_lists(usbvc_state_t *usbvcp)
1768c77a61a7Syz {
1769c77a61a7Syz /* video terminals */
1770c77a61a7Syz list_create(&(usbvcp->usbvc_term_list), sizeof (usbvc_terms_t),
1771c77a61a7Syz offsetof(usbvc_terms_t, term_node));
1772c77a61a7Syz
1773c77a61a7Syz /* video units */
1774c77a61a7Syz list_create(&(usbvcp->usbvc_unit_list), sizeof (usbvc_units_t),
1775c77a61a7Syz offsetof(usbvc_units_t, unit_node));
1776c77a61a7Syz
1777c77a61a7Syz /* stream interfaces */
1778c77a61a7Syz list_create(&(usbvcp->usbvc_stream_list), sizeof (usbvc_stream_if_t),
1779c77a61a7Syz offsetof(usbvc_stream_if_t, stream_if_node));
1780c77a61a7Syz }
1781c77a61a7Syz
1782c77a61a7Syz
1783c77a61a7Syz /*
1784c77a61a7Syz * Free all the data structures allocated when parsing descriptors of ctrl
1785c77a61a7Syz * and stream interfaces. It is safe to call this function because it always
1786c77a61a7Syz * checks the pointer before free mem.
1787c77a61a7Syz */
1788c77a61a7Syz static void
usbvc_fini_lists(usbvc_state_t * usbvcp)1789c77a61a7Syz usbvc_fini_lists(usbvc_state_t *usbvcp)
1790c77a61a7Syz {
1791c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
1792c77a61a7Syz "usbvc_fini_lists: enter");
1793c77a61a7Syz
1794c77a61a7Syz usbvc_free_ctrl_descr(usbvcp);
1795c77a61a7Syz
1796c77a61a7Syz /* Free all video stream structure and the sub-structures */
1797c77a61a7Syz usbvc_free_stream_descr(usbvcp);
1798c77a61a7Syz
1799c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
1800c77a61a7Syz "usbvc_fini_lists: end");
1801c77a61a7Syz }
1802c77a61a7Syz
1803c77a61a7Syz
1804c77a61a7Syz /*
1805c77a61a7Syz * Free all the data structures allocated when parsing descriptors of ctrl
1806c77a61a7Syz * interface.
1807c77a61a7Syz */
1808c77a61a7Syz static void
usbvc_free_ctrl_descr(usbvc_state_t * usbvcp)1809c77a61a7Syz usbvc_free_ctrl_descr(usbvc_state_t *usbvcp)
1810c77a61a7Syz {
1811c77a61a7Syz usbvc_terms_t *term;
1812c77a61a7Syz usbvc_units_t *unit;
1813c77a61a7Syz
1814c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
1815c77a61a7Syz "usbvc_free_ctrl_descr: enter");
1816c77a61a7Syz
1817c77a61a7Syz if (usbvcp->usbvc_vc_header) {
1818c77a61a7Syz kmem_free(usbvcp->usbvc_vc_header, sizeof (usbvc_vc_header_t));
1819c77a61a7Syz }
1820c77a61a7Syz
1821c77a61a7Syz /* Free all video terminal structure */
1822c77a61a7Syz while (!list_is_empty(&usbvcp->usbvc_term_list)) {
1823c77a61a7Syz term = list_head(&usbvcp->usbvc_term_list);
1824c77a61a7Syz if (term != NULL) {
1825c77a61a7Syz list_remove(&(usbvcp->usbvc_term_list), term);
1826c77a61a7Syz kmem_free(term, sizeof (usbvc_terms_t));
1827c77a61a7Syz }
1828c77a61a7Syz }
1829c77a61a7Syz
1830c77a61a7Syz /* Free all video unit structure */
1831c77a61a7Syz while (!list_is_empty(&usbvcp->usbvc_unit_list)) {
1832c77a61a7Syz unit = list_head(&usbvcp->usbvc_unit_list);
1833c77a61a7Syz if (unit != NULL) {
1834c77a61a7Syz list_remove(&(usbvcp->usbvc_unit_list), unit);
1835c77a61a7Syz kmem_free(unit, sizeof (usbvc_units_t));
1836c77a61a7Syz }
1837c77a61a7Syz }
1838c77a61a7Syz }
1839c77a61a7Syz
1840c77a61a7Syz
1841c77a61a7Syz /*
1842c77a61a7Syz * Free all the data structures allocated when parsing descriptors of stream
1843c77a61a7Syz * interfaces.
1844c77a61a7Syz */
1845c77a61a7Syz static void
usbvc_free_stream_descr(usbvc_state_t * usbvcp)1846c77a61a7Syz usbvc_free_stream_descr(usbvc_state_t *usbvcp)
1847c77a61a7Syz {
1848c77a61a7Syz usbvc_stream_if_t *strm;
1849c77a61a7Syz usbvc_input_header_t *in_hdr;
1850c77a61a7Syz usbvc_output_header_t *out_hdr;
1851c77a61a7Syz uint8_t fmt_cnt, frm_cnt;
1852c77a61a7Syz
1853c77a61a7Syz while (!list_is_empty(&usbvcp->usbvc_stream_list)) {
1854c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
1855c77a61a7Syz "usbvc_fini_lists: stream list not empty.");
1856c77a61a7Syz
1857c77a61a7Syz strm = list_head(&usbvcp->usbvc_stream_list);
1858c77a61a7Syz if (strm != NULL) {
1859c77a61a7Syz
1860c77a61a7Syz /* unlink this stream's data structure from the list */
1861c77a61a7Syz list_remove(&(usbvcp->usbvc_stream_list), strm);
1862c77a61a7Syz } else {
1863c77a61a7Syz
1864c77a61a7Syz /* No real stream data structure in the list */
1865c77a61a7Syz return;
1866c77a61a7Syz }
1867c77a61a7Syz
1868c77a61a7Syz in_hdr = strm->input_header;
1869c77a61a7Syz out_hdr = strm->output_header;
1870c77a61a7Syz
1871c77a61a7Syz if (in_hdr) {
1872c77a61a7Syz fmt_cnt = in_hdr->descr->bNumFormats;
1873c77a61a7Syz } else if (out_hdr) {
1874c77a61a7Syz fmt_cnt = out_hdr->descr->bNumFormats;
1875c77a61a7Syz }
1876c77a61a7Syz
1877c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_CLOSE,
1878c77a61a7Syz usbvcp->usbvc_log_handle, "usbvc_fini_lists:"
1879c77a61a7Syz " fmtgrp cnt=%d", fmt_cnt);
1880c77a61a7Syz
1881c77a61a7Syz /* Free headers */
1882c77a61a7Syz if (in_hdr) {
1883c77a61a7Syz kmem_free(in_hdr, sizeof (usbvc_input_header_t));
1884c77a61a7Syz }
1885c77a61a7Syz if (out_hdr) {
1886c77a61a7Syz kmem_free(out_hdr, sizeof (usbvc_output_header_t));
1887c77a61a7Syz }
1888c77a61a7Syz
1889c77a61a7Syz /* Free format descriptors */
1890c77a61a7Syz if (strm->format_group) {
1891c77a61a7Syz int i;
1892c77a61a7Syz usbvc_format_group_t *fmtgrp;
1893c77a61a7Syz
1894c77a61a7Syz for (i = 0; i < fmt_cnt; i++) {
1895c77a61a7Syz fmtgrp = &strm->format_group[i];
1896c77a61a7Syz if (fmtgrp->format == NULL) {
1897c77a61a7Syz
1898c77a61a7Syz break;
1899c77a61a7Syz }
1900c77a61a7Syz if (fmtgrp->still) {
1901c77a61a7Syz kmem_free(fmtgrp->still,
1902c77a61a7Syz sizeof (usbvc_still_image_frame_t));
1903c77a61a7Syz }
1904c77a61a7Syz frm_cnt = fmtgrp->format->bNumFrameDescriptors;
1905c77a61a7Syz
1906c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_CLOSE,
1907c77a61a7Syz usbvcp->usbvc_log_handle,
1908c77a61a7Syz "usbvc_fini_lists:"
1909c77a61a7Syz " frame cnt=%d", frm_cnt);
1910c77a61a7Syz
1911c77a61a7Syz if (fmtgrp->frames) {
1912c77a61a7Syz kmem_free(fmtgrp->frames,
1913c77a61a7Syz sizeof (usbvc_frames_t) * frm_cnt);
1914c77a61a7Syz }
1915c77a61a7Syz }
1916c77a61a7Syz kmem_free(strm->format_group,
1917c77a61a7Syz sizeof (usbvc_format_group_t) * fmt_cnt);
1918c77a61a7Syz }
1919c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_CLOSE,
1920c77a61a7Syz usbvcp->usbvc_log_handle, "usbvc_fini_lists:"
1921c77a61a7Syz " free stream_if_t");
1922c77a61a7Syz
1923c77a61a7Syz kmem_free(strm, sizeof (usbvc_stream_if_t));
1924c77a61a7Syz }
1925c77a61a7Syz }
1926c77a61a7Syz
1927c77a61a7Syz /*
1928c77a61a7Syz * Parse class specific descriptors of the video device
1929c77a61a7Syz */
1930c77a61a7Syz
1931c77a61a7Syz /*
1932c77a61a7Syz * Check the length of a class specific descriptor. Make sure cvs_buf_len is
1933c77a61a7Syz * not less than the length expected according to uvc spec.
1934c77a61a7Syz *
1935c77a61a7Syz * Args:
1936c77a61a7Syz * - off_num: the cvs_buf offset of the descriptor element that
1937c77a61a7Syz * indicates the number of variable descriptor elements;
1938c77a61a7Syz * - size: the size of each variable descriptor element, if zero, then the
1939c77a61a7Syz * size value is offered by off_size;
1940c77a61a7Syz * - off_size: the cvs_buf offset of the descriptor element that indicates
1941c77a61a7Syz * the size of each variable descriptor element;
1942c77a61a7Syz */
1943c77a61a7Syz static int
usbvc_chk_descr_len(uint8_t off_num,uint8_t size,uint8_t off_size,usb_cvs_data_t * cvs_data)1944c77a61a7Syz usbvc_chk_descr_len(uint8_t off_num, uint8_t size, uint8_t off_size,
1945c77a61a7Syz usb_cvs_data_t *cvs_data)
1946c77a61a7Syz {
1947c77a61a7Syz uchar_t *cvs_buf;
1948c77a61a7Syz uint_t cvs_buf_len;
1949c77a61a7Syz
1950c77a61a7Syz cvs_buf = cvs_data->cvs_buf;
1951c77a61a7Syz cvs_buf_len = cvs_data->cvs_buf_len;
1952c77a61a7Syz
1953c77a61a7Syz if (size == 0) {
1954c77a61a7Syz if (cvs_buf_len > off_size) {
1955c77a61a7Syz size = cvs_buf[off_size];
1956c77a61a7Syz } else {
1957c77a61a7Syz
1958c77a61a7Syz return (USB_FAILURE);
1959c77a61a7Syz }
1960c77a61a7Syz }
1961c77a61a7Syz if (cvs_buf_len < (off_num + 1)) {
1962c77a61a7Syz
1963c77a61a7Syz return (USB_FAILURE);
1964c77a61a7Syz }
1965c77a61a7Syz
1966c77a61a7Syz if (cvs_buf_len < (cvs_buf[off_num] * size + off_num +1)) {
1967c77a61a7Syz
1968c77a61a7Syz return (USB_FAILURE);
1969c77a61a7Syz }
1970c77a61a7Syz
1971c77a61a7Syz return (USB_SUCCESS);
1972c77a61a7Syz }
1973c77a61a7Syz
1974c77a61a7Syz
1975c77a61a7Syz /* Parse the descriptors of control interface */
1976c77a61a7Syz static int
usbvc_parse_ctrl_if(usbvc_state_t * usbvcp)1977c77a61a7Syz usbvc_parse_ctrl_if(usbvc_state_t *usbvcp)
1978c77a61a7Syz {
1979c77a61a7Syz int if_num;
1980c77a61a7Syz int cvs_num;
1981c77a61a7Syz usb_alt_if_data_t *if_alt_data;
1982c77a61a7Syz usb_cvs_data_t *cvs_data;
1983c77a61a7Syz uchar_t *cvs_buf;
1984c77a61a7Syz uint_t cvs_buf_len;
1985c77a61a7Syz uint16_t version;
1986c77a61a7Syz
1987c77a61a7Syz if_num = usbvcp->usbvc_reg->dev_curr_if;
1988c77a61a7Syz if_alt_data = usbvcp->usbvc_reg->dev_curr_cfg->cfg_if[if_num].if_alt;
1989c77a61a7Syz cvs_data = if_alt_data->altif_cvs;
1990c77a61a7Syz
1991c77a61a7Syz for (cvs_num = 0; cvs_num < if_alt_data->altif_n_cvs; cvs_num++) {
1992c77a61a7Syz cvs_buf = cvs_data[cvs_num].cvs_buf;
1993c77a61a7Syz cvs_buf_len = cvs_data[cvs_num].cvs_buf_len;
1994c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
1995c77a61a7Syz "usbvc_parse_ctrl_if: cvs_num= %d, cvs_buf_len=%d",
1996c77a61a7Syz cvs_num, cvs_buf_len);
1997c77a61a7Syz
1998c77a61a7Syz /*
1999c77a61a7Syz * parse interface cvs descriptors here; by checking
2000c77a61a7Syz * bDescriptorType (cvs_buf[1])
2001c77a61a7Syz */
2002c77a61a7Syz if (cvs_buf[1] != CS_INTERFACE) {
2003c77a61a7Syz
2004c77a61a7Syz continue;
2005c77a61a7Syz }
2006c77a61a7Syz
2007c77a61a7Syz /*
2008c77a61a7Syz * Different descriptors in VC interface; according to
2009c77a61a7Syz * bDescriptorSubType (cvs_buf[2])
2010c77a61a7Syz */
2011c77a61a7Syz switch (cvs_buf[2]) {
2012c77a61a7Syz case VC_HEADER:
2013c77a61a7Syz
2014c77a61a7Syz /*
2015c77a61a7Syz * According to uvc spec, there must be one and only
2016c77a61a7Syz * be one header. If more than one, return failure.
2017c77a61a7Syz */
2018c77a61a7Syz if (usbvcp->usbvc_vc_header) {
2019c77a61a7Syz
2020c77a61a7Syz return (USB_FAILURE);
2021c77a61a7Syz }
2022c77a61a7Syz /*
2023c77a61a7Syz * Check if it is a valid HEADER descriptor in case of
2024c77a61a7Syz * a device not compliant to uvc spec. This descriptor
2025c77a61a7Syz * is critical, return failure if not a valid one.
2026c77a61a7Syz */
2027c77a61a7Syz if (usbvc_chk_descr_len(11, 1, 0, cvs_data) !=
2028c77a61a7Syz USB_SUCCESS) {
2029c77a61a7Syz
2030c77a61a7Syz return (USB_FAILURE);
2031c77a61a7Syz }
2032c77a61a7Syz usbvcp->usbvc_vc_header =
2033c77a61a7Syz (usbvc_vc_header_t *)kmem_zalloc(
2034c77a61a7Syz sizeof (usbvc_vc_header_t), KM_SLEEP);
2035c77a61a7Syz usbvcp->usbvc_vc_header->descr =
2036ef772e54Sfb (usbvc_vc_header_descr_t *)&cvs_buf[0];
2037c77a61a7Syz
2038c77a61a7Syz LE_TO_UINT16(usbvcp->usbvc_vc_header->descr->bcdUVC,
2039c77a61a7Syz 0, version);
2040c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_ATTA,
2041c77a61a7Syz usbvcp->usbvc_log_handle, "usbvc_parse_ctrl_if:"
2042c77a61a7Syz " VC header, bcdUVC=%x", version);
2043c77a61a7Syz if (usbvcp->usbvc_vc_header->descr->bInCollection ==
2044c77a61a7Syz 0) {
2045c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_ATTA,
2046c77a61a7Syz usbvcp->usbvc_log_handle,
2047c77a61a7Syz "usbvc_parse_ctrl_if: no strm interfaces");
2048c77a61a7Syz
2049c77a61a7Syz break;
2050c77a61a7Syz }
2051c77a61a7Syz
2052c77a61a7Syz /* stream interface numbers */
2053c77a61a7Syz usbvcp->usbvc_vc_header->baInterfaceNr = &cvs_buf[12];
2054c77a61a7Syz
2055c77a61a7Syz break;
2056c77a61a7Syz case VC_INPUT_TERMINAL:
2057c77a61a7Syz {
2058c77a61a7Syz usbvc_terms_t *term;
2059c77a61a7Syz
2060c77a61a7Syz /*
2061c77a61a7Syz * Check if it is a valid descriptor in case of a
2062c77a61a7Syz * device not compliant to uvc spec
2063c77a61a7Syz */
2064c77a61a7Syz if (cvs_buf_len < USBVC_I_TERM_LEN_MIN) {
2065c77a61a7Syz
2066c77a61a7Syz break;
2067c77a61a7Syz }
2068c77a61a7Syz term = (usbvc_terms_t *)
2069c77a61a7Syz kmem_zalloc(sizeof (usbvc_terms_t), KM_SLEEP);
2070c77a61a7Syz term->descr = (usbvc_term_descr_t *)cvs_buf;
2071c77a61a7Syz
2072c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_ATTA,
2073c77a61a7Syz usbvcp->usbvc_log_handle, "usbvc_parse_ctrl_if: "
2074c77a61a7Syz "input term type=%x", term->descr->wTerminalType);
2075c77a61a7Syz if (term->descr->wTerminalType == ITT_CAMERA) {
2076c77a61a7Syz if (usbvc_chk_descr_len(14, 1, 0, cvs_data) !=
2077c77a61a7Syz USB_SUCCESS) {
2078c77a61a7Syz kmem_free(term, sizeof (usbvc_terms_t));
2079c77a61a7Syz
2080c77a61a7Syz break;
2081c77a61a7Syz }
2082c77a61a7Syz term->bmControls = &cvs_buf[15];
2083c77a61a7Syz } else if (cvs_buf_len > 8) { /* other input terms */
2084c77a61a7Syz term->bSpecific = &cvs_buf[8];
2085c77a61a7Syz }
2086c77a61a7Syz list_insert_tail(&(usbvcp->usbvc_term_list), term);
2087c77a61a7Syz
2088c77a61a7Syz break;
2089c77a61a7Syz }
2090c77a61a7Syz case VC_OUTPUT_TERMINAL:
2091c77a61a7Syz {
2092c77a61a7Syz usbvc_terms_t *term;
2093c77a61a7Syz
2094c77a61a7Syz if (cvs_buf_len < USBVC_O_TERM_LEN_MIN) {
2095c77a61a7Syz
2096c77a61a7Syz break;
2097c77a61a7Syz }
2098c77a61a7Syz term = (usbvc_terms_t *)
2099c77a61a7Syz kmem_zalloc(sizeof (usbvc_terms_t), KM_SLEEP);
2100c77a61a7Syz term->descr = (usbvc_term_descr_t *)cvs_buf;
2101c77a61a7Syz
2102c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_ATTA,
2103c77a61a7Syz usbvcp->usbvc_log_handle, "usbvc_parse_ctrl_if:"
2104c77a61a7Syz " output term id= %x", term->descr->bTerminalID);
2105c77a61a7Syz if (cvs_buf_len > 9) {
2106c77a61a7Syz term->bSpecific = &cvs_buf[9];
2107c77a61a7Syz }
2108c77a61a7Syz list_insert_tail(&(usbvcp->usbvc_term_list), term);
2109c77a61a7Syz
2110c77a61a7Syz break;
2111c77a61a7Syz }
2112c77a61a7Syz case VC_PROCESSING_UNIT:
2113c77a61a7Syz {
2114c77a61a7Syz uint8_t sz;
2115c77a61a7Syz usbvc_units_t *unit;
2116c77a61a7Syz
2117c77a61a7Syz if (usbvc_chk_descr_len(7, 1, 0, cvs_data) !=
2118c77a61a7Syz USB_SUCCESS) {
2119c77a61a7Syz
2120c77a61a7Syz break;
2121c77a61a7Syz }
2122c77a61a7Syz
2123c77a61a7Syz /* bControlSize */
2124c77a61a7Syz sz = cvs_buf[7];
2125c77a61a7Syz
2126c77a61a7Syz if ((sz + 8) >= cvs_buf_len) {
2127c77a61a7Syz
2128c77a61a7Syz break;
2129c77a61a7Syz }
2130c77a61a7Syz unit = (usbvc_units_t *)
2131c77a61a7Syz kmem_zalloc(sizeof (usbvc_units_t), KM_SLEEP);
2132c77a61a7Syz
2133c77a61a7Syz unit->descr = (usbvc_unit_descr_t *)cvs_buf;
2134c77a61a7Syz
2135c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_ATTA,
2136c77a61a7Syz usbvcp->usbvc_log_handle, "usbvc_parse_ctrl_if: "
2137c77a61a7Syz "unit type=%x", unit->descr->bDescriptorSubType);
2138c77a61a7Syz
2139c77a61a7Syz if (sz != 0) {
2140c77a61a7Syz unit->bmControls = &cvs_buf[8];
2141c77a61a7Syz }
2142c77a61a7Syz unit->iProcessing = cvs_buf[8 + sz];
2143c77a61a7Syz
2144c77a61a7Syz /*
2145c77a61a7Syz * video class 1.1 version add one element
2146c77a61a7Syz * (bmVideoStandards) to processing unit descriptor
2147c77a61a7Syz */
2148c77a61a7Syz if (cvs_buf_len > (9 + sz)) {
2149c77a61a7Syz unit->bmVideoStandards = cvs_buf[9 + sz];
2150c77a61a7Syz }
2151c77a61a7Syz list_insert_tail(&(usbvcp->usbvc_unit_list), unit);
2152c77a61a7Syz
2153c77a61a7Syz break;
2154c77a61a7Syz }
2155c77a61a7Syz case VC_SELECTOR_UNIT:
2156c77a61a7Syz {
2157c77a61a7Syz uint8_t pins;
2158c77a61a7Syz usbvc_units_t *unit;
2159c77a61a7Syz
2160c77a61a7Syz if (usbvc_chk_descr_len(4, 1, 0, cvs_data) !=
2161c77a61a7Syz USB_SUCCESS) {
2162c77a61a7Syz
2163c77a61a7Syz break;
2164c77a61a7Syz }
2165c77a61a7Syz pins = cvs_buf[4];
2166c77a61a7Syz if ((pins + 5) >= cvs_buf_len) {
2167c77a61a7Syz
2168c77a61a7Syz break;
2169c77a61a7Syz }
2170c77a61a7Syz unit = (usbvc_units_t *)
2171c77a61a7Syz kmem_zalloc(sizeof (usbvc_units_t), KM_SLEEP);
2172c77a61a7Syz
2173c77a61a7Syz unit->descr = (usbvc_unit_descr_t *)cvs_buf;
2174c77a61a7Syz
2175c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_ATTA,
2176c77a61a7Syz usbvcp->usbvc_log_handle, "usbvc_parse_ctrl_if: "
2177c77a61a7Syz "unit type=%x", unit->descr->bDescriptorSubType);
2178c77a61a7Syz if (pins > 0) {
2179c77a61a7Syz unit->baSourceID = &cvs_buf[5];
2180c77a61a7Syz }
2181c77a61a7Syz unit->iSelector = cvs_buf[5 + pins];
2182c77a61a7Syz
2183c77a61a7Syz list_insert_tail(&(usbvcp->usbvc_unit_list), unit);
2184c77a61a7Syz
2185c77a61a7Syz break;
2186c77a61a7Syz }
2187c77a61a7Syz case VC_EXTENSION_UNIT:
2188c77a61a7Syz {
2189c77a61a7Syz uint8_t pins, n;
2190c77a61a7Syz usbvc_units_t *unit;
2191c77a61a7Syz
2192c77a61a7Syz if (usbvc_chk_descr_len(21, 1, 0, cvs_data) !=
2193c77a61a7Syz USB_SUCCESS) {
2194c77a61a7Syz
2195c77a61a7Syz break;
2196c77a61a7Syz }
2197c77a61a7Syz pins = cvs_buf[21];
2198c77a61a7Syz if ((pins + 22) >= cvs_buf_len) {
2199c77a61a7Syz
2200c77a61a7Syz break;
2201c77a61a7Syz }
2202c77a61a7Syz
2203c77a61a7Syz /* Size of bmControls */
2204c77a61a7Syz n = cvs_buf[pins + 22];
2205c77a61a7Syz
2206c77a61a7Syz if (usbvc_chk_descr_len(pins + 22, 1, 0, cvs_data) !=
2207c77a61a7Syz USB_SUCCESS) {
2208c77a61a7Syz
2209c77a61a7Syz break;
2210c77a61a7Syz }
2211c77a61a7Syz if ((23 + pins + n) >= cvs_buf_len) {
2212c77a61a7Syz
2213c77a61a7Syz break;
2214c77a61a7Syz }
2215c77a61a7Syz unit = (usbvc_units_t *)
2216c77a61a7Syz kmem_zalloc(sizeof (usbvc_units_t), KM_SLEEP);
2217c77a61a7Syz
2218c77a61a7Syz unit->descr = (usbvc_unit_descr_t *)cvs_buf;
2219c77a61a7Syz
2220c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_ATTA,
2221c77a61a7Syz usbvcp->usbvc_log_handle, "usbvc_parse_ctrl_if: "
2222c77a61a7Syz "unit type=%x", unit->descr->bDescriptorSubType);
2223c77a61a7Syz if (pins != 0) {
2224c77a61a7Syz unit->baSourceID = &cvs_buf[22];
2225c77a61a7Syz }
2226c77a61a7Syz unit->bControlSize = cvs_buf[22 + pins];
2227c77a61a7Syz
2228c77a61a7Syz if (unit->bControlSize != 0) {
2229c77a61a7Syz unit->bmControls = &cvs_buf[23 + pins];
2230c77a61a7Syz }
2231c77a61a7Syz unit->iExtension = cvs_buf[23 + pins + n];
2232c77a61a7Syz
2233c77a61a7Syz list_insert_tail(&(usbvcp->usbvc_unit_list), unit);
2234c77a61a7Syz
2235c77a61a7Syz break;
2236c77a61a7Syz }
2237c77a61a7Syz default:
2238c77a61a7Syz
2239c77a61a7Syz break;
2240c77a61a7Syz }
2241c77a61a7Syz }
2242c77a61a7Syz
224326d97b1bSfb /*
224426d97b1bSfb * For webcam which is not compliant to video class specification
224526d97b1bSfb * and no header descriptor in VC interface, return USB_FAILURE.
224626d97b1bSfb */
224726d97b1bSfb if (!usbvcp->usbvc_vc_header) {
224826d97b1bSfb USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
224926d97b1bSfb "usbvc_parse_ctrl_if: no header descriptor");
225026d97b1bSfb
225126d97b1bSfb return (USB_FAILURE);
225226d97b1bSfb }
225326d97b1bSfb
2254c77a61a7Syz return (USB_SUCCESS);
2255c77a61a7Syz }
2256c77a61a7Syz
2257c77a61a7Syz
2258c77a61a7Syz /* Parse all the cvs descriptors in one stream interface. */
2259c77a61a7Syz usbvc_stream_if_t *
usbvc_parse_stream_if(usbvc_state_t * usbvcp,int if_num)2260c77a61a7Syz usbvc_parse_stream_if(usbvc_state_t *usbvcp, int if_num)
2261c77a61a7Syz {
2262c77a61a7Syz usb_alt_if_data_t *if_alt_data;
2263c77a61a7Syz uint_t i, j;
2264c77a61a7Syz usbvc_stream_if_t *strm_if;
2265c77a61a7Syz uint16_t pktsize;
2266c77a61a7Syz uint8_t ep_adr;
2267c77a61a7Syz
2268c77a61a7Syz strm_if = (usbvc_stream_if_t *)kmem_zalloc(sizeof (usbvc_stream_if_t),
2269c77a61a7Syz KM_SLEEP);
2270c77a61a7Syz strm_if->if_descr = &usbvcp->usbvc_reg->dev_curr_cfg->cfg_if[if_num];
2271c77a61a7Syz if_alt_data = strm_if->if_descr->if_alt;
2272c77a61a7Syz if (usbvc_parse_stream_header(usbvcp, strm_if) != USB_SUCCESS) {
2273c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2274c77a61a7Syz "usbvc_parse_stream_if: parse header fail");
2275c77a61a7Syz kmem_free(strm_if, sizeof (usbvc_stream_if_t));
2276c77a61a7Syz
2277c77a61a7Syz return (NULL);
2278c77a61a7Syz }
2279c77a61a7Syz if (usbvc_parse_format_groups(usbvcp, strm_if) != USB_SUCCESS) {
2280c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2281c77a61a7Syz "usbvc_parse_stream_if: parse groups fail");
2282c77a61a7Syz kmem_free(strm_if, sizeof (usbvc_stream_if_t));
2283c77a61a7Syz
2284c77a61a7Syz return (NULL);
2285c77a61a7Syz }
2286c77a61a7Syz
2287c77a61a7Syz /* Parse the alternate settings to find the maximum bandwidth. */
2288c77a61a7Syz for (i = 0; i < strm_if->if_descr->if_n_alt; i++) {
2289c77a61a7Syz if_alt_data = &strm_if->if_descr->if_alt[i];
2290c77a61a7Syz for (j = 0; j < if_alt_data->altif_n_ep; j++) {
2291c77a61a7Syz ep_adr =
2292c77a61a7Syz if_alt_data->altif_ep[j].ep_descr.bEndpointAddress;
2293c77a61a7Syz if (strm_if->input_header != NULL &&
2294c77a61a7Syz ep_adr !=
2295c77a61a7Syz strm_if->input_header->descr->bEndpointAddress) {
2296c77a61a7Syz
2297c77a61a7Syz continue;
2298c77a61a7Syz }
2299c77a61a7Syz if (strm_if->output_header != NULL &&
2300c77a61a7Syz ep_adr !=
2301c77a61a7Syz strm_if->output_header->descr->bEndpointAddress) {
2302c77a61a7Syz
2303c77a61a7Syz continue;
2304c77a61a7Syz }
2305c77a61a7Syz pktsize =
2306c77a61a7Syz if_alt_data->altif_ep[j].ep_descr.wMaxPacketSize;
2307c77a61a7Syz pktsize = HS_PKT_SIZE(pktsize);
2308c77a61a7Syz if (pktsize > strm_if->max_isoc_payload) {
2309c77a61a7Syz strm_if->max_isoc_payload = pktsize;
2310c77a61a7Syz }
2311c77a61a7Syz }
2312c77a61a7Syz }
2313c77a61a7Syz
2314c77a61a7Syz /* initialize MJPEC FID toggle */
2315c77a61a7Syz strm_if->fid = 0xff;
231661b2e298Slc
231761b2e298Slc /*
231861b2e298Slc * initialize desired number of buffers used internally in read() mode
231961b2e298Slc */
232061b2e298Slc strm_if->buf_read_num = USBVC_DEFAULT_READ_BUF_NUM;
232161b2e298Slc
2322c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2323c77a61a7Syz "usbvc_parse_stream_if: return. max_isoc_payload=%x",
2324c77a61a7Syz strm_if->max_isoc_payload);
2325c77a61a7Syz
2326c77a61a7Syz return (strm_if);
2327c77a61a7Syz }
2328c77a61a7Syz
2329c77a61a7Syz
2330c77a61a7Syz /*
2331c77a61a7Syz * Parse all the stream interfaces asociated with the video control interface.
2332c77a61a7Syz * This driver will attach to a video control interface on the device,
2333c77a61a7Syz * there might be multiple video stream interfaces associated with one video
2334c77a61a7Syz * control interface.
2335c77a61a7Syz */
2336c77a61a7Syz static int
usbvc_parse_stream_ifs(usbvc_state_t * usbvcp)2337c77a61a7Syz usbvc_parse_stream_ifs(usbvc_state_t *usbvcp)
2338c77a61a7Syz {
2339c77a61a7Syz int i, if_cnt, if_num;
2340c77a61a7Syz usbvc_stream_if_t *strm_if;
2341c77a61a7Syz
2342c77a61a7Syz if_cnt = usbvcp->usbvc_vc_header->descr->bInCollection;
2343c77a61a7Syz if (if_cnt == 0) {
2344c77a61a7Syz ASSERT(list_is_empty(&usbvcp->usbvc_stream_list));
2345c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2346c77a61a7Syz "usbvc_parse_stream_ifs: no stream interfaces");
2347c77a61a7Syz
2348c77a61a7Syz return (USB_SUCCESS);
2349c77a61a7Syz }
2350c77a61a7Syz for (i = 0; i < if_cnt; i++) {
2351c77a61a7Syz if_num = usbvcp->usbvc_vc_header->baInterfaceNr[i];
2352c77a61a7Syz strm_if = usbvc_parse_stream_if(usbvcp, if_num);
2353c77a61a7Syz if (strm_if == NULL) {
2354c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_ATTA,
2355c77a61a7Syz usbvcp->usbvc_log_handle, "usbvc_parse_stream_ifs:"
2356c77a61a7Syz " parse stream interface %d failed.", if_num);
2357c77a61a7Syz
2358c77a61a7Syz return (USB_FAILURE);
2359c77a61a7Syz }
2360c77a61a7Syz /* video data buffers */
2361c77a61a7Syz list_create(&(strm_if->buf_map.uv_buf_free),
2362c77a61a7Syz sizeof (usbvc_buf_t), offsetof(usbvc_buf_t, buf_node));
2363c77a61a7Syz list_create(&(strm_if->buf_map.uv_buf_done),
2364c77a61a7Syz sizeof (usbvc_buf_t), offsetof(usbvc_buf_t, buf_node));
2365c77a61a7Syz list_create(&(strm_if->buf_read.uv_buf_free),
2366c77a61a7Syz sizeof (usbvc_buf_t), offsetof(usbvc_buf_t, buf_node));
2367c77a61a7Syz list_create(&(strm_if->buf_read.uv_buf_done),
2368c77a61a7Syz sizeof (usbvc_buf_t), offsetof(usbvc_buf_t, buf_node));
2369c77a61a7Syz
2370c77a61a7Syz list_insert_tail(&(usbvcp->usbvc_stream_list), strm_if);
2371c77a61a7Syz }
2372c77a61a7Syz
2373c77a61a7Syz /* Make the first stream interface as the default one. */
2374c77a61a7Syz usbvcp->usbvc_curr_strm =
2375c77a61a7Syz (usbvc_stream_if_t *)list_head(&usbvcp->usbvc_stream_list);
2376c77a61a7Syz
2377c77a61a7Syz return (USB_SUCCESS);
2378c77a61a7Syz }
2379c77a61a7Syz
2380c77a61a7Syz
2381c77a61a7Syz /*
2382c77a61a7Syz * Parse colorspace descriptor and still image descriptor of a format group.
2383c77a61a7Syz * There is only one colorspace or still image descriptor in one format group.
2384c77a61a7Syz */
2385c77a61a7Syz static void
usbvc_parse_color_still(usbvc_state_t * usbvcp,usbvc_format_group_t * fmtgrp,usb_cvs_data_t * cvs_data,uint_t cvs_num,uint_t altif_n_cvs)2386c77a61a7Syz usbvc_parse_color_still(usbvc_state_t *usbvcp, usbvc_format_group_t *fmtgrp,
238711b5d82fSToomas Soome usb_cvs_data_t *cvs_data, uint_t cvs_num, uint_t altif_n_cvs)
2388c77a61a7Syz {
2389c77a61a7Syz uint8_t frame_cnt;
2390c77a61a7Syz uint_t last_frame, i;
2391c77a61a7Syz uchar_t *cvs_buf;
2392c77a61a7Syz uint_t cvs_buf_len;
2393c77a61a7Syz
2394c77a61a7Syz frame_cnt = fmtgrp->format->bNumFrameDescriptors;
2395c77a61a7Syz last_frame = frame_cnt + cvs_num;
2396c77a61a7Syz
2397c77a61a7Syz /*
2398c77a61a7Syz * Find the still image descr and color format descr if there are any.
2399c77a61a7Syz * UVC Spec: only one still image and one color descr is allowed in
2400c77a61a7Syz * one format group.
2401c77a61a7Syz */
2402c77a61a7Syz for (i = 1; i <= 2; i++) {
2403c77a61a7Syz if ((last_frame + i) >= altif_n_cvs) {
2404c77a61a7Syz
2405c77a61a7Syz break;
2406c77a61a7Syz }
2407c77a61a7Syz cvs_buf = cvs_data[last_frame + i].cvs_buf;
2408c77a61a7Syz cvs_buf_len = cvs_data[last_frame + i].cvs_buf_len;
2409c77a61a7Syz
2410c77a61a7Syz if (cvs_buf[2] == VS_STILL_IMAGE_FRAME) {
2411c77a61a7Syz uint8_t m, n, off;
2412c77a61a7Syz usbvc_still_image_frame_t *st;
2413c77a61a7Syz
2414c77a61a7Syz if (usbvc_chk_descr_len(4, 4, 0, cvs_data) !=
2415c77a61a7Syz USB_SUCCESS) {
2416c77a61a7Syz
2417c77a61a7Syz continue;
2418c77a61a7Syz }
2419c77a61a7Syz
2420c77a61a7Syz /* Number of Image Size patterns of this format */
2421c77a61a7Syz n = cvs_buf[4];
2422c77a61a7Syz
2423c77a61a7Syz /* offset of bNumCompressionPattern */
2424c77a61a7Syz off = 9 + 4 * n -4;
2425c77a61a7Syz
2426c77a61a7Syz if (off >= cvs_buf_len) {
2427c77a61a7Syz
2428c77a61a7Syz continue;
2429c77a61a7Syz }
2430c77a61a7Syz
2431c77a61a7Syz /* Number of compression pattern of this format */
2432c77a61a7Syz m = cvs_buf[off];
2433c77a61a7Syz
2434c77a61a7Syz if (usbvc_chk_descr_len(m, 1, 0, cvs_data) !=
2435c77a61a7Syz USB_SUCCESS) {
2436c77a61a7Syz
2437c77a61a7Syz continue;
2438c77a61a7Syz }
2439c77a61a7Syz fmtgrp->still = (usbvc_still_image_frame_t *)
2440c77a61a7Syz kmem_zalloc(sizeof (usbvc_still_image_frame_t),
2441c77a61a7Syz KM_SLEEP);
2442c77a61a7Syz st = fmtgrp->still;
2443c77a61a7Syz st->descr = (usbvc_still_image_frame_descr_t *)cvs_buf;
2444c77a61a7Syz n = st->descr->bNumImageSizePatterns;
2445c77a61a7Syz if (n > 0) {
2446c77a61a7Syz st->width_height =
2447c77a61a7Syz (width_height_t *)&cvs_buf[5];
2448c77a61a7Syz }
2449c77a61a7Syz st->bNumCompressionPattern = cvs_buf[off];
2450c77a61a7Syz if (cvs_buf[off] > 0) {
2451c77a61a7Syz st->bCompression = &cvs_buf[off + 1];
2452c77a61a7Syz }
2453c77a61a7Syz }
2454c77a61a7Syz if (cvs_buf[2] == VS_COLORFORMAT) {
2455c77a61a7Syz fmtgrp->color = (usbvc_color_matching_descr_t *)cvs_buf;
2456c77a61a7Syz fmtgrp->v4l2_color = usbvc_v4l2_colorspace(
2457c77a61a7Syz fmtgrp->color->bColorPrimaries);
2458c77a61a7Syz }
2459c77a61a7Syz }
2460c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2461c77a61a7Syz "usbvc_parse_color_still: still=%p, color=%p",
2462112116d8Sfb (void *)fmtgrp->still, (void *)fmtgrp->color);
2463c77a61a7Syz }
2464c77a61a7Syz
2465c77a61a7Syz
2466c77a61a7Syz /*
2467c77a61a7Syz * Parse frame descriptors of a format group. There might be multi frame
2468c77a61a7Syz * descriptors in one format group.
2469c77a61a7Syz */
2470c77a61a7Syz static void
usbvc_parse_frames(usbvc_state_t * usbvcp,usbvc_format_group_t * fmtgrp,usb_cvs_data_t * cvs_data,uint_t cvs_num,uint_t altif_n_cvs)2471c77a61a7Syz usbvc_parse_frames(usbvc_state_t *usbvcp, usbvc_format_group_t *fmtgrp,
247211b5d82fSToomas Soome usb_cvs_data_t *cvs_data, uint_t cvs_num, uint_t altif_n_cvs)
2473c77a61a7Syz {
2474c77a61a7Syz uint_t last_frame;
2475c77a61a7Syz usbvc_frames_t *frm;
2476c77a61a7Syz usb_cvs_data_t *cvs;
2477c77a61a7Syz uchar_t *cvs_buf;
2478c77a61a7Syz uint_t cvs_buf_len;
2479c77a61a7Syz uint8_t i;
2480c77a61a7Syz uint8_t frame_cnt = fmtgrp->format->bNumFrameDescriptors;
2481c77a61a7Syz
2482c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2483c77a61a7Syz "usbvc_parse_format_group: frame_cnt=%d", frame_cnt);
2484c77a61a7Syz
2485c77a61a7Syz if (frame_cnt == 0) {
2486c77a61a7Syz fmtgrp->frames = NULL;
2487c77a61a7Syz
2488c77a61a7Syz return;
2489c77a61a7Syz }
2490c77a61a7Syz
2491c77a61a7Syz /* All these mem allocated will be freed in cleanup() */
2492c77a61a7Syz fmtgrp->frames = (usbvc_frames_t *)
2493c77a61a7Syz kmem_zalloc(sizeof (usbvc_frames_t) * frame_cnt, KM_SLEEP);
2494c77a61a7Syz
2495c77a61a7Syz last_frame = frame_cnt + cvs_num;
2496c77a61a7Syz cvs_num++;
2497c77a61a7Syz i = 0;
2498c77a61a7Syz
2499c77a61a7Syz /*
2500c77a61a7Syz * Traverse from the format decr's first frame decr to the the last
2501c77a61a7Syz * frame descr.
2502c77a61a7Syz */
2503c77a61a7Syz for (; cvs_num <= last_frame; cvs_num++) {
2504c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2505c77a61a7Syz "usbvc_parse_frames: cvs_num=%d, i=%d", cvs_num, i);
2506c77a61a7Syz if (cvs_num >= altif_n_cvs) {
2507c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_ATTA,
2508c77a61a7Syz usbvcp->usbvc_log_handle,
2509c77a61a7Syz "usbvc_parse_frames: less frames than "
2510c77a61a7Syz "expected, cvs_num=%d, i=%d", cvs_num, i);
2511c77a61a7Syz
2512c77a61a7Syz break;
2513c77a61a7Syz }
2514c77a61a7Syz cvs = &cvs_data[cvs_num];
2515c77a61a7Syz cvs_buf = cvs->cvs_buf;
2516c77a61a7Syz cvs_buf_len = cvs->cvs_buf_len;
2517c77a61a7Syz if (cvs_buf_len < USBVC_FRAME_LEN_MIN) {
2518c77a61a7Syz i++;
2519c77a61a7Syz
2520c77a61a7Syz continue;
2521c77a61a7Syz }
2522c77a61a7Syz frm = &fmtgrp->frames[i];
2523c77a61a7Syz frm->descr = (usbvc_frame_descr_t *)cvs->cvs_buf;
2524c77a61a7Syz
2525c77a61a7Syz /* Descriptor for discrete frame interval */
2526c77a61a7Syz if (frm->descr->bFrameIntervalType > 0) {
2527c77a61a7Syz if (usbvc_chk_descr_len(25, 4, 0, cvs) != USB_SUCCESS) {
2528c77a61a7Syz frm->descr = NULL;
2529c77a61a7Syz i++;
2530c77a61a7Syz
2531c77a61a7Syz continue;
2532c77a61a7Syz }
2533c77a61a7Syz
253461b2e298Slc frm->dwFrameInterval = (uint8_t *)&cvs_buf[26];
2535c77a61a7Syz } else { /* Continuous interval */
2536c77a61a7Syz if (cvs_buf_len < USBVC_FRAME_LEN_CON) {
2537c77a61a7Syz frm->descr = NULL;
2538c77a61a7Syz i++;
2539c77a61a7Syz
2540c77a61a7Syz continue;
2541c77a61a7Syz }
2542c77a61a7Syz
2543c77a61a7Syz /* Continuous frame intervals */
2544c77a61a7Syz LE_TO_UINT32(cvs_buf, 26, frm->dwMinFrameInterval);
2545c77a61a7Syz LE_TO_UINT32(cvs_buf, 30, frm->dwMaxFrameInterval);
2546c77a61a7Syz LE_TO_UINT32(cvs_buf, 34, frm->dwFrameIntervalStep);
2547c77a61a7Syz }
2548c77a61a7Syz
2549c77a61a7Syz i++;
2550c77a61a7Syz }
2551c77a61a7Syz fmtgrp->frame_cnt = i;
2552c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2553c77a61a7Syz "usbvc_parse_frames: %d frames are actually parsed",
2554c77a61a7Syz fmtgrp->frame_cnt);
2555c77a61a7Syz }
2556c77a61a7Syz
2557c77a61a7Syz
2558c77a61a7Syz /* Parse one of the format groups in a stream interface */
2559c77a61a7Syz static int
usbvc_parse_format_group(usbvc_state_t * usbvcp,usbvc_format_group_t * fmtgrp,usb_cvs_data_t * cvs_data,uint_t cvs_num,uint_t altif_n_cvs)2560c77a61a7Syz usbvc_parse_format_group(usbvc_state_t *usbvcp, usbvc_format_group_t *fmtgrp,
256111b5d82fSToomas Soome usb_cvs_data_t *cvs_data, uint_t cvs_num, uint_t altif_n_cvs)
2562c77a61a7Syz {
2563c77a61a7Syz usbvc_format_descr_t *fmt;
2564c77a61a7Syz
2565c77a61a7Syz fmt = fmtgrp->format;
2566c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2567ef772e54Sfb "usbvc_parse_format_group: frame_cnt=%d, cvs_num=%d",
2568ef772e54Sfb fmt->bNumFrameDescriptors, cvs_num);
2569c77a61a7Syz
2570c77a61a7Syz switch (fmt->bDescriptorSubType) {
2571c77a61a7Syz case VS_FORMAT_UNCOMPRESSED:
2572c77a61a7Syz usbvc_parse_color_still(usbvcp, fmtgrp, cvs_data, cvs_num,
2573c77a61a7Syz altif_n_cvs);
2574c77a61a7Syz usbvc_parse_frames(usbvcp, fmtgrp, cvs_data, cvs_num,
2575c77a61a7Syz altif_n_cvs);
2576f6517e58Syz fmtgrp->v4l2_bpp = fmt->fmt.uncompressed.bBitsPerPixel / 8;
2577c77a61a7Syz fmtgrp->v4l2_pixelformat = usbvc_v4l2_guid2fcc(
2578c77a61a7Syz (uint8_t *)&fmt->fmt.uncompressed.guidFormat);
2579c77a61a7Syz
2580c77a61a7Syz break;
2581c77a61a7Syz case VS_FORMAT_MJPEG:
2582c77a61a7Syz usbvc_parse_color_still(usbvcp, fmtgrp, cvs_data, cvs_num,
2583c77a61a7Syz altif_n_cvs);
2584c77a61a7Syz usbvc_parse_frames(usbvcp, fmtgrp, cvs_data, cvs_num,
2585c77a61a7Syz altif_n_cvs);
2586c77a61a7Syz fmtgrp->v4l2_bpp = 0;
2587c77a61a7Syz fmtgrp->v4l2_pixelformat = V4L2_PIX_FMT_MJPEG;
2588c77a61a7Syz
2589c77a61a7Syz break;
2590c77a61a7Syz case VS_FORMAT_MPEG2TS:
2591c77a61a7Syz case VS_FORMAT_DV:
2592c77a61a7Syz case VS_FORMAT_FRAME_BASED:
2593c77a61a7Syz case VS_FORMAT_STREAM_BASED:
2594c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2595c77a61a7Syz "usbvc_parse_format_group: format not supported yet.");
2596c77a61a7Syz
2597c77a61a7Syz return (USB_FAILURE);
2598c77a61a7Syz default:
2599c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2600c77a61a7Syz "usbvc_parse_format_group: unknown format.");
2601c77a61a7Syz
2602c77a61a7Syz return (USB_FAILURE);
2603c77a61a7Syz }
2604c77a61a7Syz
2605c77a61a7Syz return (USB_SUCCESS);
2606c77a61a7Syz }
2607c77a61a7Syz
2608c77a61a7Syz
2609c77a61a7Syz /* Parse the descriptors belong to one format */
2610c77a61a7Syz static int
usbvc_parse_format_groups(usbvc_state_t * usbvcp,usbvc_stream_if_t * strm_if)2611c77a61a7Syz usbvc_parse_format_groups(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
2612c77a61a7Syz {
2613c77a61a7Syz usb_alt_if_data_t *if_alt_data;
2614c77a61a7Syz usb_cvs_data_t *cvs_data;
2615c77a61a7Syz uint8_t fmtgrp_num, fmtgrp_cnt;
2616c77a61a7Syz uchar_t *cvs_buf;
2617c77a61a7Syz uint_t cvs_num = 0;
2618c77a61a7Syz usbvc_format_group_t *fmtgrp;
2619c77a61a7Syz
2620c77a61a7Syz fmtgrp_cnt = 0;
2621c77a61a7Syz /*
2622c77a61a7Syz * bNumFormats indicates the number of formats in this stream
2623c77a61a7Syz * interface. On some devices, we see this number is larger than
2624c77a61a7Syz * the truth.
2625c77a61a7Syz */
2626c77a61a7Syz if (strm_if->input_header) {
2627c77a61a7Syz fmtgrp_cnt = strm_if->input_header->descr->bNumFormats;
2628c77a61a7Syz } else if (strm_if->output_header) {
2629c77a61a7Syz fmtgrp_cnt = strm_if->output_header->descr->bNumFormats;
2630c77a61a7Syz }
2631c77a61a7Syz if (!fmtgrp_cnt) {
2632c77a61a7Syz
2633c77a61a7Syz return (USB_FAILURE);
2634c77a61a7Syz }
2635c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2636c77a61a7Syz "usbvc_parse_format_groups: fmtgrp_cnt=%d", fmtgrp_cnt);
2637c77a61a7Syz
2638c77a61a7Syz fmtgrp = (usbvc_format_group_t *)
2639c77a61a7Syz kmem_zalloc(sizeof (usbvc_format_group_t) * fmtgrp_cnt, KM_SLEEP);
2640c77a61a7Syz
2641c77a61a7Syz if_alt_data = strm_if->if_descr->if_alt;
2642c77a61a7Syz cvs_data = if_alt_data->altif_cvs;
2643c77a61a7Syz
2644ef772e54Sfb for (fmtgrp_num = 0; fmtgrp_num < fmtgrp_cnt &&
2645ef772e54Sfb cvs_num < if_alt_data->altif_n_cvs; cvs_num++) {
2646ef772e54Sfb cvs_buf = cvs_data[cvs_num].cvs_buf;
2647ef772e54Sfb switch (cvs_buf[2]) {
2648ef772e54Sfb case VS_FORMAT_UNCOMPRESSED:
2649ef772e54Sfb case VS_FORMAT_MJPEG:
2650ef772e54Sfb case VS_FORMAT_MPEG2TS:
2651ef772e54Sfb case VS_FORMAT_DV:
2652ef772e54Sfb case VS_FORMAT_FRAME_BASED:
2653ef772e54Sfb case VS_FORMAT_STREAM_BASED:
2654ef772e54Sfb fmtgrp[fmtgrp_num].format =
2655ef772e54Sfb (usbvc_format_descr_t *)cvs_buf;
2656c77a61a7Syz
2657ef772e54Sfb /*
2658ef772e54Sfb * Now cvs_data[cvs_num].cvs_buf is format descriptor,
2659ef772e54Sfb * usbvc_parse_format_group will then parse the frame
2660ef772e54Sfb * descriptors following this format descriptor.
2661ef772e54Sfb */
2662ef772e54Sfb (void) usbvc_parse_format_group(usbvcp,
2663ef772e54Sfb &fmtgrp[fmtgrp_num], cvs_data, cvs_num,
2664ef772e54Sfb if_alt_data->altif_n_cvs);
2665c77a61a7Syz
2666ef772e54Sfb fmtgrp_num++;
2667c77a61a7Syz
2668ef772e54Sfb break;
2669ef772e54Sfb default:
2670c77a61a7Syz break;
2671c77a61a7Syz }
2672c77a61a7Syz }
2673c77a61a7Syz
2674ef772e54Sfb /* Save the number of parsed format groups. */
2675ef772e54Sfb strm_if->fmtgrp_cnt = fmtgrp_num;
267661b2e298Slc USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2677ef772e54Sfb "usbvc_parse_format_groups: acctually %d formats parsed",
2678ef772e54Sfb fmtgrp_num);
2679ef772e54Sfb
2680c77a61a7Syz /*
2681c77a61a7Syz * If can't find any formats, then free all allocated
2682c77a61a7Syz * usbvc_format_group_t, return failure.
2683c77a61a7Syz */
2684c77a61a7Syz if (!(fmtgrp[0].format)) {
2685c77a61a7Syz kmem_free(fmtgrp, sizeof (usbvc_format_group_t) * fmtgrp_cnt);
2686c77a61a7Syz strm_if->format_group = NULL;
2687c77a61a7Syz
2688c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2689c77a61a7Syz "usbvc_parse_format_groups: can't find any formats");
2690c77a61a7Syz
2691c77a61a7Syz return (USB_FAILURE);
2692c77a61a7Syz }
2693c77a61a7Syz strm_if->format_group = fmtgrp;
269461b2e298Slc USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2695c77a61a7Syz "usbvc_parse_format_groups: %d format groups parsed", fmtgrp_num);
2696c77a61a7Syz
2697c77a61a7Syz return (USB_SUCCESS);
2698c77a61a7Syz }
2699c77a61a7Syz
2700c77a61a7Syz
2701c77a61a7Syz /*
2702c77a61a7Syz * Parse the input/output header in one stream interface.
2703c77a61a7Syz * UVC Spec: there must be one and only one header in one stream interface.
2704c77a61a7Syz */
2705c77a61a7Syz int
usbvc_parse_stream_header(usbvc_state_t * usbvcp,usbvc_stream_if_t * strm_if)2706c77a61a7Syz usbvc_parse_stream_header(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
2707c77a61a7Syz {
2708c77a61a7Syz usb_alt_if_data_t *if_alt_data;
2709c77a61a7Syz usb_cvs_data_t *cvs_data;
2710c77a61a7Syz int cvs_num;
2711c77a61a7Syz uchar_t *cvs_buf;
2712c77a61a7Syz usbvc_input_header_t *in_hdr;
2713c77a61a7Syz usbvc_output_header_t *out_hdr;
2714c77a61a7Syz
2715c77a61a7Syz if_alt_data = strm_if->if_descr->if_alt;
2716c77a61a7Syz cvs_data = if_alt_data->altif_cvs;
2717c77a61a7Syz for (cvs_num = 0; cvs_num < if_alt_data->altif_n_cvs; cvs_num++) {
2718ef772e54Sfb cvs_buf = cvs_data[cvs_num].cvs_buf;
271961b2e298Slc USB_DPRINTF_L3(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2720c77a61a7Syz "usbvc_parse_stream_header: cvs_num= %d", cvs_num);
2721c77a61a7Syz
2722c77a61a7Syz /*
2723c77a61a7Syz * parse interface cvs descriptors here; by checking
2724c77a61a7Syz * bDescriptorType (cvs_buf[1])
2725c77a61a7Syz */
2726c77a61a7Syz if (cvs_buf[1] != CS_INTERFACE) {
2727c77a61a7Syz
2728c77a61a7Syz continue;
2729c77a61a7Syz }
2730c77a61a7Syz
2731c77a61a7Syz if (cvs_buf[2] == VS_INPUT_HEADER) {
2732c77a61a7Syz if (usbvc_chk_descr_len(3, 0, 12, cvs_data) !=
2733c77a61a7Syz USB_SUCCESS) {
2734c77a61a7Syz
2735c77a61a7Syz continue;
2736c77a61a7Syz }
2737c77a61a7Syz
2738c77a61a7Syz strm_if->input_header =
2739c77a61a7Syz (usbvc_input_header_t *)
2740c77a61a7Syz kmem_zalloc(sizeof (usbvc_input_header_t),
2741c77a61a7Syz KM_SLEEP);
2742c77a61a7Syz in_hdr = strm_if->input_header;
2743c77a61a7Syz in_hdr->descr = (usbvc_input_header_descr_t *)cvs_buf;
2744c77a61a7Syz if (in_hdr->descr->bNumFormats > 0) {
2745c77a61a7Syz in_hdr->bmaControls = &cvs_buf[13];
2746c77a61a7Syz }
2747c77a61a7Syz
2748c77a61a7Syz return (USB_SUCCESS);
2749c77a61a7Syz } else if (cvs_buf[2] == VS_OUTPUT_HEADER) {
2750c77a61a7Syz if (usbvc_chk_descr_len(3, 0, 8, cvs_data) !=
2751c77a61a7Syz USB_SUCCESS) {
2752c77a61a7Syz
2753c77a61a7Syz continue;
2754c77a61a7Syz }
2755c77a61a7Syz strm_if->output_header =
2756c77a61a7Syz (usbvc_output_header_t *)
2757c77a61a7Syz kmem_zalloc(sizeof (usbvc_output_header_t),
2758c77a61a7Syz KM_SLEEP);
2759c77a61a7Syz out_hdr = strm_if->output_header;
2760c77a61a7Syz out_hdr->descr =
2761c77a61a7Syz (usbvc_output_header_descr_t *)cvs_buf;
2762c77a61a7Syz if (out_hdr->descr->bNumFormats > 0) {
2763c77a61a7Syz out_hdr->bmaControls = &cvs_buf[13];
2764c77a61a7Syz }
2765c77a61a7Syz
2766c77a61a7Syz return (USB_SUCCESS);
2767c77a61a7Syz } else {
2768c77a61a7Syz
2769c77a61a7Syz continue;
2770c77a61a7Syz }
2771c77a61a7Syz }
2772c77a61a7Syz /* Didn't find one header descriptor. */
2773c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_ATTA, usbvcp->usbvc_log_handle,
2774c77a61a7Syz "usbvc_parse_stream_header: FAIL");
2775c77a61a7Syz
2776c77a61a7Syz return (USB_FAILURE);
2777c77a61a7Syz }
2778c77a61a7Syz
2779c77a61a7Syz /* read I/O functions */
2780c77a61a7Syz
2781c77a61a7Syz /* Allocate bufs for read I/O method */
2782c77a61a7Syz static int
usbvc_alloc_read_bufs(usbvc_state_t * usbvcp,usbvc_stream_if_t * strm_if)2783c77a61a7Syz usbvc_alloc_read_bufs(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
2784c77a61a7Syz {
2785c77a61a7Syz usbvc_buf_t *buf;
2786c77a61a7Syz uchar_t *data;
2787c77a61a7Syz int i;
2788c77a61a7Syz uint32_t len;
2789c77a61a7Syz
2790c77a61a7Syz ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
2791c77a61a7Syz
2792c77a61a7Syz LE_TO_UINT32(strm_if->ctrl_pc.dwMaxVideoFrameSize, 0, len);
2793c77a61a7Syz if (!len) {
2794c77a61a7Syz
2795c77a61a7Syz return (USB_FAILURE);
2796c77a61a7Syz }
279761b2e298Slc for (i = 0; i < strm_if->buf_read_num; i++) {
2798c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
2799c77a61a7Syz buf = (usbvc_buf_t *)kmem_zalloc(sizeof (usbvc_buf_t),
2800c77a61a7Syz KM_SLEEP);
2801c77a61a7Syz data = (uchar_t *)kmem_zalloc(len, KM_SLEEP);
2802c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
2803c77a61a7Syz buf->data = data;
2804c77a61a7Syz buf->len = len;
2805c77a61a7Syz list_insert_tail(&(strm_if->buf_read.uv_buf_free), buf);
2806c77a61a7Syz }
280761b2e298Slc strm_if->buf_read.buf_cnt = strm_if->buf_read_num;
2808c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_READ, usbvcp->usbvc_log_handle,
2809c77a61a7Syz "read_bufs: %d bufs allocated", strm_if->buf_read.buf_cnt);
2810c77a61a7Syz
2811c77a61a7Syz return (USB_SUCCESS);
2812c77a61a7Syz }
2813c77a61a7Syz
2814c77a61a7Syz
2815c77a61a7Syz /* Read a done buf, copy data to bp. This function is for read I/O method */
2816c77a61a7Syz static int
usbvc_read_buf(usbvc_state_t * usbvcp,struct buf * bp)2817c77a61a7Syz usbvc_read_buf(usbvc_state_t *usbvcp, struct buf *bp)
2818c77a61a7Syz {
2819c77a61a7Syz usbvc_buf_t *buf;
28209e37f2b5SRaymond Chen int buf_residue;
28219e37f2b5SRaymond Chen int len_to_copy;
2822c77a61a7Syz
2823c77a61a7Syz ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
2824c77a61a7Syz
28259e37f2b5SRaymond Chen if (list_is_empty(&usbvcp->usbvc_curr_strm->buf_read.uv_buf_done)) {
28269e37f2b5SRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
28279e37f2b5SRaymond Chen "usbvc_read_buf: empty list(uv_buf_done)!");
28289e37f2b5SRaymond Chen
28299e37f2b5SRaymond Chen return (USB_FAILURE);
28309e37f2b5SRaymond Chen }
28319e37f2b5SRaymond Chen
2832c77a61a7Syz /* read a buf from full list and then put it to free list */
2833c77a61a7Syz buf = list_head(&usbvcp->usbvc_curr_strm->buf_read.uv_buf_done);
2834c77a61a7Syz
28359e37f2b5SRaymond Chen USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
28369e37f2b5SRaymond Chen "usbvc_read_buf: buf=%p, buf->filled=%d, buf->len=%d,"
28379e37f2b5SRaymond Chen " buf->len_read=%d bp->b_bcount=%ld, bp->b_resid=%lu",
28389e37f2b5SRaymond Chen (void *)buf, buf->filled, buf->len, buf->len_read,
28399e37f2b5SRaymond Chen bp->b_bcount, bp->b_resid);
28409e37f2b5SRaymond Chen
28419e37f2b5SRaymond Chen ASSERT(buf->len_read <= buf->filled);
28429e37f2b5SRaymond Chen
28439e37f2b5SRaymond Chen buf_residue = buf->filled - buf->len_read;
28449e37f2b5SRaymond Chen len_to_copy = min(bp->b_bcount, buf_residue);
28459e37f2b5SRaymond Chen
28466847c243SRaymond Chen bcopy(buf->data + buf->len_read, bp->b_un.b_addr, len_to_copy);
28476847c243SRaymond Chen bp->b_private = NULL;
28486847c243SRaymond Chen buf->len_read += len_to_copy;
28496847c243SRaymond Chen bp->b_resid = bp->b_bcount - len_to_copy;
28506847c243SRaymond Chen
28519e37f2b5SRaymond Chen if (len_to_copy == buf_residue) {
28529e37f2b5SRaymond Chen /*
28539e37f2b5SRaymond Chen * the bp can accommodate all the remaining bytes of
28549e37f2b5SRaymond Chen * the buf. Then we can reuse this buf.
28559e37f2b5SRaymond Chen */
28566847c243SRaymond Chen buf->len_read = 0;
28579e37f2b5SRaymond Chen list_remove(&usbvcp->usbvc_curr_strm->buf_read.uv_buf_done,
28589e37f2b5SRaymond Chen buf);
28599e37f2b5SRaymond Chen list_insert_tail(&usbvcp->usbvc_curr_strm->buf_read.uv_buf_free,
28609e37f2b5SRaymond Chen buf);
28619e37f2b5SRaymond Chen }
2862c77a61a7Syz
2863c77a61a7Syz return (USB_SUCCESS);
2864c77a61a7Syz }
2865c77a61a7Syz
2866c77a61a7Syz
2867c77a61a7Syz /* Free one buf which is for read/write IO style */
2868c77a61a7Syz static void
usbvc_free_read_buf(usbvc_buf_t * buf)2869c77a61a7Syz usbvc_free_read_buf(usbvc_buf_t *buf)
2870c77a61a7Syz {
2871c77a61a7Syz if (buf != NULL) {
2872c77a61a7Syz if (buf->data) {
2873c77a61a7Syz kmem_free(buf->data, buf->len);
2874c77a61a7Syz }
2875c77a61a7Syz kmem_free(buf, sizeof (usbvc_buf_t));
2876c77a61a7Syz }
2877c77a61a7Syz }
2878c77a61a7Syz
2879c77a61a7Syz
2880c77a61a7Syz /* Free all bufs which are for read/write IO style */
2881c77a61a7Syz static void
usbvc_free_read_bufs(usbvc_state_t * usbvcp,usbvc_stream_if_t * strm_if)2882c77a61a7Syz usbvc_free_read_bufs(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
2883c77a61a7Syz {
2884c77a61a7Syz usbvc_buf_t *buf;
2885c77a61a7Syz
2886c77a61a7Syz ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
2887c77a61a7Syz
2888c77a61a7Syz if (!strm_if) {
2889c77a61a7Syz
2890c77a61a7Syz return;
2891c77a61a7Syz }
2892c77a61a7Syz buf = strm_if->buf_read.buf_filling;
2893c77a61a7Syz usbvc_free_read_buf(buf);
2894c77a61a7Syz strm_if->buf_read.buf_filling = NULL;
2895c77a61a7Syz
2896c77a61a7Syz while (!list_is_empty(&strm_if->buf_read.uv_buf_free)) {
2897c77a61a7Syz buf = list_head(&strm_if->buf_read.uv_buf_free);
2898c77a61a7Syz if (buf != NULL) {
2899c77a61a7Syz list_remove(&(strm_if->buf_read.uv_buf_free), buf);
2900c77a61a7Syz usbvc_free_read_buf(buf);
2901c77a61a7Syz }
2902c77a61a7Syz }
2903c77a61a7Syz while (!list_is_empty(&strm_if->buf_read.uv_buf_done)) {
2904c77a61a7Syz buf = list_head(&strm_if->buf_read.uv_buf_done);
2905c77a61a7Syz if (buf != NULL) {
2906c77a61a7Syz list_remove(&(strm_if->buf_read.uv_buf_done), buf);
2907c77a61a7Syz usbvc_free_read_buf(buf);
2908c77a61a7Syz }
2909c77a61a7Syz }
2910c77a61a7Syz strm_if->buf_read.buf_cnt = 0;
2911c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
2912c77a61a7Syz "usbvc_free_read_bufs: return");
2913c77a61a7Syz }
2914c77a61a7Syz
2915c77a61a7Syz
2916c77a61a7Syz /*
2917c77a61a7Syz * Allocate bufs for mapped I/O , return the number of allocated bufs
2918c77a61a7Syz * if success, return 0 if fail.
2919c77a61a7Syz */
2920c77a61a7Syz int
usbvc_alloc_map_bufs(usbvc_state_t * usbvcp,usbvc_stream_if_t * strm_if,int buf_cnt,int buf_len)2921c77a61a7Syz usbvc_alloc_map_bufs(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if,
292211b5d82fSToomas Soome int buf_cnt, int buf_len)
2923c77a61a7Syz {
2924c77a61a7Syz int i = 0;
2925c77a61a7Syz usbvc_buf_t *bufs;
2926c77a61a7Syz
2927c77a61a7Syz ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
2928c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
2929c77a61a7Syz "usbvc_alloc_map_bufs: bufcnt=%d, buflen=%d", buf_cnt, buf_len);
2930c77a61a7Syz if (buf_len <= 0 || buf_cnt <= 0) {
2931c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
2932c77a61a7Syz "usbvc_alloc_map_bufs: len<=0, cnt<=0");
2933c77a61a7Syz
2934c77a61a7Syz return (0);
2935c77a61a7Syz }
2936c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
2937c77a61a7Syz
2938c77a61a7Syz bufs = (usbvc_buf_t *) kmem_zalloc(sizeof (usbvc_buf_t) * buf_cnt,
2939c77a61a7Syz KM_SLEEP);
2940c77a61a7Syz
2941c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
2942c77a61a7Syz strm_if->buf_map.buf_head = bufs;
2943c77a61a7Syz buf_len = ptob(btopr(buf_len));
2944c77a61a7Syz
2945c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
2946c77a61a7Syz bufs[0].data = ddi_umem_alloc(buf_len * buf_cnt, DDI_UMEM_SLEEP,
2947ef772e54Sfb &bufs[0].umem_cookie);
2948c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
2949c77a61a7Syz
2950c77a61a7Syz for (i = 0; i < buf_cnt; i++) {
2951c77a61a7Syz bufs[i].len = buf_len;
2952c77a61a7Syz bufs[i].data = bufs[0].data + (buf_len * i);
2953c77a61a7Syz bufs[i].umem_cookie = bufs[0].umem_cookie;
2954c77a61a7Syz bufs[i].status = USBVC_BUF_INIT;
2955c77a61a7Syz
2956c77a61a7Syz bufs[i].v4l2_buf.index = i;
2957c77a61a7Syz bufs[i].v4l2_buf.m.offset = i * bufs[i].len;
2958c77a61a7Syz bufs[i].v4l2_buf.length = bufs[i].len;
2959c77a61a7Syz bufs[i].v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
2960c77a61a7Syz bufs[i].v4l2_buf.sequence = 0;
2961c77a61a7Syz bufs[i].v4l2_buf.field = V4L2_FIELD_NONE;
2962c77a61a7Syz bufs[i].v4l2_buf.memory = V4L2_MEMORY_MMAP;
2963c77a61a7Syz bufs[i].v4l2_buf.flags = V4L2_MEMORY_MMAP;
2964c77a61a7Syz
2965c77a61a7Syz list_insert_tail(&strm_if->buf_map.uv_buf_free, &bufs[i]);
296661b2e298Slc USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
2967c77a61a7Syz "usbvc_alloc_map_bufs: prepare %d buffers of %d bytes",
2968c77a61a7Syz buf_cnt, bufs[i].len);
2969c77a61a7Syz }
2970c77a61a7Syz strm_if->buf_map.buf_cnt = buf_cnt;
2971c77a61a7Syz strm_if->buf_map.buf_filling = NULL;
2972c77a61a7Syz
2973c77a61a7Syz return (buf_cnt);
2974c77a61a7Syz }
2975c77a61a7Syz
2976c77a61a7Syz
2977c77a61a7Syz /* Free all bufs which are for memory map IO style */
2978c77a61a7Syz void
usbvc_free_map_bufs(usbvc_state_t * usbvcp,usbvc_stream_if_t * strm_if)2979c77a61a7Syz usbvc_free_map_bufs(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
2980c77a61a7Syz {
2981c77a61a7Syz usbvc_buf_t *buf;
2982c77a61a7Syz
2983c77a61a7Syz ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
2984c77a61a7Syz if (!strm_if) {
2985c77a61a7Syz
2986c77a61a7Syz return;
2987c77a61a7Syz }
2988c77a61a7Syz strm_if->buf_map.buf_filling = NULL;
2989c77a61a7Syz while (!list_is_empty(&strm_if->buf_map.uv_buf_free)) {
2990c77a61a7Syz buf = (usbvc_buf_t *)list_head(&strm_if->buf_map.uv_buf_free);
2991c77a61a7Syz list_remove(&(strm_if->buf_map.uv_buf_free), buf);
2992c77a61a7Syz }
2993c77a61a7Syz while (!list_is_empty(&strm_if->buf_map.uv_buf_done)) {
2994c77a61a7Syz buf = (usbvc_buf_t *)list_head(&strm_if->buf_map.uv_buf_done);
2995c77a61a7Syz list_remove(&(strm_if->buf_map.uv_buf_done), buf);
2996c77a61a7Syz }
2997c77a61a7Syz buf = strm_if->buf_map.buf_head;
2998c77a61a7Syz if (!buf) {
2999c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
3000c77a61a7Syz "usbvc_free_map_bufs: no data buf need be freed, return");
3001c77a61a7Syz
3002c77a61a7Syz return;
3003c77a61a7Syz }
3004c77a61a7Syz if (buf->umem_cookie) {
3005c77a61a7Syz ddi_umem_free(buf->umem_cookie);
3006c77a61a7Syz }
3007c77a61a7Syz kmem_free(buf, sizeof (usbvc_buf_t) * strm_if->buf_map.buf_cnt);
3008c77a61a7Syz strm_if->buf_map.buf_cnt = 0;
3009c77a61a7Syz strm_if->buf_map.buf_head = NULL;
3010c77a61a7Syz
301161b2e298Slc USB_DPRINTF_L4(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
3012c77a61a7Syz "usbvc_free_map_bufs: return");
3013c77a61a7Syz }
3014c77a61a7Syz
3015c77a61a7Syz
3016c77a61a7Syz /*
3017c77a61a7Syz * Open the isoc pipe, this pipe is for video data transfer
3018c77a61a7Syz */
3019c77a61a7Syz int
usbvc_open_isoc_pipe(usbvc_state_t * usbvcp,usbvc_stream_if_t * strm_if)3020c77a61a7Syz usbvc_open_isoc_pipe(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
3021c77a61a7Syz {
3022c77a61a7Syz usb_pipe_policy_t policy;
3023c77a61a7Syz int rval = USB_SUCCESS;
3024c77a61a7Syz
3025c77a61a7Syz ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
3026c77a61a7Syz
3027c77a61a7Syz if ((rval = usbvc_set_alt(usbvcp, strm_if)) != USB_SUCCESS) {
3028c77a61a7Syz
3029c77a61a7Syz return (rval);
3030c77a61a7Syz }
3031c77a61a7Syz bzero(&policy, sizeof (usb_pipe_policy_t));
3032c77a61a7Syz policy.pp_max_async_reqs = 2;
3033c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
3034c77a61a7Syz if ((rval = usb_pipe_open(usbvcp->usbvc_dip, strm_if->curr_ep, &policy,
3035c77a61a7Syz USB_FLAGS_SLEEP, &strm_if->datain_ph)) != USB_SUCCESS) {
3036c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
3037c77a61a7Syz "usbvc_open_isoc_pipe: open pipe fail");
3038c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
3039c77a61a7Syz
3040c77a61a7Syz return (rval);
3041c77a61a7Syz }
3042c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
3043c77a61a7Syz strm_if->start_polling = 0;
3044c77a61a7Syz
304509dd0d6cSRaymond Chen strm_if->stream_on = 0;
304609dd0d6cSRaymond Chen
3047c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
3048112116d8Sfb "usbvc_open_isoc_pipe: success, datain_ph=%p",
3049112116d8Sfb (void *)strm_if->datain_ph);
3050c77a61a7Syz
3051c77a61a7Syz return (rval);
3052c77a61a7Syz }
3053c77a61a7Syz
3054c77a61a7Syz
3055c77a61a7Syz /*
3056c77a61a7Syz * Open the isoc pipe
3057c77a61a7Syz */
3058c77a61a7Syz static void
usbvc_close_isoc_pipe(usbvc_state_t * usbvcp,usbvc_stream_if_t * strm_if)3059c77a61a7Syz usbvc_close_isoc_pipe(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
3060c77a61a7Syz {
3061c77a61a7Syz ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
3062c77a61a7Syz if (!strm_if) {
3063c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_CLOSE, usbvcp->usbvc_log_handle,
3064c77a61a7Syz "usbvc_close_isoc_pipe: stream interface is NULL");
3065c77a61a7Syz
3066c77a61a7Syz return;
3067c77a61a7Syz }
3068c77a61a7Syz if (strm_if->datain_ph) {
3069c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
3070c77a61a7Syz usb_pipe_close(usbvcp->usbvc_dip, strm_if->datain_ph,
3071c77a61a7Syz USB_FLAGS_SLEEP, NULL, NULL);
3072c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
3073c77a61a7Syz }
3074c77a61a7Syz strm_if->datain_ph = NULL;
3075c77a61a7Syz }
3076c77a61a7Syz
3077c77a61a7Syz
3078c77a61a7Syz /*
3079c77a61a7Syz * Start to get video data from isoc pipe in the stream interface,
3080c77a61a7Syz * issue isoc req.
3081c77a61a7Syz */
3082c77a61a7Syz int
usbvc_start_isoc_polling(usbvc_state_t * usbvcp,usbvc_stream_if_t * strm_if,uchar_t io_type)3083c77a61a7Syz usbvc_start_isoc_polling(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if,
3084c77a61a7Syz uchar_t io_type)
3085c77a61a7Syz {
3086c77a61a7Syz int rval = USB_SUCCESS;
3087c77a61a7Syz uint_t if_num;
3088c77a61a7Syz usb_isoc_req_t *req;
3089c77a61a7Syz ushort_t pkt_size;
3090c77a61a7Syz ushort_t n_pkt, pkt;
3091c77a61a7Syz uint32_t frame_size;
3092c77a61a7Syz
3093c77a61a7Syz ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
309461b2e298Slc pkt_size = HS_PKT_SIZE(strm_if->curr_ep->wMaxPacketSize);
3095c77a61a7Syz if_num = strm_if->if_descr->if_alt->altif_descr.bInterfaceNumber;
3096c77a61a7Syz LE_TO_UINT32(strm_if->ctrl_pc.dwMaxVideoFrameSize, 0, frame_size);
309761b2e298Slc n_pkt = (frame_size + (pkt_size) - 1) / (pkt_size);
309861b2e298Slc
3099c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
3100c77a61a7Syz "usbvc_start_isoc_polling: if_num=%d, alt=%d, n_pkt=%d,"
310161b2e298Slc " pkt_size=0x%x, MaxPacketSize=0x%x(Tsac#=%d), frame_size=0x%x",
310261b2e298Slc if_num, strm_if->curr_alt, n_pkt, pkt_size,
310361b2e298Slc strm_if->curr_ep->wMaxPacketSize,
310461b2e298Slc (1 + ((strm_if->curr_ep->wMaxPacketSize>> 11) & 3)),
310561b2e298Slc frame_size);
3106c77a61a7Syz
3107c77a61a7Syz if (n_pkt > USBVC_MAX_PKTS) {
3108c77a61a7Syz n_pkt = USBVC_MAX_PKTS;
3109c77a61a7Syz }
3110c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
3111ef772e54Sfb "usbvc_start_isoc_polling: n_pkt=%d", n_pkt);
3112c77a61a7Syz
3113c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
3114c77a61a7Syz if ((req = usb_alloc_isoc_req(usbvcp->usbvc_dip, n_pkt,
3115c77a61a7Syz n_pkt * pkt_size, USB_FLAGS_SLEEP)) != NULL) {
3116c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
3117c77a61a7Syz
3118c77a61a7Syz /* Initialize the packet descriptor */
3119c77a61a7Syz for (pkt = 0; pkt < n_pkt; pkt++) {
3120c77a61a7Syz req->isoc_pkt_descr[pkt].isoc_pkt_length = pkt_size;
3121c77a61a7Syz }
3122c77a61a7Syz
3123c77a61a7Syz req->isoc_pkts_count = n_pkt;
3124c77a61a7Syz
3125c77a61a7Syz /*
3126c77a61a7Syz * zero here indicates that HCDs will use
3127c77a61a7Syz * isoc_pkt_descr->isoc_pkt_length to calculate
3128c77a61a7Syz * isoc_pkts_length.
3129c77a61a7Syz */
3130c77a61a7Syz req->isoc_pkts_length = 0;
3131c77a61a7Syz req->isoc_attributes = USB_ATTRS_ISOC_XFER_ASAP |
3132c77a61a7Syz USB_ATTRS_SHORT_XFER_OK | USB_ATTRS_AUTOCLEARING;
3133c77a61a7Syz req->isoc_cb = usbvc_isoc_cb;
3134c77a61a7Syz req->isoc_exc_cb = usbvc_isoc_exc_cb;
3135c77a61a7Syz usbvcp->usbvc_io_type = io_type;
3136c77a61a7Syz req->isoc_client_private = (usb_opaque_t)usbvcp;
3137c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
3138c77a61a7Syz rval = usb_pipe_isoc_xfer(strm_if->datain_ph, req, 0);
3139c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
3140c77a61a7Syz } else {
3141c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
3142c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
3143c77a61a7Syz "usbvc_start_isoc_polling: alloc_isoc_req fail");
3144c77a61a7Syz
3145c77a61a7Syz return (USB_FAILURE);
3146c77a61a7Syz }
3147c77a61a7Syz
3148c77a61a7Syz if (rval != USB_SUCCESS) {
3149c77a61a7Syz if (req) {
3150c77a61a7Syz usb_free_isoc_req(req);
3151c77a61a7Syz req = NULL;
3152c77a61a7Syz }
3153c77a61a7Syz }
3154c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_IOCTL, usbvcp->usbvc_log_handle,
3155ef772e54Sfb "usbvc_start_isoc_polling: return, rval=%d", rval);
3156c77a61a7Syz
3157c77a61a7Syz return (rval);
3158c77a61a7Syz }
3159c77a61a7Syz
3160c77a61a7Syz /* callbacks for receiving video data (isco in transfer) */
3161c77a61a7Syz
3162c77a61a7Syz /*ARGSUSED*/
3163c77a61a7Syz /* Isoc transfer callback, get video data */
3164c77a61a7Syz static void
usbvc_isoc_cb(usb_pipe_handle_t ph,usb_isoc_req_t * isoc_req)3165c77a61a7Syz usbvc_isoc_cb(usb_pipe_handle_t ph, usb_isoc_req_t *isoc_req)
3166c77a61a7Syz {
3167c77a61a7Syz usbvc_state_t *usbvcp =
3168ef772e54Sfb (usbvc_state_t *)isoc_req->isoc_client_private;
3169c77a61a7Syz int i;
3170c77a61a7Syz mblk_t *data = isoc_req->isoc_data;
3171c77a61a7Syz usbvc_buf_grp_t *bufgrp;
3172c77a61a7Syz
3173c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
3174c77a61a7Syz
3175c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3176c77a61a7Syz "usbvc_isoc_cb: rq=0x%p, fno=%" PRId64 ", n_pkts=%u, flag=0x%x,"
3177c77a61a7Syz " data=0x%p, cnt=%d",
3178112116d8Sfb (void *)isoc_req, isoc_req->isoc_frame_no,
3179112116d8Sfb isoc_req->isoc_pkts_count, isoc_req->isoc_attributes,
3180112116d8Sfb (void *)isoc_req->isoc_data, isoc_req->isoc_error_count);
3181c77a61a7Syz
3182c77a61a7Syz ASSERT((isoc_req->isoc_cb_flags & USB_CB_INTR_CONTEXT) != 0);
3183c77a61a7Syz for (i = 0; i < isoc_req->isoc_pkts_count; i++) {
3184c77a61a7Syz
3185c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3186c77a61a7Syz "\tpkt%d: "
3187c77a61a7Syz "pktsize=%d status=%d resid=%d",
3188c77a61a7Syz i,
3189c77a61a7Syz isoc_req->isoc_pkt_descr[i].isoc_pkt_length,
3190c77a61a7Syz isoc_req->isoc_pkt_descr[i].isoc_pkt_status,
3191c77a61a7Syz isoc_req->isoc_pkt_descr[i].isoc_pkt_actual_length);
3192c77a61a7Syz
3193c77a61a7Syz if (isoc_req->isoc_pkt_descr[i].isoc_pkt_status !=
3194c77a61a7Syz USB_CR_OK) {
3195c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_CB,
3196c77a61a7Syz usbvcp->usbvc_log_handle,
3197c77a61a7Syz "record: pkt=%d status=%s", i, usb_str_cr(
3198c77a61a7Syz isoc_req->isoc_pkt_descr[i].isoc_pkt_status));
3199c77a61a7Syz }
3200c77a61a7Syz
3201c77a61a7Syz if (usbvcp->usbvc_io_type == V4L2_MEMORY_MMAP) {
3202c77a61a7Syz bufgrp = &usbvcp->usbvc_curr_strm->buf_map;
3203c77a61a7Syz } else {
3204c77a61a7Syz bufgrp = &usbvcp->usbvc_curr_strm->buf_read;
3205c77a61a7Syz }
3206c77a61a7Syz
3207c77a61a7Syz if (isoc_req->isoc_pkt_descr[i].isoc_pkt_actual_length) {
3208c77a61a7Syz if (usbvc_decode_stream_header(usbvcp, bufgrp, data,
3209c77a61a7Syz isoc_req->isoc_pkt_descr[i].isoc_pkt_actual_length)
3210c77a61a7Syz != USB_SUCCESS) {
3211c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_CB,
3212c77a61a7Syz usbvcp->usbvc_log_handle, "decode error");
3213c77a61a7Syz }
3214c77a61a7Syz if (bufgrp->buf_filling &&
3215c77a61a7Syz (bufgrp->buf_filling->status == USBVC_BUF_ERR ||
3216c77a61a7Syz bufgrp->buf_filling->status == USBVC_BUF_DONE)) {
3217c77a61a7Syz
3218c77a61a7Syz /* Move the buf to the full list */
3219c77a61a7Syz list_insert_tail(&bufgrp->uv_buf_done,
3220c77a61a7Syz bufgrp->buf_filling);
3221c77a61a7Syz
3222c77a61a7Syz bufgrp->buf_filling = NULL;
3223c77a61a7Syz
3224c77a61a7Syz if (usbvcp->usbvc_io_type == V4L2_MEMORY_MMAP) {
3225c77a61a7Syz cv_broadcast(&usbvcp->usbvc_mapio_cv);
3226c77a61a7Syz } else {
3227c77a61a7Syz cv_broadcast(&usbvcp->usbvc_read_cv);
3228c77a61a7Syz }
3229c77a61a7Syz }
3230c77a61a7Syz }
323161b2e298Slc
323261b2e298Slc data->b_rptr += isoc_req->isoc_pkt_descr[i].isoc_pkt_length;
3233c77a61a7Syz }
3234c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
3235c77a61a7Syz usb_free_isoc_req(isoc_req);
3236c77a61a7Syz }
3237c77a61a7Syz
3238c77a61a7Syz
3239c77a61a7Syz /*ARGSUSED*/
3240c77a61a7Syz static void
usbvc_isoc_exc_cb(usb_pipe_handle_t ph,usb_isoc_req_t * isoc_req)3241c77a61a7Syz usbvc_isoc_exc_cb(usb_pipe_handle_t ph, usb_isoc_req_t *isoc_req)
3242c77a61a7Syz {
3243c77a61a7Syz usbvc_state_t *usbvcp =
3244ef772e54Sfb (usbvc_state_t *)isoc_req->isoc_client_private;
3245c77a61a7Syz usb_cr_t completion_reason;
3246c77a61a7Syz int rval;
3247c77a61a7Syz usbvc_stream_if_t *strm_if;
3248c77a61a7Syz
3249c77a61a7Syz ASSERT(!list_is_empty(&usbvcp->usbvc_stream_list));
3250c77a61a7Syz
3251c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
3252c77a61a7Syz
3253c77a61a7Syz /* get the first stream interface */
3254c77a61a7Syz strm_if = usbvcp->usbvc_curr_strm;
3255c77a61a7Syz
3256c77a61a7Syz completion_reason = isoc_req->isoc_completion_reason;
3257c77a61a7Syz
325861b2e298Slc USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3259c77a61a7Syz "usbvc_isoc_exc_cb: ph=0x%p, isoc_req=0x%p, cr=%d",
3260c77a61a7Syz (void *)ph, (void *)isoc_req, completion_reason);
3261c77a61a7Syz
3262c77a61a7Syz ASSERT((isoc_req->isoc_cb_flags & USB_CB_INTR_CONTEXT) == 0);
3263c77a61a7Syz
3264c77a61a7Syz switch (completion_reason) {
3265c77a61a7Syz case USB_CR_STOPPED_POLLING:
3266c77a61a7Syz case USB_CR_PIPE_CLOSING:
3267c77a61a7Syz case USB_CR_PIPE_RESET:
3268c77a61a7Syz
3269c77a61a7Syz break;
3270c77a61a7Syz case USB_CR_NO_RESOURCES:
3271c77a61a7Syz /*
3272c77a61a7Syz * keep the show going: Since we have the original
3273c77a61a7Syz * request, we just resubmit it
3274c77a61a7Syz */
3275c77a61a7Syz rval = usb_pipe_isoc_xfer(strm_if->datain_ph, isoc_req,
3276c77a61a7Syz USB_FLAGS_NOSLEEP);
3277c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3278c77a61a7Syz "usbvc_isoc_exc_cb: restart capture rval=%d", rval);
3279c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
3280c77a61a7Syz
3281c77a61a7Syz return;
3282c77a61a7Syz default:
3283c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
3284c77a61a7Syz usb_pipe_stop_isoc_polling(ph, USB_FLAGS_NOSLEEP);
3285c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3286c77a61a7Syz "usbvc_isoc_exc_cb: stop polling");
3287c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
3288c77a61a7Syz }
3289c77a61a7Syz usb_free_isoc_req(isoc_req);
3290c77a61a7Syz strm_if->start_polling = 0;
3291c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3292c77a61a7Syz "usbvc_isoc_exc_cb: start_polling=%d cr=%d",
3293c77a61a7Syz strm_if->start_polling, completion_reason);
3294c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
3295c77a61a7Syz }
3296c77a61a7Syz
3297c77a61a7Syz /*
3298c77a61a7Syz * Other utility functions
3299c77a61a7Syz */
3300c77a61a7Syz
3301c77a61a7Syz /*
3302c77a61a7Syz * Find a proper alternate according to the bandwidth that the current video
3303c77a61a7Syz * format need;
3304c77a61a7Syz * Set alternate by calling usb_set_alt_if;
3305c77a61a7Syz * Called before open pipes in stream interface.
3306c77a61a7Syz */
3307c77a61a7Syz static int
usbvc_set_alt(usbvc_state_t * usbvcp,usbvc_stream_if_t * strm_if)3308c77a61a7Syz usbvc_set_alt(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if)
3309c77a61a7Syz {
3310c77a61a7Syz usb_alt_if_data_t *alt;
3311c77a61a7Syz uint_t i, j, if_num;
3312c77a61a7Syz uint16_t pktsize, curr_pktsize;
3313c77a61a7Syz uint32_t bandwidth;
3314c77a61a7Syz int rval = USB_SUCCESS;
3315c77a61a7Syz usbvc_input_header_t *ihd;
3316c77a61a7Syz usbvc_output_header_t *ohd;
3317c77a61a7Syz
3318c77a61a7Syz ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
3319c77a61a7Syz
3320c77a61a7Syz LE_TO_UINT32(strm_if->ctrl_pc.dwMaxPayloadTransferSize, 0, bandwidth);
3321c77a61a7Syz if (!bandwidth) {
3322c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
3323c77a61a7Syz "usbvc_set_alt: bandwidth is not set yet");
3324c77a61a7Syz
3325c77a61a7Syz return (USB_FAILURE);
3326c77a61a7Syz }
3327c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
3328ef772e54Sfb "usbvc_set_alt: bandwidth=%x", bandwidth);
3329c77a61a7Syz
3330c77a61a7Syz strm_if->curr_ep = NULL;
3331c77a61a7Syz curr_pktsize = 0xffff;
3332c77a61a7Syz ohd = strm_if->output_header;
3333c77a61a7Syz ihd = strm_if->input_header;
3334c77a61a7Syz /*
3335c77a61a7Syz * Find one alternate setting whose isoc ep's max pktsize is just
3336c77a61a7Syz * enough for the bandwidth.
3337c77a61a7Syz */
3338c77a61a7Syz for (i = 0; i < strm_if->if_descr->if_n_alt; i++) {
3339c77a61a7Syz alt = &strm_if->if_descr->if_alt[i];
3340c77a61a7Syz
3341c77a61a7Syz for (j = 0; j < alt->altif_n_ep; j++) {
3342c77a61a7Syz
3343c77a61a7Syz /* if this stream interface is for input */
3344c77a61a7Syz if (ihd != NULL &&
3345c77a61a7Syz alt->altif_ep[j].ep_descr.bEndpointAddress !=
3346c77a61a7Syz ihd->descr->bEndpointAddress) {
3347c77a61a7Syz
3348c77a61a7Syz continue;
3349c77a61a7Syz }
3350c77a61a7Syz /* if this stream interface is for output */
3351c77a61a7Syz if (ohd != NULL &&
3352c77a61a7Syz alt->altif_ep[j].ep_descr.bEndpointAddress !=
3353c77a61a7Syz ohd->descr->bEndpointAddress) {
3354c77a61a7Syz
3355c77a61a7Syz continue;
3356c77a61a7Syz }
3357c77a61a7Syz pktsize =
3358c77a61a7Syz alt->altif_ep[j].ep_descr.wMaxPacketSize;
3359c77a61a7Syz pktsize = HS_PKT_SIZE(pktsize);
3360c77a61a7Syz if (pktsize >= bandwidth && pktsize < curr_pktsize) {
3361c77a61a7Syz curr_pktsize = pktsize;
3362c77a61a7Syz strm_if->curr_alt = i;
3363c77a61a7Syz strm_if->curr_ep = &alt->altif_ep[j].ep_descr;
3364c77a61a7Syz }
3365c77a61a7Syz }
3366c77a61a7Syz }
3367c77a61a7Syz if (!strm_if->curr_ep) {
3368c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
3369c77a61a7Syz "usbvc_set_alt: can't find a proper ep to satisfy"
3370c77a61a7Syz " the given bandwidth");
3371c77a61a7Syz
3372c77a61a7Syz return (USB_FAILURE);
3373c77a61a7Syz }
3374c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
3375c77a61a7Syz "usbvc_set_alt: strm_if->curr_alt=%d", strm_if->curr_alt);
3376c77a61a7Syz if_num = strm_if->if_descr->if_alt->altif_descr.bInterfaceNumber;
3377c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
3378c77a61a7Syz if ((rval = usb_set_alt_if(usbvcp->usbvc_dip, if_num, strm_if->curr_alt,
3379ef772e54Sfb USB_FLAGS_SLEEP, NULL, NULL)) != USB_SUCCESS) {
3380c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
3381c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
3382c77a61a7Syz "usbvc_set_alt: usb_set_alt_if fail, if.alt=%d.%d, rval=%d",
3383c77a61a7Syz if_num, strm_if->curr_alt, rval);
3384c77a61a7Syz
3385c77a61a7Syz return (rval);
3386c77a61a7Syz }
3387c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
3388c77a61a7Syz
3389c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_OPEN, usbvcp->usbvc_log_handle,
3390c77a61a7Syz "usbvc_set_alt: return, if_num=%d, alt=%d",
3391c77a61a7Syz if_num, strm_if->curr_alt);
3392c77a61a7Syz
3393c77a61a7Syz return (rval);
3394c77a61a7Syz }
3395c77a61a7Syz
3396c77a61a7Syz
3397c77a61a7Syz /*
3398c77a61a7Syz * Decode stream header for mjpeg and uncompressed format video data.
3399c77a61a7Syz * mjpeg and uncompressed format have the same stream header. See their
3400c77a61a7Syz * payload spec, 2.2 and 2.4
3401c77a61a7Syz */
3402c77a61a7Syz static int
usbvc_decode_stream_header(usbvc_state_t * usbvcp,usbvc_buf_grp_t * bufgrp,mblk_t * data,int actual_len)3403c77a61a7Syz usbvc_decode_stream_header(usbvc_state_t *usbvcp, usbvc_buf_grp_t *bufgrp,
340411b5d82fSToomas Soome mblk_t *data, int actual_len)
3405c77a61a7Syz {
3406c77a61a7Syz uint32_t len, buf_left, data_len;
3407c77a61a7Syz usbvc_stream_if_t *strm_if;
3408c77a61a7Syz uchar_t head_flag, head_len;
3409c77a61a7Syz usbvc_buf_t *buf_filling;
3410c77a61a7Syz
3411c77a61a7Syz ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
3412c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3413c77a61a7Syz "usbvc_decode_stream_header: enter. actual_len=%x", actual_len);
3414c77a61a7Syz
3415c77a61a7Syz /* header length check. */
3416c77a61a7Syz if (actual_len < 2) {
3417c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3418c77a61a7Syz "usbvc_decode_stream_header: header is not completed");
3419c77a61a7Syz
3420c77a61a7Syz return (USB_FAILURE);
3421c77a61a7Syz }
3422c77a61a7Syz head_len = data->b_rptr[0];
3423c77a61a7Syz head_flag = data->b_rptr[1];
3424c77a61a7Syz
3425c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3426c77a61a7Syz "usbvc_decode_stream_header: headlen=%x", head_len);
3427c77a61a7Syz
3428c77a61a7Syz /* header length check. */
3429c77a61a7Syz if (actual_len < head_len) {
3430c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3431c77a61a7Syz "usbvc_decode_stream_header: actual_len < head_len");
3432c77a61a7Syz
3433c77a61a7Syz return (USB_FAILURE);
3434c77a61a7Syz }
3435c77a61a7Syz
3436c77a61a7Syz /*
3437c77a61a7Syz * If there is no stream data in this packet and this packet is not
3438c77a61a7Syz * used to indicate the end of a frame, then just skip it.
3439c77a61a7Syz */
3440c77a61a7Syz if ((actual_len == head_len) && !(head_flag & USBVC_STREAM_EOF)) {
3441ef772e54Sfb USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3442c77a61a7Syz "usbvc_decode_stream_header: only header, no data");
3443c77a61a7Syz
3444c77a61a7Syz return (USB_FAILURE);
3445c77a61a7Syz }
3446c77a61a7Syz
3447c77a61a7Syz /* Get the first stream interface */
3448c77a61a7Syz strm_if = usbvcp->usbvc_curr_strm;
3449c77a61a7Syz
3450c77a61a7Syz LE_TO_UINT32(strm_if->ctrl_pc.dwMaxVideoFrameSize, 0, len);
3451c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3452c77a61a7Syz "usbvc_decode_stream_header: dwMaxVideoFrameSize=%x, head_flag=%x",
3453c77a61a7Syz len, head_flag);
3454c77a61a7Syz
3455c77a61a7Syz /*
3456c77a61a7Syz * if no buf is filling, pick one buf from free list and alloc data
3457c77a61a7Syz * mem for the buf.
3458c77a61a7Syz */
3459c77a61a7Syz if (!bufgrp->buf_filling) {
3460c77a61a7Syz if (list_is_empty(&bufgrp->uv_buf_free)) {
3461c77a61a7Syz strm_if->fid = head_flag & USBVC_STREAM_FID;
3462c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3463c77a61a7Syz "usbvc_decode_stream_header: free list are empty");
3464c77a61a7Syz
3465c77a61a7Syz return (USB_FAILURE);
3466c77a61a7Syz
3467c77a61a7Syz } else {
3468c77a61a7Syz bufgrp->buf_filling =
3469c77a61a7Syz (usbvc_buf_t *)list_head(&bufgrp->uv_buf_free);
3470c77a61a7Syz
3471c77a61a7Syz /* unlink from buf free list */
3472c77a61a7Syz list_remove(&bufgrp->uv_buf_free, bufgrp->buf_filling);
3473c77a61a7Syz }
3474c77a61a7Syz bufgrp->buf_filling->filled = 0;
3475c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3476c77a61a7Syz "usbvc_decode_stream_header: status=%d",
3477c77a61a7Syz bufgrp->buf_filling->status);
3478c77a61a7Syz bufgrp->buf_filling->status = USBVC_BUF_EMPTY;
3479c77a61a7Syz }
3480c77a61a7Syz buf_filling = bufgrp->buf_filling;
3481c77a61a7Syz ASSERT(buf_filling->len >= buf_filling->filled);
3482c77a61a7Syz buf_left = buf_filling->len - buf_filling->filled;
3483c77a61a7Syz
3484c77a61a7Syz /* if no buf room left, then return with a err status */
3485c77a61a7Syz if (buf_left == 0) {
348609dd0d6cSRaymond Chen /* buffer full, got an EOF packet(head only, no payload) */
348709dd0d6cSRaymond Chen if ((head_flag & USBVC_STREAM_EOF) &&
348809dd0d6cSRaymond Chen (actual_len == head_len)) {
348909dd0d6cSRaymond Chen buf_filling->status = USBVC_BUF_DONE;
349009dd0d6cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
349109dd0d6cSRaymond Chen "usbvc_decode_stream_header: got a EOF packet");
349209dd0d6cSRaymond Chen
349309dd0d6cSRaymond Chen return (USB_SUCCESS);
349409dd0d6cSRaymond Chen }
349509dd0d6cSRaymond Chen
349609dd0d6cSRaymond Chen /* Otherwise, mark the buf error and return failure */
3497c77a61a7Syz buf_filling->status = USBVC_BUF_ERR;
349809dd0d6cSRaymond Chen USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
349909dd0d6cSRaymond Chen "usbvc_decode_stream_header: frame buf full");
3500c77a61a7Syz
3501c77a61a7Syz return (USB_FAILURE);
3502c77a61a7Syz }
3503c77a61a7Syz
3504c77a61a7Syz /* get this sample's data length except header */
3505c77a61a7Syz data_len = actual_len - head_len;
3506c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3507c77a61a7Syz "usbvc_decode_stream_header: fid=%x, len=%x, filled=%x",
3508c77a61a7Syz strm_if->fid, buf_filling->len, buf_filling->filled);
3509c77a61a7Syz
3510c77a61a7Syz /* if the first sample for a frame */
3511c77a61a7Syz if (buf_filling->filled == 0) {
3512c77a61a7Syz /*
3513c77a61a7Syz * Only if it is the frist packet of a frame,
3514c77a61a7Syz * we will begin filling a frame.
3515c77a61a7Syz */
3516c77a61a7Syz if (strm_if->fid != 0xff && strm_if->fid ==
3517c77a61a7Syz (head_flag & USBVC_STREAM_FID)) {
3518c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3519c77a61a7Syz "usbvc_decode_stream_header: 1st sample of a frame,"
3520c77a61a7Syz " fid is incorrect.");
3521c77a61a7Syz
3522c77a61a7Syz return (USB_FAILURE);
3523c77a61a7Syz }
3524c77a61a7Syz strm_if->fid = head_flag & USBVC_STREAM_FID;
3525c77a61a7Syz
3526c77a61a7Syz /* If in the middle of a frame, fid should be consistent. */
3527c77a61a7Syz } else if (strm_if->fid != (head_flag & USBVC_STREAM_FID)) {
3528c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3529c77a61a7Syz "usbvc_decode_stream_header: fid is incorrect.");
3530c77a61a7Syz strm_if->fid = head_flag & USBVC_STREAM_FID;
3531c77a61a7Syz buf_filling->status = USBVC_BUF_ERR;
3532c77a61a7Syz
3533c77a61a7Syz return (USB_FAILURE);
3534c77a61a7Syz }
3535c77a61a7Syz if (data_len) {
3536c77a61a7Syz bcopy((void *)(data->b_rptr + head_len),
3537c77a61a7Syz (void *)(buf_filling->data + buf_filling->filled),
3538c77a61a7Syz min(data_len, buf_left));
3539c77a61a7Syz
3540c77a61a7Syz buf_filling->filled += min(data_len, buf_left);
3541c77a61a7Syz }
3542c77a61a7Syz
3543c77a61a7Syz /* If the last packet for this frame */
3544c77a61a7Syz if (head_flag & USBVC_STREAM_EOF) {
3545c77a61a7Syz buf_filling->status = USBVC_BUF_DONE;
3546c77a61a7Syz }
3547c77a61a7Syz if (data_len > buf_left) {
3548c77a61a7Syz buf_filling->status = USBVC_BUF_ERR;
3549c77a61a7Syz }
3550c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_CB, usbvcp->usbvc_log_handle,
3551c77a61a7Syz "usbvc_decode_stream_header: buf_status=%d", buf_filling->status);
3552c77a61a7Syz
3553c77a61a7Syz return (USB_SUCCESS);
3554c77a61a7Syz }
3555c77a61a7Syz
3556c77a61a7Syz
3557c77a61a7Syz /*
3558c77a61a7Syz * usbvc_serialize_access:
3559c77a61a7Syz * Get the serial synchronization object before returning.
3560c77a61a7Syz *
3561c77a61a7Syz * Arguments:
3562c77a61a7Syz * usbvcp - Pointer to usbvc state structure
3563c77a61a7Syz * waitsig - Set to:
3564c77a61a7Syz * USBVC_SER_SIG - to wait such that a signal can interrupt
3565c77a61a7Syz * USBVC_SER_NOSIG - to wait such that a signal cannot interrupt
3566c77a61a7Syz */
3567c77a61a7Syz static int
usbvc_serialize_access(usbvc_state_t * usbvcp,boolean_t waitsig)3568c77a61a7Syz usbvc_serialize_access(usbvc_state_t *usbvcp, boolean_t waitsig)
3569c77a61a7Syz {
3570c77a61a7Syz int rval = 1;
3571c77a61a7Syz
3572c77a61a7Syz ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
3573c77a61a7Syz
3574c77a61a7Syz while (usbvcp->usbvc_serial_inuse) {
3575c77a61a7Syz if (waitsig == USBVC_SER_SIG) {
3576c77a61a7Syz rval = cv_wait_sig(&usbvcp->usbvc_serial_cv,
3577c77a61a7Syz &usbvcp->usbvc_mutex);
3578c77a61a7Syz } else {
3579c77a61a7Syz cv_wait(&usbvcp->usbvc_serial_cv,
3580c77a61a7Syz &usbvcp->usbvc_mutex);
3581c77a61a7Syz }
3582c77a61a7Syz }
3583c77a61a7Syz usbvcp->usbvc_serial_inuse = B_TRUE;
3584c77a61a7Syz
3585c77a61a7Syz return (rval);
3586c77a61a7Syz }
3587c77a61a7Syz
3588c77a61a7Syz
3589c77a61a7Syz /*
3590c77a61a7Syz * usbvc_release_access:
3591c77a61a7Syz * Release the serial synchronization object.
3592c77a61a7Syz */
3593c77a61a7Syz static void
usbvc_release_access(usbvc_state_t * usbvcp)3594c77a61a7Syz usbvc_release_access(usbvc_state_t *usbvcp)
3595c77a61a7Syz {
3596c77a61a7Syz ASSERT(mutex_owned(&usbvcp->usbvc_mutex));
3597c77a61a7Syz usbvcp->usbvc_serial_inuse = B_FALSE;
3598c77a61a7Syz cv_broadcast(&usbvcp->usbvc_serial_cv);
3599c77a61a7Syz }
3600c77a61a7Syz
3601c77a61a7Syz
3602c77a61a7Syz /* Send req to video control interface to get ctrl */
3603c77a61a7Syz int
usbvc_vc_get_ctrl(usbvc_state_t * usbvcp,uint8_t req_code,uint8_t entity_id,uint16_t cs,uint16_t wlength,mblk_t * data)3604c77a61a7Syz usbvc_vc_get_ctrl(usbvc_state_t *usbvcp, uint8_t req_code, uint8_t entity_id,
3605c77a61a7Syz uint16_t cs, uint16_t wlength, mblk_t *data)
3606c77a61a7Syz {
3607c77a61a7Syz usb_cb_flags_t cb_flags;
3608c77a61a7Syz usb_cr_t cr;
3609c77a61a7Syz usb_ctrl_setup_t setup;
3610c77a61a7Syz
3611c77a61a7Syz setup.bmRequestType = USBVC_GET_IF; /* bmRequestType */
3612c77a61a7Syz setup.bRequest = req_code; /* bRequest */
3613c77a61a7Syz setup.wValue = cs<<8;
3614c77a61a7Syz setup.wIndex = entity_id<<8;
3615c77a61a7Syz setup.wLength = wlength;
3616c77a61a7Syz setup.attrs = 0;
3617c77a61a7Syz
3618c77a61a7Syz if (usb_pipe_ctrl_xfer_wait(usbvcp->usbvc_default_ph, &setup, &data,
3619c77a61a7Syz &cr, &cb_flags, 0) != USB_SUCCESS) {
3620c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3621c77a61a7Syz "usbvc_vc_get_ctrl: cmd failed, cr=%d, cb_flags=%x",
3622c77a61a7Syz cr, cb_flags);
3623c77a61a7Syz
3624c77a61a7Syz return (USB_FAILURE);
3625c77a61a7Syz }
3626c77a61a7Syz
3627c77a61a7Syz return (USB_SUCCESS);
3628c77a61a7Syz }
3629c77a61a7Syz
3630c77a61a7Syz
3631c77a61a7Syz /* Send req to video control interface to get ctrl */
3632c77a61a7Syz int
usbvc_vc_set_ctrl(usbvc_state_t * usbvcp,uint8_t req_code,uint8_t entity_id,uint16_t cs,uint16_t wlength,mblk_t * data)3633c77a61a7Syz usbvc_vc_set_ctrl(usbvc_state_t *usbvcp, uint8_t req_code, uint8_t entity_id,
363411b5d82fSToomas Soome uint16_t cs, uint16_t wlength, mblk_t *data)
3635c77a61a7Syz {
3636c77a61a7Syz usb_cb_flags_t cb_flags;
3637c77a61a7Syz usb_cr_t cr;
3638c77a61a7Syz usb_ctrl_setup_t setup;
3639c77a61a7Syz
3640c77a61a7Syz setup.bmRequestType = USBVC_SET_IF; /* bmRequestType */
3641c77a61a7Syz setup.bRequest = req_code; /* bRequest */
3642c77a61a7Syz setup.wValue = cs<<8;
3643c77a61a7Syz setup.wIndex = entity_id<<8;
3644c77a61a7Syz setup.wLength = wlength;
3645c77a61a7Syz setup.attrs = 0;
3646c77a61a7Syz
3647c77a61a7Syz if (usb_pipe_ctrl_xfer_wait(usbvcp->usbvc_default_ph, &setup, &data,
3648c77a61a7Syz &cr, &cb_flags, 0) != USB_SUCCESS) {
3649c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3650c77a61a7Syz "usbvc_vc_set_ctrl: cmd failed, cr=%d, cb_flags=%x",
3651c77a61a7Syz cr, cb_flags);
3652c77a61a7Syz
3653c77a61a7Syz return (USB_FAILURE);
3654c77a61a7Syz }
3655c77a61a7Syz
3656c77a61a7Syz return (USB_SUCCESS);
3657c77a61a7Syz }
3658c77a61a7Syz
3659c77a61a7Syz
3660c77a61a7Syz /* Set probe or commit ctrl for video stream interface */
3661c77a61a7Syz int
usbvc_vs_set_probe_commit(usbvc_state_t * usbvcp,usbvc_stream_if_t * strm_if,usbvc_vs_probe_commit_t * ctrl_pc,uchar_t cs)3662c77a61a7Syz usbvc_vs_set_probe_commit(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if,
366311b5d82fSToomas Soome usbvc_vs_probe_commit_t *ctrl_pc, uchar_t cs)
3664c77a61a7Syz {
3665c77a61a7Syz mblk_t *data;
3666c77a61a7Syz usb_cb_flags_t cb_flags;
3667c77a61a7Syz usb_cr_t cr;
3668c77a61a7Syz usb_ctrl_setup_t setup;
3669c77a61a7Syz int rval;
3670c77a61a7Syz
3671c77a61a7Syz setup.bmRequestType = USBVC_SET_IF; /* bmRequestType */
3672c77a61a7Syz setup.bRequest = SET_CUR; /* bRequest */
3673c77a61a7Syz
3674c77a61a7Syz /* wValue, VS_PROBE_CONTROL or VS_COMMIT_CONTROL */
3675c77a61a7Syz setup.wValue = cs;
3676c77a61a7Syz
3677c77a61a7Syz /* UVC Spec: this value must be put to the high byte */
3678c77a61a7Syz setup.wValue = setup.wValue << 8;
3679c77a61a7Syz
3680c77a61a7Syz setup.wIndex = strm_if->if_descr->if_alt->altif_descr.bInterfaceNumber;
3681c77a61a7Syz setup.wLength = usbvcp->usbvc_vc_header->descr->bcdUVC[0] ? 34 : 26;
3682c77a61a7Syz setup.attrs = 0;
3683c77a61a7Syz
3684c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3685c77a61a7Syz "usbvc_vs_set_probe_commit: wLength=%d", setup.wLength);
3686c77a61a7Syz
3687c77a61a7Syz /* Data block */
3688c77a61a7Syz if ((data = allocb(setup.wLength, BPRI_HI)) == NULL) {
3689c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3690c77a61a7Syz "usbvc_vs_set_probe_commit: allocb failed");
3691c77a61a7Syz
3692c77a61a7Syz return (USB_FAILURE);
3693c77a61a7Syz }
3694c77a61a7Syz
3695c77a61a7Syz bcopy(ctrl_pc, data->b_rptr, setup.wLength);
3696c77a61a7Syz data->b_wptr += setup.wLength;
3697c77a61a7Syz
3698c77a61a7Syz if ((rval = usb_pipe_ctrl_xfer_wait(usbvcp->usbvc_default_ph, &setup,
3699c77a61a7Syz &data, &cr, &cb_flags, 0)) != USB_SUCCESS) {
3700c77a61a7Syz if (data) {
3701c77a61a7Syz freemsg(data);
3702c77a61a7Syz }
3703c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3704c77a61a7Syz "usbvc_vs_set_probe_commit: fail, rval=%d, cr=%d, "
3705c77a61a7Syz "cb_flags=%x", rval, cr, cb_flags);
3706c77a61a7Syz
3707c77a61a7Syz return (rval);
3708c77a61a7Syz }
3709c77a61a7Syz if (data) {
3710c77a61a7Syz freemsg(data);
3711c77a61a7Syz }
3712c77a61a7Syz
3713c77a61a7Syz return (USB_SUCCESS);
3714c77a61a7Syz }
3715c77a61a7Syz
3716c77a61a7Syz
3717c77a61a7Syz /* Get probe ctrl for vodeo stream interface */
3718c77a61a7Syz int
usbvc_vs_get_probe(usbvc_state_t * usbvcp,usbvc_stream_if_t * strm_if,usbvc_vs_probe_commit_t * ctrl_pc,uchar_t bRequest)3719c77a61a7Syz usbvc_vs_get_probe(usbvc_state_t *usbvcp, usbvc_stream_if_t *strm_if,
372011b5d82fSToomas Soome usbvc_vs_probe_commit_t *ctrl_pc, uchar_t bRequest)
3721c77a61a7Syz {
3722c77a61a7Syz mblk_t *data = NULL;
3723c77a61a7Syz usb_cb_flags_t cb_flags;
3724c77a61a7Syz usb_cr_t cr;
3725c77a61a7Syz usb_ctrl_setup_t setup;
3726c77a61a7Syz
3727c77a61a7Syz setup.bmRequestType = USBVC_GET_IF; /* bmRequestType */
3728c77a61a7Syz setup.bRequest = bRequest; /* bRequest */
3729c77a61a7Syz setup.wValue = VS_PROBE_CONTROL; /* wValue, PROBE or COMMIT */
3730c77a61a7Syz setup.wValue = setup.wValue << 8;
3731c77a61a7Syz setup.wIndex =
3732c77a61a7Syz (uint16_t)strm_if->if_descr->if_alt->altif_descr.bInterfaceNumber;
3733c77a61a7Syz setup.wLength = usbvcp->usbvc_vc_header->descr->bcdUVC[0] ? 34 : 26;
3734c77a61a7Syz
3735c77a61a7Syz setup.attrs = 0;
3736c77a61a7Syz
3737c77a61a7Syz if (usb_pipe_ctrl_xfer_wait(usbvcp->usbvc_default_ph, &setup, &data,
3738c77a61a7Syz &cr, &cb_flags, 0) != USB_SUCCESS) {
3739c77a61a7Syz if (data) {
3740c77a61a7Syz freemsg(data);
3741c77a61a7Syz }
3742c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3743c77a61a7Syz "usbvc_vs_get_probe: cmd failed, cr=%d, cb_flags=%x",
3744c77a61a7Syz cr, cb_flags);
3745c77a61a7Syz
3746c77a61a7Syz return (USB_FAILURE);
3747c77a61a7Syz }
3748c77a61a7Syz bcopy(data->b_rptr, ctrl_pc, setup.wLength);
3749c77a61a7Syz if (data) {
3750c77a61a7Syz freemsg(data);
3751c77a61a7Syz }
3752c77a61a7Syz
3753c77a61a7Syz return (USB_SUCCESS);
3754c77a61a7Syz }
3755c77a61a7Syz
3756c77a61a7Syz
3757c77a61a7Syz /* Set a default format when open the device */
3758c77a61a7Syz static int
usbvc_set_default_stream_fmt(usbvc_state_t * usbvcp)3759c77a61a7Syz usbvc_set_default_stream_fmt(usbvc_state_t *usbvcp)
3760c77a61a7Syz {
3761c77a61a7Syz usbvc_vs_probe_commit_t ctrl, ctrl_get;
3762c77a61a7Syz usbvc_stream_if_t *strm_if;
3763c77a61a7Syz usbvc_format_group_t *curr_fmtgrp;
3764c77a61a7Syz uint32_t bandwidth;
3765c77a61a7Syz uint8_t index, i;
3766c77a61a7Syz
3767c77a61a7Syz USB_DPRINTF_L4(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3768c77a61a7Syz "usbvc_set_default_stream_fmt: enter");
3769c77a61a7Syz
3770c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
3771c77a61a7Syz if (list_is_empty(&usbvcp->usbvc_stream_list)) {
3772c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3773c77a61a7Syz "usbvc_set_default_stream_fmt: no stream interface, fail");
3774c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
3775c77a61a7Syz
3776c77a61a7Syz return (USB_FAILURE);
3777c77a61a7Syz }
3778c77a61a7Syz bzero((void *)&ctrl, sizeof (usbvc_vs_probe_commit_t));
3779c77a61a7Syz
3780c77a61a7Syz /* Get the current stream interface */
3781c77a61a7Syz strm_if = usbvcp->usbvc_curr_strm;
3782c77a61a7Syz
3783c77a61a7Syz /* Fill the probe commit req data */
3784c77a61a7Syz ctrl.bmHint[0] = 0;
3785c77a61a7Syz
3786c77a61a7Syz for (i = 0; i < strm_if->fmtgrp_cnt; i++) {
3787c77a61a7Syz curr_fmtgrp = &strm_if->format_group[i];
3788c77a61a7Syz
3789c77a61a7Syz /*
3790c77a61a7Syz * If v4l2_pixelformat is NULL, then that means there is not
3791c77a61a7Syz * a parsed format in format_group[i].
3792c77a61a7Syz */
3793c77a61a7Syz if (!curr_fmtgrp || !curr_fmtgrp->v4l2_pixelformat ||
3794c77a61a7Syz curr_fmtgrp->frame_cnt == 0) {
3795c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_DEVCTRL,
3796c77a61a7Syz usbvcp->usbvc_log_handle,
3797c77a61a7Syz "usbvc_set_default_stream_fmt: no frame, fail");
3798c77a61a7Syz
3799c77a61a7Syz continue;
3800c77a61a7Syz } else {
3801c77a61a7Syz
3802c77a61a7Syz break;
3803c77a61a7Syz }
3804c77a61a7Syz }
3805c77a61a7Syz if (!curr_fmtgrp || curr_fmtgrp->frame_cnt == 0) {
3806c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3807c77a61a7Syz "usbvc_set_default_stream_fmt: can't find a fmtgrp"
3808c77a61a7Syz "which has a frame, fail");
3809c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
3810c77a61a7Syz
3811c77a61a7Syz return (USB_FAILURE);
3812c77a61a7Syz }
3813c77a61a7Syz
3814c77a61a7Syz ctrl.bFormatIndex = curr_fmtgrp->format->bFormatIndex;
3815c77a61a7Syz
3816c77a61a7Syz /* use the first frame descr as default */
3817c77a61a7Syz ctrl.bFrameIndex = curr_fmtgrp->frames[0].descr->bFrameIndex;
3818c77a61a7Syz
3819c77a61a7Syz /* use bcopy to keep the byte sequence as 32 bit little endian */
3820c77a61a7Syz bcopy(&(curr_fmtgrp->frames[0].descr->dwDefaultFrameInterval[0]),
3821c77a61a7Syz &(ctrl.dwFrameInterval[0]), 4);
3822c77a61a7Syz
3823c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
3824c77a61a7Syz if (usbvc_vs_set_probe_commit(usbvcp, strm_if, &ctrl, VS_PROBE_CONTROL)
3825c77a61a7Syz != USB_SUCCESS) {
3826c77a61a7Syz
3827c77a61a7Syz return (USB_FAILURE);
3828c77a61a7Syz }
3829c77a61a7Syz if (usbvc_vs_get_probe(usbvcp, strm_if, &ctrl_get, GET_CUR)
3830c77a61a7Syz != USB_SUCCESS) {
3831c77a61a7Syz
3832c77a61a7Syz return (USB_FAILURE);
3833c77a61a7Syz }
3834c77a61a7Syz
3835c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
3836c77a61a7Syz LE_TO_UINT32(strm_if->ctrl_pc.dwMaxPayloadTransferSize, 0, bandwidth);
3837c77a61a7Syz USB_DPRINTF_L3(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3838c77a61a7Syz "usbvc_set_default_stream_fmt: get bandwidth=%x", bandwidth);
3839c77a61a7Syz
3840c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
3841c77a61a7Syz if (usbvc_vs_set_probe_commit(usbvcp, strm_if, &ctrl_get,
3842c77a61a7Syz VS_COMMIT_CONTROL) != USB_SUCCESS) {
3843c77a61a7Syz
3844c77a61a7Syz return (USB_FAILURE);
3845c77a61a7Syz }
3846c77a61a7Syz
3847c77a61a7Syz mutex_enter(&usbvcp->usbvc_mutex);
3848c77a61a7Syz
3849c77a61a7Syz /* it's good to check index here before use it */
3850c77a61a7Syz index = ctrl_get.bFormatIndex - curr_fmtgrp->format->bFormatIndex;
3851c77a61a7Syz if (index < strm_if->fmtgrp_cnt) {
3852c77a61a7Syz strm_if->cur_format_group = &strm_if->format_group[index];
3853c77a61a7Syz } else {
3854c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3855c77a61a7Syz "usbvc_set_default_stream_fmt: format index out of range");
3856c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
3857c77a61a7Syz
3858c77a61a7Syz return (USB_FAILURE);
3859c77a61a7Syz }
3860c77a61a7Syz
3861c77a61a7Syz index = ctrl_get.bFrameIndex -
3862c77a61a7Syz strm_if->cur_format_group->frames[0].descr->bFrameIndex;
3863c77a61a7Syz if (index < strm_if->cur_format_group->frame_cnt) {
3864c77a61a7Syz strm_if->cur_format_group->cur_frame =
3865c77a61a7Syz &strm_if->cur_format_group->frames[index];
3866c77a61a7Syz } else {
3867c77a61a7Syz USB_DPRINTF_L2(PRINT_MASK_DEVCTRL, usbvcp->usbvc_log_handle,
3868c77a61a7Syz "usbvc_set_default_stream: frame index out of range");
3869c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
3870c77a61a7Syz
3871c77a61a7Syz return (USB_FAILURE);
3872c77a61a7Syz }
3873c77a61a7Syz
3874c77a61a7Syz /*
3875c77a61a7Syz * by now, the video format is set successfully. record the current
3876c77a61a7Syz * setting to strm_if->ctrl_pc
3877c77a61a7Syz */
3878c77a61a7Syz bcopy(&ctrl_get, &strm_if->ctrl_pc, sizeof (usbvc_vs_probe_commit_t));
3879c77a61a7Syz
3880c77a61a7Syz mutex_exit(&usbvcp->usbvc_mutex);
3881c77a61a7Syz
3882c77a61a7Syz return (USB_SUCCESS);
3883c77a61a7Syz }
3884