diff options
| author | ap25164 <none@none> | 2005-06-16 12:21:18 -0700 |
|---|---|---|
| committer | ap25164 <none@none> | 2005-06-16 12:21:18 -0700 |
| commit | 8eea8e29cc4374d1ee24c25a07f45af132db3499 (patch) | |
| tree | 4086aa247025ffc4f1f2aff56f12c9ae6ebb10db /usr/src/uts/common/io/1394 | |
| parent | 89a8ba833ef2615d086ab2ae9deffa0a858ed398 (diff) | |
| download | illumos-gate-8eea8e29cc4374d1ee24c25a07f45af132db3499.tar.gz | |
PSARC/2004/364 dcam1394 driver
4841261 dcamctl links are not removed
5087103 IIDC (1394-based digital cameras) should be supported by Solaris
6261805 dcam1394 will cause a panic after being called to release its isoch resources multiple times
Diffstat (limited to 'usr/src/uts/common/io/1394')
| -rw-r--r-- | usr/src/uts/common/io/1394/targets/dcam1394/dcam.c | 1262 | ||||
| -rw-r--r-- | usr/src/uts/common/io/1394/targets/dcam1394/dcam_frame.c | 644 | ||||
| -rw-r--r-- | usr/src/uts/common/io/1394/targets/dcam1394/dcam_param.c | 1803 | ||||
| -rw-r--r-- | usr/src/uts/common/io/1394/targets/dcam1394/dcam_reg.c | 125 | ||||
| -rw-r--r-- | usr/src/uts/common/io/1394/targets/dcam1394/dcam_ring_buff.c | 350 |
5 files changed, 4184 insertions, 0 deletions
diff --git a/usr/src/uts/common/io/1394/targets/dcam1394/dcam.c b/usr/src/uts/common/io/1394/targets/dcam1394/dcam.c new file mode 100644 index 0000000000..cb05730690 --- /dev/null +++ b/usr/src/uts/common/io/1394/targets/dcam1394/dcam.c @@ -0,0 +1,1262 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * dcam.c + * + * dcam1394 driver. Controls IIDC compliant devices attached through a + * IEEE-1394 bus. + */ + +#include <sys/conf.h> +#include <sys/ddi.h> +#include <sys/modctl.h> +#include <sys/sunndi.h> +#include <sys/types.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/file.h> +#include <sys/errno.h> +#include <sys/open.h> +#include <sys/cred.h> +#include <sys/mkdev.h> +#include <sys/kmem.h> +#include <sys/stat.h> +#include <sys/cmn_err.h> +#include <sys/stream.h> +#include <sys/buf.h> +#include <sys/uio.h> +#include <sys/devops.h> +#include <sys/1394/t1394.h> +#include <sys/tnf_probe.h> + +#include <sys/dcam/dcam1394_io.h> +#include <sys/1394/targets/dcam1394/dcam.h> +#include <sys/1394/targets/dcam1394/dcam_reg.h> +#include <sys/1394/targets/dcam1394/dcam_param.h> +#include <sys/1394/targets/dcam1394/dcam_frame.h> + +#ifndef NPROBE +extern int tnf_mod_load(void); +extern int tnf_mod_unload(struct modlinkage *mlp); +#endif /* ! NPROBE */ + + +/* for power management (we have only one component) */ +static char *dcam_pmc[] = { + "NAME=dcam1394", + "0=Off", + "1=On" +}; + +int g_vid_mode_frame_num_bytes[] = +{ + 57600, /* vid mode 0 */ + 153600, /* vid mode 1 */ + 460800, /* vid mode 2 */ + 614400, /* vid mode 3 */ + 921600, /* vid mode 4 */ + 307200 /* vid mode 5 */ +}; + +static int byte_copy_to_user_buff(uchar_t *src_addr_p, struct uio *uio_p, + size_t num_bytes, int start_index, int *end_index); +static int byte_copy_from_user_buff(uchar_t *dst_addr_p, struct uio *uio_p, + size_t num_bytes, int start_index, int *end_index); +static int dcam_reset(dcam_state_t *softc_p); + +/* opaque state structure head */ +void *dcam_state_p; + +static struct cb_ops dcam_cb_ops = { + dcam_open, /* open */ + dcam_close, /* close */ + nodev, /* strategy */ + nodev, /* print */ + nodev, /* dump */ + dcam_read, /* read */ + nodev, /* write */ + dcam_ioctl, /* ioctl */ + nodev, /* devmap */ + nodev, /* mmap */ + nodev, /* segmap */ + dcam_chpoll, /* chpoll */ + ddi_prop_op, /* prop_op */ + NULL, /* streams */ + /* flags */ + D_NEW | D_MP | D_64BIT | D_HOTPLUG, + CB_REV, /* rev */ + nodev, /* aread */ + nodev /* awrite */ +}; + +static struct dev_ops dcam_dev_ops = { + DEVO_REV, /* DEVO_REV indicated by manual */ + 0, /* device reference count */ + dcam_getinfo, /* getinfo */ + nulldev, /* identify */ + nulldev, /* probe */ + dcam_attach, /* attach */ + dcam_detach, /* detach */ + nodev, /* reset */ + &dcam_cb_ops, /* ptr to cb_ops struct */ + NULL, /* ptr to bus_ops struct; none */ + dcam_power, /* power */ +}; + +extern struct mod_ops mod_driverops; + +static struct modldrv modldrv = { + &mod_driverops, + "SUNW 1394-based Digital Camera driver 1.0", + &dcam_dev_ops, +}; + +static struct modlinkage modlinkage = { + MODREV_1, + (void *)&modldrv, + NULL, +}; + + +int +_init(void) +{ + int err; + + err = ddi_soft_state_init(&dcam_state_p, sizeof (dcam_state_t), 2); + + if (err) { + return (err); + } + +#ifndef NPROBE + (void) tnf_mod_load(); +#endif /* ! NPROBE */ + + if (err = mod_install(&modlinkage)) { + +#ifndef NPROBE + (void) tnf_mod_unload(&modlinkage); +#endif /* ! NPROBE */ + + ddi_soft_state_fini(&dcam_state_p); + + } + + return (err); +} + + +int +_info(struct modinfo *modinfop) +{ + int err; + + err = mod_info(&modlinkage, modinfop); + return (err); +} + + +int +_fini(void) +{ + int err; + + if ((err = mod_remove(&modlinkage)) != 0) { + return (err); + } + +#ifndef NPROBE + (void) tnf_mod_unload(&modlinkage); +#endif /* ! NPROBE */ + + ddi_soft_state_fini(&dcam_state_p); + + return (err); +} + + +/* + * dcam_attach + */ +/* ARGSUSED */ +int +dcam_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + char tmp_str[MAX_STR_LEN]; + dcam_state_t *softc_p; + ddi_eventcookie_t ev_cookie; + int instance; + int ret_val; + + switch (cmd) { + + case DDI_ATTACH: + instance = ddi_get_instance(dip); + + if (ddi_soft_state_zalloc(dcam_state_p, instance) != + DDI_SUCCESS) { + return (DDI_FAILURE); + } + + if ((softc_p = ddi_get_soft_state(dcam_state_p, instance)) == + NULL) { + ddi_soft_state_free(dcam_state_p, instance); + return (DDI_FAILURE); + } + + /* + * Initialize soft state + */ + softc_p->dip = dip; + softc_p->instance = instance; + softc_p->usr_model = -1; + softc_p->ixlp = NULL; + + softc_p->seq_count = 0; + softc_p->param_status = 0; + + /* + * set default vid_mode, frame_rate and ring_buff_capacity + */ + softc_p->cur_vid_mode = 1; + softc_p->cur_frame_rate = 3; + softc_p->cur_ring_buff_capacity = 10; + softc_p->camera_online = 1; + + (void) sprintf(tmp_str, "dcam%d", instance); + + if (ddi_create_minor_node(dip, tmp_str, S_IFCHR, instance, + DDI_PSEUDO, 0) != DDI_SUCCESS) { + ddi_soft_state_free(dcam_state_p, instance); + + return (DDI_FAILURE); + } + + (void) sprintf(tmp_str, "dcamctl%d", instance); + + if (ddi_create_minor_node(dip, tmp_str, S_IFCHR, + instance + DCAM1394_MINOR_CTRL, "ddi_dcam1394", 0) != + DDI_SUCCESS) { + ddi_soft_state_free(dcam_state_p, instance); + + return (DDI_FAILURE); + } + + if (t1394_attach(dip, T1394_VERSION_V1, 0, + &(softc_p->attachinfo), + &(softc_p->sl_handle)) != DDI_SUCCESS) { + ddi_soft_state_free(dcam_state_p, instance); + ddi_remove_minor_node(dip, NULL); + + return (DDI_FAILURE); + } + + if (t1394_get_targetinfo(softc_p->sl_handle, + softc_p->attachinfo.localinfo.bus_generation, 0, + &(softc_p->targetinfo)) != DDI_SUCCESS) { + cmn_err(CE_WARN, + "dcam_attach: t1394_get_targetinfo failed\n"); + } + + if (ddi_get_eventcookie(dip, DDI_DEVI_BUS_RESET_EVENT, + &ev_cookie) != DDI_SUCCESS) { + (void) t1394_detach(&softc_p->sl_handle, 0); + + ddi_soft_state_free(dcam_state_p, instance); + ddi_remove_minor_node(dip, NULL); + + return (DDI_FAILURE); + } + + if (ddi_add_event_handler(dip, ev_cookie, dcam_bus_reset_notify, + softc_p, &softc_p->event_id) != DDI_SUCCESS) { + (void) t1394_detach(&softc_p->sl_handle, 0); + + ddi_soft_state_free(dcam_state_p, instance); + ddi_remove_minor_node(dip, NULL); + + return (DDI_FAILURE); + } + + mutex_init(&softc_p->softc_mutex, NULL, MUTEX_DRIVER, + softc_p->attachinfo.iblock_cookie); + + mutex_init(&softc_p->dcam_frame_is_done_mutex, NULL, + MUTEX_DRIVER, softc_p->attachinfo.iblock_cookie); + + /* + * init the soft state's parameter attribute structure + */ + if (param_attr_init(softc_p, softc_p->param_attr) != + DDI_SUCCESS) { + (void) ddi_remove_event_handler(softc_p->event_id); + (void) t1394_detach(&softc_p->sl_handle, 0); + + ddi_soft_state_free(dcam_state_p, instance); + ddi_remove_minor_node(dip, NULL); + + return (DDI_FAILURE); + } + + /* + * power management stuff + */ + if (ddi_prop_update_string_array(DDI_DEV_T_NONE, + dip, "pm-components", dcam_pmc, + sizeof (dcam_pmc)/sizeof (char *)) == DDI_PROP_SUCCESS) { + + (void) pm_raise_power(dip, 0, 1); + if (ddi_prop_exists(DDI_DEV_T_ANY, dip, 0, + "power-managed?")) { + (void) pm_idle_component(dip, 0); + } else { + (void) pm_busy_component(dip, 0); + } + } + + softc_p->flags |= DCAM1394_FLAG_ATTACH_COMPLETE; + + ddi_report_dev(dip); + ret_val = DDI_SUCCESS; + break; + + case DDI_RESUME: + instance = ddi_get_instance(dip); + if ((softc_p = ddi_get_soft_state(dcam_state_p, instance)) == + NULL) { + ddi_soft_state_free(dcam_state_p, instance); + return (DDI_FAILURE); + } + + mutex_enter(&softc_p->softc_mutex); + + if (softc_p->flags & DCAM1394_FLAG_FRAME_RCV_INIT) { + (void) dcam1394_ioctl_frame_rcv_start(softc_p); + } + + softc_p->suspended = 0; + + mutex_exit(&softc_p->softc_mutex); + + ret_val = DDI_SUCCESS; + break; + + default: + ret_val = DDI_FAILURE; + break; + } + + return (ret_val); +} + + +/* + * dcam_power: perform dcam power management + */ +/* ARGSUSED */ +int +dcam_power(dev_info_t *dip, int component, int level) +{ + dcam_state_t *softc_p; + int instance; + + instance = ddi_get_instance(dip); + softc_p = (dcam_state_t *)ddi_get_soft_state(dcam_state_p, instance); + + if (softc_p == NULL) + return (DDI_FAILURE); + + softc_p->pm_cable_power = level; + + return (DDI_SUCCESS); + +} + + +/* + * dcam_getinfo + */ +/* ARGSUSED */ +int +dcam_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) +{ + dev_t dev; + dcam_state_t *softc_p; + int status; + int instance; + + switch (cmd) { + + case DDI_INFO_DEVT2DEVINFO: + dev = (dev_t)arg; + instance = DEV_TO_INSTANCE(dev); + softc_p = (dcam_state_t *) + ddi_get_soft_state(dcam_state_p, instance); + + if (softc_p == NULL) { + return (DDI_FAILURE); + } + + *result = (void *)softc_p->dip; + status = DDI_SUCCESS; + break; + + case DDI_INFO_DEVT2INSTANCE: + dev = (dev_t)arg; + instance = DEV_TO_INSTANCE(dev); + *result = (void *)(uintptr_t)instance; + status = DDI_SUCCESS; + break; + + default: + status = DDI_FAILURE; + } + + return (status); +} + + +/* + * dcam_detach + */ +/* ARGSUSED */ +int +dcam_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + int instance; + dcam_state_t *softc_p; + + instance = ddi_get_instance(dip); + + softc_p = (dcam_state_t *)ddi_get_soft_state(dcam_state_p, instance); + if (softc_p == NULL) { + return (DDI_FAILURE); + } + + + switch (cmd) { + + case DDI_SUSPEND: + mutex_enter(&softc_p->softc_mutex); + + softc_p->suspended = 1; + + if (softc_p->flags & DCAM1394_FLAG_FRAME_RCV_INIT) { + (void) dcam_frame_rcv_stop(softc_p); + } + + mutex_exit(&softc_p->softc_mutex); + return (DDI_SUCCESS); + + + case DDI_DETACH: + /* + * power management stuff + */ + (void) pm_lower_power(dip, 0, 0); + (void) ddi_prop_remove(DDI_DEV_T_NONE, dip, "pm-components"); + + /* + * deregister with 1394 DDI framework + */ + if (t1394_detach(&softc_p->sl_handle, 0) != DDI_SUCCESS) { + return (DDI_FAILURE); + } + + (void) ddi_remove_event_handler(softc_p->event_id); + + /* + * free state structures, mutexes, condvars; + * deregister interrupts + */ + mutex_destroy(&softc_p->softc_mutex); + mutex_destroy(&softc_p->dcam_frame_is_done_mutex); + + /* + * Remove all minor nodes, all dev_t's properties + */ + ddi_remove_minor_node(dip, NULL); + + ddi_soft_state_free(dcam_state_p, instance); + ddi_prop_remove_all(dip); + + return (DDI_SUCCESS); + + default: + return (DDI_FAILURE); + + } +} + + +/* + * dcam_open + */ +/* ARGSUSED */ +int +dcam_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p) +{ + dcam_state_t *softc_p; + int instance; + int is_ctrl_file; + uint_t new_flags; + + instance = (int)DEV_TO_INSTANCE(*dev_p); + + if ((softc_p = ddi_get_soft_state(dcam_state_p, instance)) == NULL) { + return (ENXIO); + } + + /* + * if dcam_attach hasn't completed, return error + * XXX: Check this out + */ + if (!(softc_p->flags & DCAM1394_FLAG_ATTACH_COMPLETE)) { + return (ENXIO); + } + + /* disallow block, mount, and layered opens */ + if (otyp != OTYP_CHR) { + return (EINVAL); + } + + new_flags = 0; + is_ctrl_file = (getminor(*dev_p) & DCAM1394_MINOR_CTRL) ? 1 : 0; + + mutex_enter(&softc_p->softc_mutex); + + /* + * The open is either for the capture file or the control file. + * If it's the control file construct new flags. + * + * If it's the capture file return busy if it's already open, + * otherwise construct new flags. + */ + if (is_ctrl_file) { + new_flags |= DCAM1394_FLAG_OPEN_CONTROL; + } else { + if (softc_p->flags & DCAM1394_FLAG_OPEN_CAPTURE) { + mutex_exit(&softc_p->softc_mutex); + return (EBUSY); + } + + new_flags |= DCAM1394_FLAG_OPEN_CAPTURE; + } + + new_flags |= DCAM1394_FLAG_OPEN; + softc_p->flags |= new_flags; + + mutex_exit(&softc_p->softc_mutex); + + /* + * power management stuff + */ + if (softc_p->pm_open_count == 0) { + if (ddi_prop_exists(DDI_DEV_T_ANY, softc_p->dip, 0, + "power-managed?")) { + (void) pm_busy_component(softc_p->dip, 0); + if (softc_p->pm_cable_power == 0) { + int i; + + (void) pm_raise_power(softc_p->dip, 0, 1); + + /* + * Wait for the power to be up and stable + * before proceeding. 100 msecs should + * certainly be enough, and if we check + * every msec we'll probably loop just a + * few times. + */ + for (i = 0; i < 100; i++) { + if (param_power_set(softc_p, 1) == 0) { + break; + } + delay((clock_t)drv_usectohz(1000)); + } + } + } + } + softc_p->pm_open_count++; + + return (0); +} + + +/* + * dcam_close + */ +/* ARGSUSED */ +int +dcam_close(dev_t dev, int flags, int otyp, cred_t *cred_p) +{ + int instance; + dcam_state_t *softc; + + instance = DEV_TO_INSTANCE(dev); + softc = (dcam_state_t *)ddi_get_soft_state(dcam_state_p, instance); + + /* + * power management stuff + */ + softc->pm_open_count = 0; + if (ddi_prop_exists(DDI_DEV_T_ANY, softc->dip, 0, "power-managed?")) { + (void) pm_idle_component(softc->dip, 0); + } + + mutex_enter(&softc->softc_mutex); + + if (getminor(dev) & DCAM1394_MINOR_CTRL) { + softc->flags &= ~DCAM1394_FLAG_OPEN_CONTROL; + } else { + /* + * If an application which has opened the camera capture + * device exits without calling DCAM1394_CMD_FRAME_RCV_STOP + * ioctl, then we need to release resources. + */ + if (softc->flags & DCAM1394_FLAG_FRAME_RCV_INIT) { + (void) dcam_frame_rcv_stop(softc); + softc->flags &= ~DCAM1394_FLAG_FRAME_RCV_INIT; + } + + (void) param_power_set(softc, 0); + + softc->flags &= ~DCAM1394_FLAG_OPEN_CAPTURE; + } + + /* + * If driver is completely closed, then stabilize the camera + * and turn off transient flags + */ + if (!(softc->flags & + (DCAM1394_FLAG_OPEN_CONTROL | DCAM1394_FLAG_OPEN_CAPTURE))) { + softc->flags &= DCAM1394_FLAG_ATTACH_COMPLETE; + } + + mutex_exit(&softc->softc_mutex); + + return (DDI_SUCCESS); + +} + + +/* + * dcam_read + * + * If read pointer is not pointing to the same position as write pointer + * copy frame data from ring buffer position pointed to by read pointer. + * + * If during the course of copying frame data, the device driver + * invalidated this read() request processing operation, restart + * this operation. + * + * Increment read pointer and return frame data to user process. + * + * Else return error + * + */ +/* ARGSUSED */ +int +dcam_read(dev_t dev, struct uio *uio_p, cred_t *cred_p) +{ + buff_info_t *buff_info_p; + dcam_state_t *softc_p; + hrtime_t timestamp; + int index, instance; + int read_ptr_id; + size_t read_ptr_pos, write_ptr_pos; + int read_req_invalid; + ring_buff_t *ring_buff_p; + uchar_t *frame_data_p; + uint_t seq_num; + unsigned long user_frame_buff_addr; + uint_t vid_mode; + int gotten_addr_flag; + + instance = DEV_TO_INSTANCE(dev); + + softc_p = (dcam_state_t *)ddi_get_soft_state(dcam_state_p, instance); + if (softc_p == NULL) { + return (ENXIO); + } + + if ((ring_buff_p = softc_p->ring_buff_p) == NULL) { + return (EAGAIN); + } + + read_ptr_id = 0; + + mutex_enter(&softc_p->dcam_frame_is_done_mutex); + + softc_p->reader_flags[read_ptr_id] |= DCAM1394_FLAG_READ_REQ_PROC; + + user_frame_buff_addr = 0; + gotten_addr_flag = 0; + + do { + read_ptr_pos = ring_buff_read_ptr_pos_get(ring_buff_p, + read_ptr_id); + + write_ptr_pos = ring_buff_write_ptr_pos_get(ring_buff_p); + + if (read_ptr_pos != write_ptr_pos) { + /* + * Since the app wants realtime video, set the read + * pointer to the newest data. + */ + if (write_ptr_pos == 0) { + read_ptr_pos = ring_buff_p->num_buffs - 1; + } else { + read_ptr_pos = write_ptr_pos - 1; + } + + /* + * copy frame data from ring buffer position pointed + * to by read pointer + */ + index = 0; + buff_info_p = + &(ring_buff_p->buff_info_array_p[read_ptr_pos]); + + vid_mode = softc_p->cur_vid_mode; + seq_num = buff_info_p->seq_num; + timestamp = buff_info_p->timestamp; + frame_data_p = (uchar_t *)buff_info_p->kaddr_p; + + mutex_exit(&softc_p->dcam_frame_is_done_mutex); + + /* + * Fix for bug #4424042 + * don't lock this section + */ + + if (byte_copy_to_user_buff((uchar_t *)&vid_mode, + uio_p, sizeof (uint_t), index, &index)) { + + return (EFAULT); + } + + if (byte_copy_to_user_buff((uchar_t *)&seq_num, + uio_p, sizeof (unsigned int), index, &index)) { + + return (EFAULT); + } + + if (byte_copy_to_user_buff((uchar_t *)×tamp, + uio_p, sizeof (hrtime_t), index, &index)) { + + return (EFAULT); + } + + /* + * get buff pointer; do ddi_copyout() + * get user buffer address only once + */ + if (!gotten_addr_flag) { + if (byte_copy_from_user_buff( + (uchar_t *)&user_frame_buff_addr, uio_p, + softc_p->usr_model, index, &index)) { + + return (EFAULT); + } + +#ifdef _MULTI_DATAMODEL + if (softc_p->usr_model == ILP32_PTR_SIZE) { + user_frame_buff_addr = + ((user_frame_buff_addr >> 32) & + 0xffffffffULL) | + ((user_frame_buff_addr << 32) & + 0xffffffff00000000ULL); + } +#endif /* _MULTI_DATAMODEL */ + + gotten_addr_flag = 1; + } + + if (ddi_copyout( + (caddr_t)frame_data_p, + (caddr_t)user_frame_buff_addr, + g_vid_mode_frame_num_bytes[softc_p->cur_vid_mode], + 0)) { + return (EFAULT); + } + + /* + * if during the course of copying frame data, + * the device driver invalidated this read() + * request processing operation; restart this + * operation + */ + + mutex_enter(&softc_p->dcam_frame_is_done_mutex); + + read_req_invalid = softc_p->reader_flags[read_ptr_id] & + DCAM1394_FLAG_READ_REQ_INVALID; + + softc_p->reader_flags[read_ptr_id] &= + ~(DCAM1394_FLAG_READ_REQ_INVALID); + + mutex_exit(&softc_p->dcam_frame_is_done_mutex); + + } else { + mutex_exit(&softc_p->dcam_frame_is_done_mutex); + return (EAGAIN); + } + + mutex_enter(&softc_p->dcam_frame_is_done_mutex); + } while (read_req_invalid); + + /* + * return number of bytes actually written to user space + */ + uio_p->uio_resid -= g_vid_mode_frame_num_bytes[softc_p->cur_vid_mode]; + + softc_p->reader_flags[read_ptr_id] &= ~(DCAM1394_FLAG_READ_REQ_PROC); + + /* increment read pointer */ + ring_buff_read_ptr_incr(ring_buff_p, read_ptr_id); + + mutex_exit(&softc_p->dcam_frame_is_done_mutex); + + return (0); +} + + +/* + * dcam_ioctl + */ +/* ARGSUSED */ +int +dcam_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p, + int *rvalp) +{ + dcam_state_t *softc_p; + dcam1394_param_list_t *param_list; + dcam1394_reg_io_t dcam_reg_io; + int instance, is_ctrl_file, rc, i; + + rc = 0; + param_list = (dcam1394_param_list_t *)0; + + instance = DEV_TO_INSTANCE(dev); + + if ((softc_p = ddi_get_soft_state(dcam_state_p, instance)) == NULL) { + rc = ENXIO; + goto done; + } + + /* + * determine user applications data model + */ + if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) + softc_p->usr_model = ILP32_PTR_SIZE; + else + softc_p->usr_model = LP64_PTR_SIZE; + + + switch (cmd) { + + case DCAM1394_CMD_REG_READ: + if (ddi_copyin((caddr_t)arg, &dcam_reg_io, + sizeof (dcam1394_reg_io_t), mode)) { + rc = EFAULT; + goto done; + } + + if (dcam_reg_read(softc_p, &dcam_reg_io)) { + rc = EFAULT; + goto done; + } + + if (ddi_copyout(&dcam_reg_io, (caddr_t)arg, + sizeof (dcam1394_reg_io_t), mode)) { + rc = EFAULT; + goto done; + } + break; + + case DCAM1394_CMD_REG_WRITE: + if (ddi_copyin((caddr_t)arg, &dcam_reg_io, + sizeof (dcam1394_reg_io_t), mode)) { + rc = EFAULT; + goto done; + } + + if (dcam_reg_write(softc_p, &dcam_reg_io)) { + rc = EFAULT; + goto done; + } + + if (ddi_copyout(&dcam_reg_io, (caddr_t)arg, + sizeof (dcam1394_reg_io_t), mode)) { + rc = EFAULT; + goto done; + } + break; + + case DCAM1394_CMD_CAM_RESET: + if (dcam_reset(softc_p)) { + rc = EIO; + goto done; + } + break; + + case DCAM1394_CMD_PARAM_GET: + param_list = (dcam1394_param_list_t *) + kmem_alloc(sizeof (dcam1394_param_list_t), KM_SLEEP); + + if (ddi_copyin((caddr_t)arg, (caddr_t)param_list, + sizeof (dcam1394_param_list_t), mode)) { + rc = EFAULT; + goto done; + } + + if (dcam1394_ioctl_param_get(softc_p, *param_list)) { + rc = EINVAL; + } + + if (ddi_copyout((caddr_t)param_list, (caddr_t)arg, + sizeof (dcam1394_param_list_t), mode)) { + rc = EFAULT; + goto done; + } + break; + + case DCAM1394_CMD_PARAM_SET: + param_list = (dcam1394_param_list_t *) + kmem_alloc((size_t)sizeof (dcam1394_param_list_t), + KM_SLEEP); + + if (ddi_copyin((caddr_t)arg, (caddr_t)param_list, + sizeof (dcam1394_param_list_t), mode)) { + rc = EFAULT; + goto done; + } + + is_ctrl_file = (getminor(dev) & DCAM1394_MINOR_CTRL) ? 1:0; + + if (dcam1394_ioctl_param_set(softc_p, is_ctrl_file, + *param_list)) { + rc = EINVAL; + } + + if (is_ctrl_file) { + mutex_enter(&softc_p->dcam_frame_is_done_mutex); + softc_p->param_status |= DCAM1394_STATUS_PARAM_CHANGE; + mutex_exit(&softc_p->dcam_frame_is_done_mutex); + } + + if (ddi_copyout(param_list, (caddr_t)arg, + sizeof (dcam1394_param_list_t), mode)) { + rc = EFAULT; + goto done; + } + break; + + case DCAM1394_CMD_FRAME_RCV_START: + if (dcam1394_ioctl_frame_rcv_start(softc_p)) { + rc = ENXIO; + } + break; + + case DCAM1394_CMD_FRAME_RCV_STOP: + if (dcam_frame_rcv_stop(softc_p)) { + rc = ENXIO; + } + break; + + case DCAM1394_CMD_RING_BUFF_FLUSH: + if (softc_p->ring_buff_p == NULL) { + rc = EAGAIN; + break; + } + + /* + * the simplest way to flush ring_buff is to empty it + */ + for (i = 0; i < softc_p->ring_buff_p->num_read_ptrs; i++) { + softc_p->ring_buff_p->read_ptr_pos[i] = + softc_p->ring_buff_p->write_ptr_pos; + + /* + * if device driver is processing a user + * process's read() request + */ + if (softc_p->reader_flags[i] & + DCAM1394_FLAG_READ_REQ_PROC) { + + /* + * invalidate the read() request processing + * operation + */ + softc_p->reader_flags[i] |= + DCAM1394_FLAG_READ_REQ_INVALID; + } + } + break; + + case DCAM1394_CMD_FRAME_SEQ_NUM_COUNT_RESET: + mutex_enter(&softc_p->dcam_frame_is_done_mutex); + softc_p->seq_count = 0; + mutex_exit(&softc_p->dcam_frame_is_done_mutex); + break; + + default: + rc = EIO; + break; + + } + +done: + if (param_list) + kmem_free(param_list, sizeof (dcam1394_param_list_t)); + + return (rc); +} + + +/* + * dcam_chpoll + */ +/* ARGSUSED */ +int +dcam_chpoll(dev_t dev, short events, int anyyet, short *reventsp, + struct pollhead **phpp) +{ + dcam_state_t *softc_p; + int instance, ring_buff_has_data, read_ptr_id; + size_t read_ptr_pos, write_ptr_pos; + short revent; + + instance = DEV_TO_INSTANCE(dev); + + softc_p = (dcam_state_t *)ddi_get_soft_state(dcam_state_p, instance); + if (softc_p == NULL) { + return (ENXIO); + } + + read_ptr_id = 0; + revent = 0; + + if (softc_p->ring_buff_p == NULL) { + ring_buff_has_data = 0; + } else { + mutex_enter(&softc_p->dcam_frame_is_done_mutex); + + read_ptr_pos = + ring_buff_read_ptr_pos_get(softc_p->ring_buff_p, + read_ptr_id); + + write_ptr_pos = + ring_buff_write_ptr_pos_get(softc_p->ring_buff_p); + + if (read_ptr_pos != write_ptr_pos) { + ring_buff_has_data = 1; + } else { + ring_buff_has_data = 0; + } + + mutex_exit(&softc_p->dcam_frame_is_done_mutex); + } + + /* + * now check for events + */ + if ((events & POLLRDNORM) && ring_buff_has_data) { + revent |= POLLRDNORM; + } + + if ((events & POLLPRI) && softc_p->param_status) { + revent |= POLLPRI; + } + + /* if no events have occurred */ + if (revent == 0) { + if (!anyyet) { + *phpp = &softc_p->dcam_pollhead; + } + } + + *reventsp = revent; + + return (0); +} + + +/* + * dcam_bus_reset_notify + */ +/* ARGSUSED */ +void +dcam_bus_reset_notify(dev_info_t *dip, ddi_eventcookie_t ev_cookie, void *arg, + void *impl_data) +{ + + dcam_state_t *softc_p; + t1394_localinfo_t *localinfo = impl_data; + t1394_targetinfo_t targetinfo; + + softc_p = arg; + + /* + * this is needed to handle LG camera "changing GUID" bug + * XXX: What's this about? + */ + if ((dip == NULL) || (arg == NULL) || (impl_data == NULL) || + (softc_p->sl_handle == NULL)) { + return; + } + + localinfo = impl_data; + + /* + * simply return if no target info + */ + if (t1394_get_targetinfo(softc_p->sl_handle, + localinfo->bus_generation, 0, &targetinfo) != DDI_SUCCESS) + return; + + if (localinfo->local_nodeID == softc_p->targetinfo.target_nodeID) { + softc_p->param_status |= DCAM1394_STATUS_CAM_UNPLUG; + } else { + softc_p->param_status &= ~DCAM1394_STATUS_CAM_UNPLUG; + } + + /* struct copies */ + softc_p->attachinfo.localinfo = *localinfo; + + if (targetinfo.target_nodeID != T1394_INVALID_NODEID) { + softc_p->targetinfo.current_max_payload = + targetinfo.current_max_payload; + + softc_p->targetinfo.current_max_speed = + targetinfo.current_max_speed; + + softc_p->targetinfo.target_nodeID = + targetinfo.target_nodeID; + } +} + + +/* + * byte_copy_to_user_buff + */ +static int +byte_copy_to_user_buff(uchar_t *src_addr_p, struct uio *uio_p, size_t num_bytes, + int start_index, int *end_index_p) +{ + int index; + size_t len; + uchar_t *u8_p; + + index = start_index; + u8_p = (uchar_t *)src_addr_p; + + while (num_bytes) { + + len = num_bytes; + + if (uiomove(u8_p, len, UIO_READ, uio_p)) { + return (-1); + } + + index++; + u8_p += len; + num_bytes -= len; + } + + *end_index_p = index; + + return (0); +} + + +/* + * byte_copy_from_user_buff + */ +static int +byte_copy_from_user_buff(uchar_t *dst_addr_p, struct uio *uio_p, + size_t num_bytes, int start_index, int *end_index_p) +{ + int index; + size_t len; + uchar_t *u8_p; + + index = start_index; + u8_p = (uchar_t *)dst_addr_p; + + while (num_bytes) { + len = num_bytes; + + if (uiomove(u8_p, len, UIO_WRITE, uio_p)) { + return (-1); + + } + + index++; + u8_p += len; + num_bytes -= len; + + } + + *end_index_p = index; + + return (0); +} + + +/* + * dcam_reset() + */ +static int +dcam_reset(dcam_state_t *softc_p) +{ + dcam1394_reg_io_t dcam_reg_io; + + dcam_reg_io.offs = DCAM1394_REG_OFFS_INITIALIZE; + dcam_reg_io.val = DCAM1394_REG_VAL_INITIALIZE_ASSERT; + + if (dcam_reg_write(softc_p, &dcam_reg_io)) { + return (-1); + } + + /* + * If the camera has a TI VSP, tweak the iris feature + * to "on" and value 4. + */ + dcam_reg_io.offs = DCAM1394_REG_OFFS_FEATURE_CSR_BASE + + DCAM1394_REG_OFFS_IRIS_CSR; + dcam_reg_io.val = 0x82000004; + + if (dcam_reg_write(softc_p, &dcam_reg_io)) { + return (-1); + } + + return (0); +} diff --git a/usr/src/uts/common/io/1394/targets/dcam1394/dcam_frame.c b/usr/src/uts/common/io/1394/targets/dcam1394/dcam_frame.c new file mode 100644 index 0000000000..d7bf12aa0f --- /dev/null +++ b/usr/src/uts/common/io/1394/targets/dcam1394/dcam_frame.c @@ -0,0 +1,644 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * dcam_frame.c + * + * dcam1394 driver. Support for video frame access. + */ + +#include <sys/int_limits.h> +#include <sys/types.h> +#include <sys/kmem.h> +#include <sys/cmn_err.h> +#include <sys/1394/targets/dcam1394/dcam.h> +#include <sys/1394/targets/dcam1394/dcam_frame.h> +#include <sys/tnf_probe.h> +#include <sys/dcam/dcam1394_io.h> + +#include <sys/1394/targets/dcam1394/dcam_reg.h> + +static void dcam_free_resources(dcam_state_t *); + +typedef struct dcam_mode_info_s { + int bytes_per_pkt; + int pkts_per_frame; +} dcam_mode_info_t; + +/* + * packets per frame + * + * 30fps + * mode_0 1/2h, 60q, 240b + * mode_1 1h, 160q, 640 + * mode_2 2h, 480q, 1920 + * mode_3 2h, 640q, 2560 + * mode_4 2h, 960q, 3840 + * mode_5 2h, 320q, 1280 + * + * 15fps + * mode_0 1/4h, 30q, 120 + * mode_1 1/2h, 80q, 320 + * mode_2 1h, 240q, 960 + * mode_3 1h, 320q, 1280 + * mode_4 1h, 480q, 1920 + * mode_5 1h, 160q, 640 + * + * 7.5fps + * mode_0 1/8h, 15q, 60 + * mode_1 1/4h, 40q, 160 + * mode_2 1/2h, 120q, 480 + * mode_3 1/2h, 160q, 640 + * mode_4 1/2h, 240q, 960 + * mode_5 1/2h, 80q, 320 + * + * 3.75fps + * mode_0 x + * mode_1 1/8h, 20q, 80 + * mode_2 1/4h, 60q, 240 + * mode_3 1/4h, 80q, 320 + * mode_4 1/4h, 120q, 480 + * mode_5 1/4h, 40q, 160 + * + * 60fps + * mode_5 4H, 640q, 2560 + * + */ + +/* indexed by vid mode, frame rate */ +static int g_bytes_per_packet[6][5] = { + + /* fps: 3.75 7.5 15 30 60 */ + /* vid mode 0 */ -1, 60, 120, 240, -1, + /* vid mode 1 */ 80, 160, 320, 640, -1, + /* vid mode 2 */ 240, 480, 960, 1920, -1, + /* vid mode 3 */ 320, 640, 1280, 2560, -1, + /* vid mode 4 */ 480, 960, 1920, 3840, -1, + /* vid mode 5 */ 160, 320, 640, 1280, 2560 +}; + +/* indexed by vid mode */ +static int g_bytes_per_frame[6] = { + 57600, + 153600, + 460800, + 614400, + 921600, + 307200 +}; + + +static +void dcam_rsrc_fail(t1394_isoch_single_handle_t t1394_single_hdl, + opaque_t single_evt_arg, t1394_isoch_rsrc_error_t fail_args); + +/* + * dcam1394_ioctl_frame_rcv_start + */ +int +dcam1394_ioctl_frame_rcv_start(dcam_state_t *softc_p) +{ + if (!(softc_p->flags & DCAM1394_FLAG_FRAME_RCV_INIT)) { + + if (dcam_frame_rcv_init(softc_p, softc_p->cur_vid_mode, + softc_p->cur_frame_rate, softc_p->cur_ring_buff_capacity)) { + + dcam_free_resources(softc_p); + return (1); + } + + softc_p->flags |= DCAM1394_FLAG_FRAME_RCV_INIT; + } + + if (dcam_frame_rcv_start(softc_p)) { + return (1); + } + + return (0); +} + + +/* + * dcam_frame_rcv_init + */ +int +dcam_frame_rcv_init(dcam_state_t *softc_p, int vid_mode, int frame_rate, + int ring_buff_capacity) +{ + int16_t bytes_per_pkt; /* # pkt bytes + overhead */ + int bytes_per_frame; + size_t frame; + int cookie; + int failure; + id1394_isoch_dmainfo_t isoch_args; /* for alloc isoch call */ + ixl1394_command_t *last_ixlp; /* last ixl in chain, */ + /* used for appending ixls */ + ixl1394_command_t *new_ixl_cmdp; /* new ixl command */ + ixl1394_set_syncwait_t *new_ixl_sswp; /* new ixl set syncwait */ + ixl1394_xfer_pkt_t *new_ixl_xfpp; /* new ixl xfer packet */ + ixl1394_xfer_buf_t *new_ixl_xfbp; /* new ixl xfer buffer */ + ixl1394_callback_t *new_ixl_cbp; /* new ixl callback */ + ixl1394_jump_t *new_ixl_jmpp; /* new ixl jump */ + int32_t result; /* errno from alloc_isoch_dma */ + buff_info_t *buff_info_p; + dcam1394_reg_io_t reg_io; + uint_t data; + size_t num_bytes, num_bytes_left; + size_t num_xfer_cmds, xfer_cmd; + size_t max_ixl_buff_size; + caddr_t ixl_buff_kaddr, ixl_buff_vaddr; + + bytes_per_pkt = g_bytes_per_packet[vid_mode][frame_rate]; + if (bytes_per_pkt == -1) { + return (1); + } + + bytes_per_frame = g_bytes_per_frame[vid_mode]; + + if ((softc_p->ring_buff_p = ring_buff_create(softc_p, + (size_t)ring_buff_capacity, (size_t)bytes_per_frame)) == NULL) { + return (1); + } + + softc_p->ring_buff_p->read_ptr_pos[0] = 0; + + /* allocate isoch channel */ + softc_p->sii.si_channel_mask = 0xFFFF000000000000; + softc_p->sii.si_bandwidth = bytes_per_pkt; + softc_p->sii.rsrc_fail_target = dcam_rsrc_fail; + softc_p->sii.single_evt_arg = softc_p; + softc_p->sii.si_speed = softc_p->targetinfo.current_max_speed; + + if (t1394_alloc_isoch_single(softc_p->sl_handle, + &softc_p->sii, 0, &softc_p->sii_output_args, &softc_p->sii_hdl, + &failure) != DDI_SUCCESS) { + return (1); + } + + /* + * At this point, all buffer memory has been allocated and + * mapped, and is tracked on a linear linked list. Now need to + * build the IXL. Done on a frame-by-frame basis. Could + * theoretically have been done at the same time as the mem alloc + * above, but hey, no need to be so fancy here. + * + * ixl buff size is bound by SHRT_MAX and needs to + * be a multiple of packet size + */ + max_ixl_buff_size = (SHRT_MAX / bytes_per_pkt) * bytes_per_pkt; + + /* for each frame build frame's ixl list */ + for (frame = 0; frame < softc_p->ring_buff_p->num_buffs; frame++) { + + buff_info_p = &(softc_p->ring_buff_p->buff_info_array_p[frame]); + + /* + * if this is the 1st frame, put a IXL label at the top so a + * loop can be created later + */ + if (frame == 0) { + new_ixl_cmdp = kmem_zalloc( + sizeof (ixl1394_label_t), KM_SLEEP); + softc_p->ixlp = new_ixl_cmdp; + + new_ixl_cmdp->ixl_opcode = IXL1394_OP_LABEL; + + last_ixlp = softc_p->ixlp; + } + + /* add wait-for-sync IXL command */ + new_ixl_sswp = kmem_zalloc( + sizeof (ixl1394_set_syncwait_t), KM_SLEEP); + + new_ixl_sswp->ixl_opcode = IXL1394_OP_SET_SYNCWAIT; + + last_ixlp->next_ixlp = (ixl1394_command_t *)new_ixl_sswp; + last_ixlp = (ixl1394_command_t *)new_ixl_sswp; + + /* add in each dma cookie */ + for (cookie = 0; cookie < buff_info_p->dma_cookie_count; + cookie++) { + + num_xfer_cmds = min(bytes_per_frame, + buff_info_p->dma_cookie.dmac_size) / + max_ixl_buff_size; + + if (min(bytes_per_frame, + buff_info_p->dma_cookie.dmac_size) % + max_ixl_buff_size) { + num_xfer_cmds++; + } + + num_bytes_left = min(bytes_per_frame, + buff_info_p->dma_cookie.dmac_size); + + ixl_buff_kaddr = (caddr_t) + buff_info_p->dma_cookie.dmac_laddress; + + ixl_buff_vaddr = buff_info_p->kaddr_p; + + for (xfer_cmd = 0; xfer_cmd < (num_xfer_cmds + 1); + xfer_cmd++) { + num_bytes = min(num_bytes_left, + max_ixl_buff_size); + + if (xfer_cmd == 0) { + new_ixl_xfpp = + kmem_zalloc( + sizeof (ixl1394_xfer_pkt_t), + KM_SLEEP); + + new_ixl_xfpp->ixl_opcode = + IXL1394_OP_RECV_PKT_ST; + + new_ixl_xfpp->ixl_buf._dmac_ll = + (uint64_t)ixl_buff_kaddr; + new_ixl_xfpp->size = + (uint16_t)bytes_per_pkt; + new_ixl_xfpp->mem_bufp = + ixl_buff_vaddr; + + last_ixlp->next_ixlp = + (ixl1394_command_t *)new_ixl_xfpp; + last_ixlp = + (ixl1394_command_t *)new_ixl_xfpp; + + num_bytes_left -= bytes_per_pkt; + ixl_buff_kaddr += bytes_per_pkt; + ixl_buff_vaddr += bytes_per_pkt; + + continue; + } + + /* allocate & init an IXL transfer command. */ + new_ixl_xfbp = + kmem_zalloc(sizeof (ixl1394_xfer_buf_t), + KM_SLEEP); + + new_ixl_xfbp->ixl_opcode = IXL1394_OP_RECV_BUF; + + new_ixl_xfbp->ixl_buf._dmac_ll = + (uint64_t)ixl_buff_kaddr; + new_ixl_xfbp->size = (uint16_t)num_bytes; + new_ixl_xfbp->pkt_size = bytes_per_pkt; + new_ixl_xfbp->mem_bufp = ixl_buff_vaddr; + + last_ixlp->next_ixlp = + (ixl1394_command_t *)new_ixl_xfbp; + last_ixlp = + (ixl1394_command_t *)new_ixl_xfbp; + + num_bytes_left -= num_bytes; + ixl_buff_kaddr += num_bytes; + ixl_buff_vaddr += num_bytes; + } + + if (cookie > 0) { + ddi_dma_nextcookie(buff_info_p->dma_handle, + &(buff_info_p->dma_cookie)); + } + + } + + /* + * at this point, have finished a frame. put in a callback + */ + new_ixl_cbp = kmem_zalloc( + sizeof (ixl1394_callback_t), KM_SLEEP); + + new_ixl_cbp->ixl_opcode = IXL1394_OP_CALLBACK; + + new_ixl_cbp->callback = &dcam_frame_is_done; + new_ixl_cbp->callback_arg = NULL; + + last_ixlp->next_ixlp = (ixl1394_command_t *)new_ixl_cbp; + last_ixlp = (ixl1394_command_t *)new_ixl_cbp; + } + + /* + * for the final touch, put an IXL jump at the end to jump to the + * label at the top + */ + new_ixl_jmpp = kmem_zalloc(sizeof (ixl1394_jump_t), KM_SLEEP); + + new_ixl_jmpp->ixl_opcode = IXL1394_OP_JUMP; + + new_ixl_jmpp->label = softc_p->ixlp; + + last_ixlp->next_ixlp = (ixl1394_command_t *)new_ixl_jmpp; + + /* don't need this, but it's neater */ + last_ixlp = (ixl1394_command_t *)new_ixl_jmpp; + + /* call fwim routine to alloc an isoch resource */ + isoch_args.ixlp = softc_p->ixlp; + isoch_args.channel_num = softc_p->sii_output_args.channel_num; + + /* other misc args. note speed doesn't matter for isoch receive */ + isoch_args.idma_options = ID1394_LISTEN_PKT_MODE; + isoch_args.default_tag = 0; + isoch_args.default_sync = 1; + isoch_args.global_callback_arg = softc_p; + + /* set the ISO channel number */ + data = (softc_p->sii_output_args.channel_num & 0xF) << 28; + + /* set the ISO speed */ + data |= (softc_p->targetinfo.current_max_speed << 24); + + reg_io.offs = DCAM1394_REG_OFFS_CUR_ISO_CHANNEL; + reg_io.val = data; + + if (dcam_reg_write(softc_p, ®_io)) { + return (1); + } + + result = 1234; + + if (t1394_alloc_isoch_dma(softc_p->sl_handle, &isoch_args, 0, + &softc_p->isoch_handle, &result) != DDI_SUCCESS) { + return (1); + } + + return (0); +} + + +/* + * dcam_frame_rcv_fini + */ +int +dcam_frame_rcv_fini(dcam_state_t *softc_p) +{ + t1394_free_isoch_dma(softc_p->sl_handle, 0, &softc_p->isoch_handle); + + softc_p->isoch_handle = NULL; + + t1394_free_isoch_single(softc_p->sl_handle, &softc_p->sii_hdl, 0); + + return (0); +} + + +/* + * dcam_frame_rcv_start + */ +int +dcam_frame_rcv_start(dcam_state_t *softc_p) +{ + id1394_isoch_dma_ctrlinfo_t idma_ctrlinfo; /* currently not used */ + int32_t result; + dcam1394_reg_io_t reg_io; + + if ((t1394_start_isoch_dma(softc_p->sl_handle, softc_p->isoch_handle, + &idma_ctrlinfo, 0, &result)) != DDI_SUCCESS) { + return (1); + } + + reg_io.offs = DCAM1394_REG_OFFS_ISO_EN; + reg_io.val = 0x80000000; + + if (dcam_reg_write(softc_p, ®_io)) { + return (1); + } + + softc_p->flags |= DCAM1394_FLAG_FRAME_RCVING; + + return (0); +} + + +/* + * dcam_frame_rcv_stop + */ +int +dcam_frame_rcv_stop(dcam_state_t *softc_p) +{ + dcam1394_reg_io_t reg_io; + + /* if resources have already been cleared, nothing to do */ + if (!(softc_p->flags & DCAM1394_FLAG_FRAME_RCV_INIT)) { + return (0); + } + + reg_io.offs = DCAM1394_REG_OFFS_ISO_EN; + reg_io.val = 0; + + (void) dcam_reg_write(softc_p, ®_io); + + t1394_stop_isoch_dma(softc_p->sl_handle, softc_p->isoch_handle, 0); + t1394_free_isoch_dma(softc_p->sl_handle, 0, &softc_p->isoch_handle); + t1394_free_isoch_single(softc_p->sl_handle, &softc_p->sii_hdl, 0); + + dcam_free_resources(softc_p); + + return (0); +} + + +void +dcam_free_resources(dcam_state_t *softc_p) +{ + ixl1394_command_t *ptr; + ixl1394_command_t *tmp; + + /* + * The following fixes a memory leak. See bug #4423667. + * The original code only released memory for the first frame. + */ + + /* free ixl opcode resources */ + ptr = softc_p->ixlp; + + while (ptr != NULL) { + tmp = ptr; + ptr = ptr->next_ixlp; + + switch (tmp->ixl_opcode) { + case IXL1394_OP_LABEL: + kmem_free(tmp, sizeof (ixl1394_label_t)); + break; + + case IXL1394_OP_SET_SYNCWAIT: + kmem_free(tmp, sizeof (ixl1394_set_syncwait_t)); + break; + + case IXL1394_OP_RECV_PKT_ST: + kmem_free(tmp, sizeof (ixl1394_xfer_pkt_t)); + break; + + case IXL1394_OP_RECV_BUF: + kmem_free(tmp, sizeof (ixl1394_xfer_buf_t)); + break; + + case IXL1394_OP_CALLBACK: + kmem_free(tmp, sizeof (ixl1394_callback_t)); + break; + + case IXL1394_OP_JUMP: + kmem_free(tmp, sizeof (ixl1394_jump_t)); + break; + } + } + + /* + * free ring buff and indicate that the resources have been cleared + */ + ring_buff_free(softc_p, softc_p->ring_buff_p); + + softc_p->flags &= ~DCAM1394_FLAG_FRAME_RCV_INIT; + softc_p->ixlp = NULL; +} + + +/* + * dcam_frame_is_done + * + * This routine is called after DMA engine has stored a single received + * frame in ring buffer position pointed to by write pointer; this + * routine marks the frame's vid mode, timestamp, and sequence number + * + * Store received frame in ring buffer position pointed to by write pointer. + * Increment write pointer. If write pointer is pointing to the same + * position as read pointer, increment read pointer. + * + * If device driver is processing a user process's read() request + * invalidate the read() request processing operation. + * + */ + +/* ARGSUSED */ +void +dcam_frame_is_done(void *ssp, ixl1394_callback_t *ixlp) +{ + dcam_state_t *softc_p; + int num_read_ptrs; + int read_ptr_id; + int vid_mode; + size_t write_ptr_pos; + ring_buff_t *ring_buff_p; + unsigned int seq_num; + + /* + * Store received frame in ring buffer position pointed to by + * write pointer (this routine is called after DMA engine has + * stored a single received frame in ring buffer position pointed + * to by write pointer; this routine marks the frame's vid mode, + * timestamp, and sequence number) + */ + + if ((softc_p = (dcam_state_t *)ssp) == NULL) { + return; + } + + if ((ring_buff_p = softc_p->ring_buff_p) == NULL) { + return; + } + + mutex_enter(&softc_p->dcam_frame_is_done_mutex); + + write_ptr_pos = ring_buff_write_ptr_pos_get(ring_buff_p); + + /* mark vid mode */ + vid_mode = + softc_p-> + param_attr[DCAM1394_PARAM_VID_MODE][DCAM1394_SUBPARAM_NONE]; + ring_buff_p->buff_info_array_p[write_ptr_pos].vid_mode = vid_mode; + + + /* update sequence counter overflow in param_status */ + if (softc_p->seq_count == 0xffffffff) + softc_p->param_status |= + DCAM1394_STATUS_FRAME_SEQ_NUM_COUNT_OVERFLOW; + + + /* mark frame's sequence number */ + ring_buff_p->buff_info_array_p[write_ptr_pos].seq_num = + softc_p->seq_count++; + + seq_num = ring_buff_p->buff_info_array_p[write_ptr_pos].seq_num; + + + /* mark frame's timestamp */ + ring_buff_p->buff_info_array_p[write_ptr_pos].timestamp = gethrtime(); + + + /* increment write pointer */ + ring_buff_write_ptr_incr(ring_buff_p); + + num_read_ptrs = 1; + + for (read_ptr_id = 0; read_ptr_id < num_read_ptrs; read_ptr_id++) { + + /* + * if write pointer is pointing to the same position as + * read pointer + */ + + if ((ring_buff_write_ptr_pos_get(ring_buff_p) == + ring_buff_read_ptr_pos_get(ring_buff_p, read_ptr_id)) && + (seq_num != 0)) { + + /* increment read pointer */ + ring_buff_read_ptr_incr(ring_buff_p, read_ptr_id); + + /* + * if device driver is processing a user + * process's read() request + */ + if (softc_p->reader_flags[read_ptr_id] & + DCAM1394_FLAG_READ_REQ_PROC) { + + /* + * invalidate the read() request processing + * operation + */ + softc_p->reader_flags[read_ptr_id] |= + DCAM1394_FLAG_READ_REQ_INVALID; + } + + /* inform user app that we have lost one frame */ + softc_p->param_status |= + DCAM1394_STATUS_RING_BUFF_LOST_FRAME; + } + } + + /* inform user app that we have received one frame */ + softc_p->param_status |= DCAM1394_STATUS_FRAME_RCV_DONE; + + mutex_exit(&softc_p->dcam_frame_is_done_mutex); +} + + +/* ARGSUSED */ +static void +dcam_rsrc_fail(t1394_isoch_single_handle_t t1394_single_hdl, + opaque_t single_evt_arg, t1394_isoch_rsrc_error_t fail_args) +{ + cmn_err(CE_NOTE, "dcam_rsrc_fail(): unable to re-alloc resources\n"); +} diff --git a/usr/src/uts/common/io/1394/targets/dcam1394/dcam_param.c b/usr/src/uts/common/io/1394/targets/dcam1394/dcam_param.c new file mode 100644 index 0000000000..445d3ae04d --- /dev/null +++ b/usr/src/uts/common/io/1394/targets/dcam1394/dcam_param.c @@ -0,0 +1,1803 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * dcam_param.c + * + * dcam1394 driver. Device parameter access. + */ + +#include <sys/1394/targets/dcam1394/dcam.h> +#include <sys/1394/targets/dcam1394/dcam_param.h> +#include <sys/1394/targets/dcam1394/dcam_reg.h> +#include <sys/tnf_probe.h> + +/* index by vid_mode */ +int g_frame_num_bytes[] = { + 57600, /* vid mode 0 */ + 153600, /* vid mode 1 */ + 460800, /* vid mode 2 */ + 614400, /* vid mode 3 */ + 921600, /* vid mode 4 */ + 307200 /* vid mode 5 */ +}; + + +static uint_t feature_csr_val_construct(uint_t subparam, uint_t param_val, + uint_t init_val); +static uint_t feature_csr_val_subparam_extract(uint_t subparam, + uint_t feature_csr_val); +static uint_t feature_elm_inq_reg_val_subparam_extract(uint_t subparam, + uint_t reg_val); + + +/* + * param_attr_init + */ +int +param_attr_init(dcam_state_t *softc_p, dcam1394_param_attr_t param_attr) +{ + int err, ret_err; + uint_t attr_bmap, cap_on_off, cap_power_ctrl, cap_read; + uint_t param, presence, subparam; + + bzero(param_attr, sizeof (dcam1394_param_attr_t)); + + ret_err = DDI_SUCCESS; + + /* + * power ctrl cap + */ + param = DCAM1394_PARAM_CAP_POWER_CTRL; + subparam = DCAM1394_SUBPARAM_NONE; + attr_bmap = IS_VALID | IS_PRESENT | CAP_GET; + param_attr_set(param_attr, param, subparam, attr_bmap); + + /* + * video mode cap + */ + param = DCAM1394_PARAM_CAP_VID_MODE; + attr_bmap = IS_VALID | IS_PRESENT | CAP_GET; + + for (subparam = DCAM1394_SUBPARAM_VID_MODE_0; + subparam <= DCAM1394_SUBPARAM_VID_MODE_5; subparam++) { + param_attr_set(param_attr, param, subparam, attr_bmap); + } + + /* + * frame rate cap + */ + attr_bmap = IS_VALID | IS_PRESENT | CAP_GET; + + for (param = DCAM1394_PARAM_CAP_FRAME_RATE_VID_MODE_0; + param <= DCAM1394_PARAM_CAP_FRAME_RATE_VID_MODE_5; param++) { + + for (subparam = DCAM1394_SUBPARAM_FRAME_RATE_0; + subparam <= DCAM1394_SUBPARAM_FRAME_RATE_4; subparam++) { + param_attr_set(param_attr, param, subparam, attr_bmap); + } + } + + /* + * power + */ + param = DCAM1394_PARAM_POWER; + subparam = DCAM1394_SUBPARAM_NONE; + err = dcam1394_param_get(softc_p, DCAM1394_PARAM_CAP_POWER_CTRL, + DCAM1394_SUBPARAM_NONE, &cap_power_ctrl); + attr_bmap = IS_VALID | IS_PRESENT | CAP_GET; + + if (cap_power_ctrl) { + attr_bmap |= CAP_SET; + } + param_attr_set(param_attr, param, subparam, attr_bmap); + + /* + * video mode + */ + param = DCAM1394_PARAM_VID_MODE; + subparam = DCAM1394_SUBPARAM_NONE; + attr_bmap = IS_VALID | IS_PRESENT | CAP_GET | CAP_SET; + param_attr_set(param_attr, param, subparam, attr_bmap); + + /* + * frame rate + */ + param = DCAM1394_PARAM_FRAME_RATE; + subparam = DCAM1394_SUBPARAM_NONE; + attr_bmap = IS_VALID | IS_PRESENT | CAP_GET | CAP_SET; + param_attr_set(param_attr, param, subparam, attr_bmap); + + /* + * ring buffer capacity + */ + param = DCAM1394_PARAM_RING_BUFF_CAPACITY; + subparam = DCAM1394_SUBPARAM_NONE; + attr_bmap = IS_VALID | IS_PRESENT | CAP_GET | CAP_SET; + param_attr_set(param_attr, param, subparam, attr_bmap); + + /* + * ring buffer: num frames ready + */ + param = DCAM1394_PARAM_RING_BUFF_NUM_FRAMES_READY; + subparam = DCAM1394_SUBPARAM_NONE; + attr_bmap = IS_VALID | IS_PRESENT | CAP_GET; + param_attr_set(param_attr, param, subparam, attr_bmap); + + /* + * ring buffer: read ptr increment stride + */ + param = DCAM1394_PARAM_RING_BUFF_READ_PTR_INCR; + subparam = DCAM1394_SUBPARAM_NONE; + attr_bmap = IS_VALID | IS_PRESENT | CAP_GET | CAP_SET; + param_attr_set(param_attr, param, subparam, attr_bmap); + + /* + * frame size + */ + param = DCAM1394_PARAM_FRAME_NUM_BYTES; + subparam = DCAM1394_SUBPARAM_NONE; + attr_bmap = IS_VALID | IS_PRESENT | CAP_GET; + param_attr_set(param_attr, param, subparam, attr_bmap); + + /* + * cam status + */ + param = DCAM1394_PARAM_STATUS; + subparam = DCAM1394_SUBPARAM_NONE; + attr_bmap = IS_VALID | IS_PRESENT | CAP_GET; + param_attr_set(param_attr, param, subparam, attr_bmap); + + /* + * features + */ + for (param = DCAM1394_PARAM_BRIGHTNESS; param <= DCAM1394_PARAM_TILT; + param++) { + + /* + * get feature presence + * If the operation to read the parameter fails, then act as + * though the feature is not implemented (because it isn't), + * don't report a DDI failure (as was previously done). + */ + err = dcam1394_param_get(softc_p, param, + DCAM1394_SUBPARAM_PRESENCE, &presence); + + if (!err) { + /* feature presence */ + subparam = DCAM1394_SUBPARAM_PRESENCE; + attr_bmap = IS_VALID | IS_PRESENT | CAP_GET; + param_attr_set(param_attr, param, subparam, attr_bmap); + + if (presence) { + /* feature cap read */ + subparam = DCAM1394_SUBPARAM_CAP_READ; + attr_bmap = IS_VALID | IS_PRESENT | CAP_GET; + param_attr_set(param_attr, param, + subparam, attr_bmap); + + /* feature cap on/off */ + subparam = DCAM1394_SUBPARAM_CAP_ON_OFF; + attr_bmap = IS_VALID | IS_PRESENT | CAP_GET; + param_attr_set(param_attr, param, + subparam, attr_bmap); + + /* feature cap ctrl auto */ + subparam = DCAM1394_SUBPARAM_CAP_CTRL_AUTO; + attr_bmap = IS_VALID | IS_PRESENT | CAP_GET; + param_attr_set(param_attr, param, + subparam, attr_bmap); + + /* feature cap ctrl manual */ + subparam = DCAM1394_SUBPARAM_CAP_CTRL_MANUAL; + attr_bmap = IS_VALID | IS_PRESENT | CAP_GET; + param_attr_set(param_attr, param, + subparam, attr_bmap); + + /* feature min val */ + subparam = DCAM1394_SUBPARAM_MIN_VAL; + attr_bmap = IS_VALID | IS_PRESENT | CAP_GET; + param_attr_set(param_attr, param, + subparam, attr_bmap); + + /* feature max val */ + subparam = DCAM1394_SUBPARAM_MAX_VAL; + attr_bmap = IS_VALID | IS_PRESENT | CAP_GET; + param_attr_set(param_attr, param, + subparam, attr_bmap); + + /* feature on/off */ + subparam = DCAM1394_SUBPARAM_ON_OFF; + + err = dcam1394_param_get(softc_p, param, + DCAM1394_SUBPARAM_CAP_ON_OFF, &cap_on_off); + + attr_bmap = IS_VALID | IS_PRESENT | CAP_GET; + + if (cap_on_off) { + attr_bmap |= (CAP_SET | CAP_CTRL_SET); + } + + param_attr_set(param_attr, param, + subparam, attr_bmap); + + /* feature control mode */ + subparam = DCAM1394_SUBPARAM_CTRL_MODE; + attr_bmap = IS_VALID | IS_PRESENT | CAP_GET | + CAP_SET | CAP_CTRL_SET; + + param_attr_set(param_attr, param, + subparam, attr_bmap); + + /* get value read-out capability */ + err = dcam1394_param_get(softc_p, param, + DCAM1394_SUBPARAM_CAP_READ, + &cap_read); + + if (param == DCAM1394_PARAM_WHITE_BALANCE) { + /* + * white balance feature: u, v value + */ + subparam = DCAM1394_SUBPARAM_U_VALUE; + attr_bmap = IS_VALID | IS_PRESENT | + CAP_SET | CAP_CTRL_SET; + + if (cap_read) { + attr_bmap |= CAP_GET; + } + + param_attr_set(param_attr, param, + subparam, attr_bmap); + + subparam = DCAM1394_SUBPARAM_V_VALUE; + attr_bmap = IS_VALID | IS_PRESENT | + CAP_SET | CAP_CTRL_SET; + + if (cap_read) { + attr_bmap |= CAP_GET; + } + + param_attr_set(param_attr, param, + subparam, attr_bmap); + + } else { + /* feature value */ + subparam = DCAM1394_SUBPARAM_VALUE; + attr_bmap = IS_VALID | IS_PRESENT | + CAP_SET | CAP_CTRL_SET; + + if (cap_read) { + attr_bmap |= CAP_GET; + } + + param_attr_set(param_attr, param, + subparam, attr_bmap); + } + + } + + } + } + + return (ret_err); +} + + +/* + * param_attr_set + */ +void +param_attr_set(dcam1394_param_attr_t param_attr, uint_t param, + uint_t subparam, uint_t attr_bmap) +{ + param_attr[param][subparam] = attr_bmap; +} + + +/* + * dcam1394_ioctl_param_get + * + * softc's param_attr field must be initialized via param_attr_init() + * before using this function. + */ +int +dcam1394_ioctl_param_get(dcam_state_t *softc_p, + dcam1394_param_list_t param_list) +{ + int err, ret_err; + int param, subparam; + uint_t cap_get, is_present, is_valid, val; + + ret_err = 0; + + for (param = 0; param < DCAM1394_NUM_PARAM; param++) { + for (subparam = 0; + subparam < DCAM1394_NUM_SUBPARAM; + subparam++) { + + if (param_list[param][subparam].flag) { + is_valid = + softc_p->param_attr[param][subparam] & + IS_VALID; + is_present = + softc_p->param_attr[param][subparam] & + IS_PRESENT; + cap_get = + softc_p->param_attr[param][subparam] & + CAP_GET; + + if (is_valid && is_present && cap_get) { + if (err = dcam1394_param_get(softc_p, + param, subparam, &val)) { + + param_list[param][subparam].err = 1; + ret_err = 1; + } + + if (!err) { + param_list[param][subparam].val = + val; + } + } else { + param_list[param][subparam].err = 1; + ret_err = 1; + } + } + } + } + + return (ret_err); +} + + +/* + * dcam1394_ioctl_param_set + * softc's param_attr field must be initialized via param_attr_init() + * before using this function. + */ +int +dcam1394_ioctl_param_set(dcam_state_t *softc_p, int is_ctrl_file, + dcam1394_param_list_t param_list) +{ + int param, subparam; + int ret_err; + uint_t cap_set, is_present, is_valid, val; + + ret_err = 0; + + for (param = 0; param < DCAM1394_NUM_PARAM; param++) { + for (subparam = 0; + subparam < DCAM1394_NUM_SUBPARAM; + subparam++) { + if (param_list[param][subparam].flag) { + is_valid = + softc_p->param_attr[param][subparam] & + IS_VALID; + is_present = + softc_p->param_attr[param][subparam] & + IS_PRESENT; + + cap_set = is_ctrl_file ? + (softc_p->param_attr[param][subparam] + & CAP_CTRL_SET) : + (softc_p->param_attr[param][subparam] + & CAP_SET); + + if (is_valid && is_present && cap_set) { + val = param_list[param][subparam].val; + + if (dcam1394_param_set(softc_p, + param, subparam, val)) { + + param_list[param][subparam].err = 1; + ret_err = 1; + } + } else { + param_list[param][subparam].err = 1; + ret_err = 1; + } + } + } + } + + return (ret_err); +} + + +/* + * dcam1394_param_get + */ +int +dcam1394_param_get(dcam_state_t *softc_p, uint_t param, uint_t subparam, + uint_t *val_p) +{ + int err; + + switch (param) { + + case DCAM1394_PARAM_CAP_POWER_CTRL: + err = param_cap_power_ctrl_get(softc_p, val_p); + break; + + case DCAM1394_PARAM_CAP_VID_MODE: + err = param_cap_vid_mode_get(softc_p, subparam, val_p); + break; + + case DCAM1394_PARAM_CAP_FRAME_RATE_VID_MODE_0: + case DCAM1394_PARAM_CAP_FRAME_RATE_VID_MODE_1: + case DCAM1394_PARAM_CAP_FRAME_RATE_VID_MODE_2: + case DCAM1394_PARAM_CAP_FRAME_RATE_VID_MODE_3: + case DCAM1394_PARAM_CAP_FRAME_RATE_VID_MODE_4: + case DCAM1394_PARAM_CAP_FRAME_RATE_VID_MODE_5: + err = param_cap_frame_rate_get(softc_p, param, subparam, val_p); + break; + + case DCAM1394_PARAM_POWER: + err = param_power_get(softc_p, val_p); + break; + + case DCAM1394_PARAM_VID_MODE: + err = param_vid_mode_get(softc_p, val_p); + break; + + case DCAM1394_PARAM_FRAME_RATE: + err = param_frame_rate_get(softc_p, val_p); + break; + + case DCAM1394_PARAM_RING_BUFF_CAPACITY: + err = param_ring_buff_capacity_get(softc_p, val_p); + break; + + case DCAM1394_PARAM_RING_BUFF_NUM_FRAMES_READY: + err = param_ring_buff_num_frames_ready_get(softc_p, val_p); + break; + + case DCAM1394_PARAM_RING_BUFF_READ_PTR_INCR: + err = param_ring_buff_read_ptr_incr_get(softc_p, val_p); + break; + + case DCAM1394_PARAM_FRAME_NUM_BYTES: + err = param_frame_num_bytes_get(softc_p, val_p); + break; + + case DCAM1394_PARAM_STATUS: + err = param_status_get(softc_p, val_p); + break; + + case DCAM1394_PARAM_BRIGHTNESS: + err = param_brightness_get(softc_p, subparam, val_p); + break; + + case DCAM1394_PARAM_EXPOSURE: + err = param_exposure_get(softc_p, subparam, val_p); + break; + + case DCAM1394_PARAM_SHARPNESS: + err = param_sharpness_get(softc_p, subparam, val_p); + break; + + case DCAM1394_PARAM_WHITE_BALANCE: + err = param_white_balance_get(softc_p, subparam, val_p); + break; + + case DCAM1394_PARAM_HUE: + err = param_hue_get(softc_p, subparam, val_p); + break; + + case DCAM1394_PARAM_SATURATION: + err = param_saturation_get(softc_p, subparam, val_p); + break; + + case DCAM1394_PARAM_GAMMA: + err = param_gamma_get(softc_p, subparam, val_p); + break; + + case DCAM1394_PARAM_SHUTTER: + err = param_shutter_get(softc_p, subparam, val_p); + break; + + case DCAM1394_PARAM_GAIN: + err = param_gain_get(softc_p, subparam, val_p); + break; + + case DCAM1394_PARAM_IRIS: + err = param_iris_get(softc_p, subparam, val_p); + break; + + case DCAM1394_PARAM_FOCUS: + err = param_focus_get(softc_p, subparam, val_p); + break; + + case DCAM1394_PARAM_ZOOM: + err = param_zoom_get(softc_p, subparam, val_p); + break; + + case DCAM1394_PARAM_PAN: + err = param_pan_get(softc_p, subparam, val_p); + break; + + case DCAM1394_PARAM_TILT: + err = param_tilt_get(softc_p, subparam, val_p); + break; + + default: + err = 1; + break; + } + + return (err); +} + + +/* + * dcam1394_param_set + */ +int +dcam1394_param_set(dcam_state_t *softc_p, uint_t param, uint_t subparam, + uint_t val) +{ + int err; + + switch (param) { + + case DCAM1394_PARAM_POWER: + err = param_power_set(softc_p, val); + break; + + case DCAM1394_PARAM_VID_MODE: + err = param_vid_mode_set(softc_p, val); + break; + + case DCAM1394_PARAM_FRAME_RATE: + err = param_frame_rate_set(softc_p, val); + break; + + case DCAM1394_PARAM_RING_BUFF_CAPACITY: + err = param_ring_buff_capacity_set(softc_p, val); + break; + + case DCAM1394_PARAM_RING_BUFF_READ_PTR_INCR: + err = param_ring_buff_read_ptr_incr_set(softc_p, val); + break; + + case DCAM1394_PARAM_BRIGHTNESS: + err = param_brightness_set(softc_p, subparam, val); + break; + + case DCAM1394_PARAM_EXPOSURE: + err = param_exposure_set(softc_p, subparam, val); + break; + + case DCAM1394_PARAM_SHARPNESS: + err = param_sharpness_set(softc_p, subparam, val); + break; + + case DCAM1394_PARAM_WHITE_BALANCE: + err = param_white_balance_set(softc_p, subparam, val); + break; + + case DCAM1394_PARAM_HUE: + err = param_hue_set(softc_p, subparam, val); + break; + + case DCAM1394_PARAM_SATURATION: + err = param_saturation_set(softc_p, subparam, val); + break; + + case DCAM1394_PARAM_GAMMA: + err = param_gamma_set(softc_p, subparam, val); + break; + + case DCAM1394_PARAM_SHUTTER: + err = param_shutter_set(softc_p, subparam, val); + break; + + case DCAM1394_PARAM_GAIN: + err = param_gain_set(softc_p, subparam, val); + break; + + case DCAM1394_PARAM_IRIS: + err = param_iris_set(softc_p, subparam, val); + break; + + case DCAM1394_PARAM_FOCUS: + err = param_focus_set(softc_p, subparam, val); + break; + + case DCAM1394_PARAM_ZOOM: + err = param_zoom_set(softc_p, subparam, val); + break; + + case DCAM1394_PARAM_PAN: + err = param_pan_set(softc_p, subparam, val); + break; + + case DCAM1394_PARAM_TILT: + err = param_tilt_set(softc_p, subparam, val); + break; + + default: + err = 1; + break; + } + + return (err); +} + + +/* + * feature_get + */ +int +feature_get(dcam_state_t *softc_p, uint_t feature_csr_offs, + uint_t feature_elm_inq_reg_offs, uint_t subparam, uint_t *val_p) +{ + dcam1394_reg_io_t reg_io; + uint_t val; + + switch (subparam) { + + case DCAM1394_SUBPARAM_PRESENCE: + case DCAM1394_SUBPARAM_ON_OFF: + case DCAM1394_SUBPARAM_CTRL_MODE: + case DCAM1394_SUBPARAM_VALUE: + case DCAM1394_SUBPARAM_U_VALUE: + case DCAM1394_SUBPARAM_V_VALUE: + reg_io.offs = feature_csr_offs + + DCAM1394_REG_OFFS_FEATURE_CSR_BASE; + + if (dcam_reg_read(softc_p, ®_io)) { + return (1); + } + + val = feature_csr_val_subparam_extract(subparam, reg_io.val); + break; + + case DCAM1394_SUBPARAM_CAP_READ: + case DCAM1394_SUBPARAM_CAP_ON_OFF: + case DCAM1394_SUBPARAM_CAP_CTRL_AUTO: + case DCAM1394_SUBPARAM_CAP_CTRL_MANUAL: + case DCAM1394_SUBPARAM_MIN_VAL: + case DCAM1394_SUBPARAM_MAX_VAL: + reg_io.offs = feature_elm_inq_reg_offs + + DCAM1394_REG_OFFS_FEATURE_ELM_INQ_BASE; + + if (dcam_reg_read(softc_p, ®_io)) { + return (1); + } + + val = feature_elm_inq_reg_val_subparam_extract(subparam, + reg_io.val); + + break; + + default: + return (1); + } + + *val_p = val; + + return (0); +} + + +/* + * feature_set + */ +int +feature_set(dcam_state_t *softc_p, uint_t feature_csr_offs, + uint_t subparam, uint_t val) +{ + dcam1394_reg_io_t reg_io; + + reg_io.offs = feature_csr_offs + DCAM1394_REG_OFFS_FEATURE_CSR_BASE; + + if (dcam_reg_read(softc_p, ®_io)) { + return (1); + } + + reg_io.val = feature_csr_val_construct(subparam, val, reg_io.val); + + if (dcam_reg_write(softc_p, ®_io)) { + return (1); + } + + return (0); +} + + +/* + * param_cap_power_ctrl_get + */ +int +param_cap_power_ctrl_get(dcam_state_t *softc_p, uint_t *val_p) +{ + dcam1394_reg_io_t reg_io; + + reg_io.offs = DCAM1394_REG_OFFS_BASIC_FUNC_INQ; + + if (dcam_reg_read(softc_p, ®_io)) { + return (1); + } + + *val_p = (reg_io.val & DCAM1394_MASK_CAM_POWER_CTRL) >> + DCAM1394_SHIFT_CAM_POWER_CTRL; + + return (0); +} + + +/* + * param_cap_vid_mode_get + * dcam spec: sec 1.2.1.1 + */ +int +param_cap_vid_mode_get(dcam_state_t *softc_p, uint_t subparam, uint_t *val_p) +{ + dcam1394_reg_io_t reg_io; + uint_t mask, shift, vid_mode; + + vid_mode = subparam - DCAM1394_SUBPARAM_VID_MODE_0; + reg_io.offs = DCAM1394_REG_OFFS_VID_MODE_INQ; + + if (dcam_reg_read(softc_p, ®_io)) { + return (1); + } + + mask = 1 << (31 - vid_mode); + shift = 31 - vid_mode; + + *val_p = (reg_io.val & mask) >> shift; + + return (0); +} + + +/* + * param_cap_frame_rate_get() + * dcam spec: sec 1.2.2 + */ +int +param_cap_frame_rate_get(dcam_state_t *softc_p, uint_t param, + uint_t subparam, uint_t *val_p) +{ + dcam1394_reg_io_t reg_io; + uint_t frame_rate, mask, shift, vid_mode; + + vid_mode = param - DCAM1394_PARAM_CAP_FRAME_RATE_VID_MODE_0; + frame_rate = subparam - DCAM1394_SUBPARAM_FRAME_RATE_0; + + reg_io.offs = DCAM1394_REG_OFFS_FRAME_RATE_INQ_BASE + (4 * vid_mode); + + if (dcam_reg_read(softc_p, ®_io)) { + return (1); + } + + mask = 1 << (31 - (frame_rate + 1)); + shift = 31 - (frame_rate + 1); + + *val_p = (reg_io.val & mask) >> shift; + + return (0); +} + + +/* + * param_power_get + */ +int +param_power_get(dcam_state_t *softc_p, uint_t *val_p) +{ + dcam1394_reg_io_t reg_io; + + reg_io.offs = DCAM1394_REG_OFFS_CAMERA_POWER; + + if (dcam_reg_read(softc_p, ®_io)) { + return (1); + } + + *val_p = reg_io.val >> DCAM1394_SHIFT_CAMERA_POWER; + + return (0); +} + + +/* + * param_power_set() + */ +int +param_power_set(dcam_state_t *softc_p, uint_t val) +{ + dcam1394_reg_io_t reg_io; + + reg_io.offs = DCAM1394_REG_OFFS_CAMERA_POWER; + reg_io.val = val << DCAM1394_SHIFT_CAMERA_POWER; + + if (dcam_reg_write(softc_p, ®_io)) { + return (1); + } + + return (0); +} + + +/* + * param_vid_mode_get + */ +int +param_vid_mode_get(dcam_state_t *softc_p, uint_t *val_p) +{ + dcam1394_reg_io_t reg_io; + + reg_io.offs = DCAM1394_REG_OFFS_CUR_V_MODE; + + if (dcam_reg_read(softc_p, ®_io)) { + return (1); + } + + *val_p = reg_io.val >> DCAM1394_SHIFT_CUR_V_MODE; + + return (0); +} + + +/* + * param_vid_mode_set + */ +int +param_vid_mode_set(dcam_state_t *softc_p, uint_t val) +{ + dcam1394_reg_io_t reg_io; + uint_t vid_mode; + + vid_mode = val - DCAM1394_VID_MODE_0; + + reg_io.offs = DCAM1394_REG_OFFS_CUR_V_MODE; + reg_io.val = vid_mode << DCAM1394_SHIFT_CUR_V_MODE; + + if (dcam_reg_write(softc_p, ®_io)) { + return (1); + } + + softc_p->cur_vid_mode = val; + + /* + * if we are currently receiving frames, we need to do a restart + * so that the new vid mode value takes effect + */ + if (softc_p->flags & DCAM1394_FLAG_FRAME_RCV_INIT) { + (void) dcam_frame_rcv_stop(softc_p); + (void) dcam1394_ioctl_frame_rcv_start(softc_p); + } + + return (0); +} + + +/* + * param_frame_rate_get + */ +int +param_frame_rate_get(dcam_state_t *softc_p, uint_t *val_p) +{ + dcam1394_reg_io_t reg_io; + uint_t frame_rate; + + reg_io.offs = DCAM1394_REG_OFFS_CUR_V_FRM_RATE; + + if (dcam_reg_read(softc_p, ®_io)) { + return (1); + } + + frame_rate = reg_io.val >> DCAM1394_SHIFT_CUR_V_FRM_RATE; + + *val_p = frame_rate - 1 + DCAM1394_FRAME_RATE_0; + + return (0); +} + + +/* + * param_frame_rate_set + */ +int +param_frame_rate_set(dcam_state_t *softc_p, uint_t val) +{ + dcam1394_reg_io_t reg_io; + uint_t frame_rate; + + /* if we are currently receiving frames, stop the camera */ + if (softc_p->flags & DCAM1394_FLAG_FRAME_RCV_INIT) { + (void) dcam_frame_rcv_stop(softc_p); + + frame_rate = val - DCAM1394_FRAME_RATE_0 + 1; + + reg_io.offs = DCAM1394_REG_OFFS_CUR_V_FRM_RATE; + reg_io.val = frame_rate << DCAM1394_SHIFT_CUR_V_FRM_RATE; + + if (dcam_reg_write(softc_p, ®_io)) { + return (1); + } + + /* + * Update the state info. + * note: the driver maintains frame rate in an array + * whereas the the camera uses predefined values whose + * lowest frame rate starts at 6 + */ + softc_p->cur_frame_rate = val - 6; + + /* restart the camera */ + (void) dcam1394_ioctl_frame_rcv_start(softc_p); + } else { + frame_rate = val - DCAM1394_FRAME_RATE_0 + 1; + + reg_io.offs = DCAM1394_REG_OFFS_CUR_V_FRM_RATE; + reg_io.val = frame_rate << DCAM1394_SHIFT_CUR_V_FRM_RATE; + + if (dcam_reg_write(softc_p, ®_io)) { + return (1); + } + + /* see note above re skewing of value by 6 */ + softc_p->cur_frame_rate = val - 6; + } + + return (0); +} + + +/* + * param_ring_buff_capacity_get() + */ +int +param_ring_buff_capacity_get(dcam_state_t *softc_p, uint_t *val_p) +{ + *val_p = softc_p->cur_ring_buff_capacity; + + return (0); +} + + +/* + * param_ring_buff_capacity_set + */ +int +param_ring_buff_capacity_set(dcam_state_t *softc_p, uint_t val) +{ + /* bounds check */ + if ((val < 2) || (val > 30)) { + return (1); + } + + /* update our state info */ + softc_p->cur_ring_buff_capacity = val; + + + /* + * if we are currently receiving frames, we need to do a restart + * so that the new buff_capacity value takes effect + */ + if (softc_p->flags & DCAM1394_FLAG_FRAME_RCV_INIT) { + (void) dcam_frame_rcv_stop(softc_p); + (void) dcam1394_ioctl_frame_rcv_start(softc_p); + } + return (0); +} + + +/* + * param_ring_buff_num_frames_ready_get() + */ +int +param_ring_buff_num_frames_ready_get(dcam_state_t *softc_p, uint_t *val_p) +{ + size_t read_pos, write_pos; + + /* + * note: currently we support only one read_ptr_id, so the + * following logic will work. If multiple read_ptr_id's are + * supported, this function call will need to receive a + * read_ptr_id + */ + + if (softc_p->ring_buff_p == NULL) { + return (1); + } + + mutex_enter(&softc_p->dcam_frame_is_done_mutex); + + read_pos = ring_buff_read_ptr_pos_get(softc_p->ring_buff_p, 0); + write_pos = ring_buff_write_ptr_pos_get(softc_p->ring_buff_p); + + if (read_pos < write_pos) { + *val_p = write_pos - read_pos; + } else { + *val_p = (softc_p->ring_buff_p->num_buffs + write_pos) - + read_pos; + } + + mutex_exit(&softc_p->dcam_frame_is_done_mutex); + + return (0); +} + + +/* + * param_ring_buff_read_ptr_incr_get() + */ + +int +param_ring_buff_read_ptr_incr_get(dcam_state_t *softc_p, uint_t *val_p) +{ + if (softc_p->ring_buff_p == NULL) { + return (1); + } + + *val_p = softc_p->ring_buff_p->read_ptr_incr_val; + + return (0); +} + + +/* + * param_ring_buff_read_ptr_incr_set + */ +int +param_ring_buff_read_ptr_incr_set(dcam_state_t *softc_p, uint_t val) +{ + if (softc_p->ring_buff_p == NULL) { + return (1); + } + + softc_p->ring_buff_p->read_ptr_incr_val = val; + + return (0); +} + + +/* + * param_frame_num_bytes_get + */ +int +param_frame_num_bytes_get(dcam_state_t *softc_p, uint_t *val_p) +{ + if (softc_p == NULL) { + return (1); + } + + *val_p = g_frame_num_bytes[softc_p->cur_vid_mode]; + + return (0); +} + + +/* + * param_status_get() + */ + +int +param_status_get(dcam_state_t *softc_p, uint_t *val_p) +{ + mutex_enter(&softc_p->dcam_frame_is_done_mutex); + + *val_p = softc_p->param_status; + softc_p->param_status = 0; + + mutex_exit(&softc_p->dcam_frame_is_done_mutex); + + return (0); +} + + +/* + * param_brightness_get + */ +int +param_brightness_get(dcam_state_t *softc_p, uint_t subparam, uint_t *val_p) +{ + int ret_val; + uint_t feature_csr_offs; + uint_t feature_elm_inq_reg_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_BRIGHTNESS_CSR; + feature_elm_inq_reg_offs = DCAM1394_REG_OFFS_BRIGHTNESS_INQ; + + ret_val = feature_get(softc_p, feature_csr_offs, + feature_elm_inq_reg_offs, subparam, val_p); + + return (ret_val); +} + + +/* + * param_brightness_set() + */ +int +param_brightness_set(dcam_state_t *softc_p, uint_t subparam, uint_t val) +{ + int ret_val; + uint_t feature_csr_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_BRIGHTNESS_CSR; + + ret_val = feature_set(softc_p, feature_csr_offs, subparam, val); + + return (ret_val); +} + + +/* + * param_exposure_get + */ +int +param_exposure_get(dcam_state_t *softc_p, uint_t subparam, uint_t *val_p) +{ + int ret_val; + uint_t feature_csr_offs; + uint_t feature_elm_inq_reg_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_EXPOSURE_CSR; + feature_elm_inq_reg_offs = DCAM1394_REG_OFFS_EXPOSURE_INQ; + + ret_val = feature_get(softc_p, feature_csr_offs, + feature_elm_inq_reg_offs, subparam, val_p); + + return (ret_val); +} + + +/* + * param_exposure_set + */ +int +param_exposure_set(dcam_state_t *softc_p, uint_t subparam, uint_t val) +{ + int ret_val; + uint_t feature_csr_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_EXPOSURE_CSR; + + ret_val = feature_set(softc_p, feature_csr_offs, subparam, val); + + return (ret_val); +} + + +/* + * param_sharpness_get + */ +int +param_sharpness_get(dcam_state_t *softc_p, uint_t subparam, uint_t *val_p) +{ + int ret_val; + uint_t feature_csr_offs; + uint_t feature_elm_inq_reg_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_SHARPNESS_CSR; + feature_elm_inq_reg_offs = DCAM1394_REG_OFFS_SHARPNESS_INQ; + + ret_val = feature_get(softc_p, feature_csr_offs, + feature_elm_inq_reg_offs, subparam, val_p); + + return (ret_val); +} + + +/* + * param_sharpness_set + */ +int +param_sharpness_set(dcam_state_t *softc_p, uint_t subparam, uint_t val) +{ + int ret_val; + uint_t feature_csr_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_SHARPNESS_CSR; + + ret_val = feature_set(softc_p, feature_csr_offs, subparam, val); + + return (ret_val); +} + + +/* + * param_white_balance_get + */ +int +param_white_balance_get(dcam_state_t *softc_p, uint_t subparam, uint_t *val_p) +{ + int ret_val; + uint_t feature_csr_offs; + uint_t feature_elm_inq_reg_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_WHITE_BALANCE_CSR; + feature_elm_inq_reg_offs = DCAM1394_REG_OFFS_WHITE_BALANCE_INQ; + + ret_val = feature_get(softc_p, feature_csr_offs, + feature_elm_inq_reg_offs, subparam, val_p); + + return (ret_val); +} + + +/* + * param_white_balance_set + */ +int +param_white_balance_set(dcam_state_t *softc_p, uint_t subparam, uint_t val) +{ + int ret_val; + uint_t feature_csr_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_WHITE_BALANCE_CSR; + + ret_val = feature_set(softc_p, feature_csr_offs, subparam, val); + + return (ret_val); +} + + +/* + * param_hue_get + */ +int +param_hue_get(dcam_state_t *softc_p, uint_t subparam, uint_t *val_p) +{ + int ret_val; + uint_t feature_csr_offs; + uint_t feature_elm_inq_reg_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_HUE_CSR; + feature_elm_inq_reg_offs = DCAM1394_REG_OFFS_HUE_INQ; + + ret_val = feature_get(softc_p, feature_csr_offs, + feature_elm_inq_reg_offs, subparam, val_p); + + return (ret_val); +} + + +/* + * param_hue_set + */ +int +param_hue_set(dcam_state_t *softc_p, uint_t subparam, uint_t val) +{ + int ret_val; + uint_t feature_csr_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_HUE_CSR; + + ret_val = feature_set(softc_p, feature_csr_offs, subparam, val); + + return (ret_val); +} + + +/* + * param_saturation_get + */ +int +param_saturation_get(dcam_state_t *softc_p, uint_t subparam, uint_t *val_p) +{ + int ret_val; + uint_t feature_csr_offs; + uint_t feature_elm_inq_reg_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_SATURATION_CSR; + feature_elm_inq_reg_offs = DCAM1394_REG_OFFS_SATURATION_INQ; + + ret_val = feature_get(softc_p, feature_csr_offs, + feature_elm_inq_reg_offs, subparam, val_p); + + return (ret_val); +} + + +/* + * param_saturation_set + */ +int +param_saturation_set(dcam_state_t *softc_p, uint_t subparam, uint_t val) +{ + int ret_val; + uint_t feature_csr_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_SATURATION_CSR; + + ret_val = feature_set(softc_p, feature_csr_offs, subparam, val); + + return (ret_val); +} + + +/* + * param_gamma_get + */ +int +param_gamma_get(dcam_state_t *softc_p, uint_t subparam, uint_t *val_p) +{ + int ret_val; + uint_t feature_csr_offs; + uint_t feature_elm_inq_reg_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_GAMMA_CSR; + feature_elm_inq_reg_offs = DCAM1394_REG_OFFS_GAMMA_INQ; + + ret_val = feature_get(softc_p, feature_csr_offs, + feature_elm_inq_reg_offs, subparam, val_p); + + return (ret_val); +} + + +/* + * param_gamma_set + */ +int +param_gamma_set(dcam_state_t *softc_p, uint_t subparam, uint_t val) +{ + int ret_val; + uint_t feature_csr_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_GAMMA_CSR; + + ret_val = feature_set(softc_p, feature_csr_offs, subparam, val); + + return (ret_val); +} + + +/* + * param_shutter_get + */ +int +param_shutter_get(dcam_state_t *softc_p, uint_t subparam, uint_t *val_p) +{ + int ret_val; + uint_t feature_csr_offs; + uint_t feature_elm_inq_reg_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_SHUTTER_CSR; + feature_elm_inq_reg_offs = DCAM1394_REG_OFFS_SHUTTER_INQ; + + ret_val = feature_get(softc_p, feature_csr_offs, + feature_elm_inq_reg_offs, subparam, val_p); + + return (ret_val); +} + + +/* + * param_shutter_set + */ +int +param_shutter_set(dcam_state_t *softc_p, uint_t subparam, uint_t val) +{ + int ret_val; + uint_t feature_csr_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_SHUTTER_CSR; + + ret_val = feature_set(softc_p, feature_csr_offs, subparam, val); + + return (ret_val); +} + + +/* + * param_gain_get + */ +int +param_gain_get(dcam_state_t *softc_p, uint_t subparam, uint_t *val_p) +{ + int ret_val; + uint_t feature_csr_offs; + uint_t feature_elm_inq_reg_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_GAIN_CSR; + feature_elm_inq_reg_offs = DCAM1394_REG_OFFS_GAIN_INQ; + + ret_val = feature_get(softc_p, feature_csr_offs, + feature_elm_inq_reg_offs, subparam, val_p); + + return (ret_val); +} + + +/* + * param_gain_set + */ +int +param_gain_set(dcam_state_t *softc_p, uint_t subparam, uint_t val) +{ + int ret_val; + uint_t feature_csr_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_GAIN_CSR; + + ret_val = feature_set(softc_p, feature_csr_offs, subparam, val); + + return (ret_val); +} + + +/* + * param_iris_get + */ +int +param_iris_get(dcam_state_t *softc_p, uint_t subparam, uint_t *val_p) +{ + int ret_val; + uint_t feature_csr_offs; + uint_t feature_elm_inq_reg_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_IRIS_CSR; + feature_elm_inq_reg_offs = DCAM1394_REG_OFFS_IRIS_INQ; + + ret_val = feature_get(softc_p, feature_csr_offs, + feature_elm_inq_reg_offs, subparam, val_p); + + switch (subparam) { + case DCAM1394_SUBPARAM_PRESENCE: + *val_p = 0; + break; + case DCAM1394_SUBPARAM_ON_OFF: + *val_p = 1; + break; + case DCAM1394_SUBPARAM_MIN_VAL: + case DCAM1394_SUBPARAM_MAX_VAL: + case DCAM1394_SUBPARAM_VALUE: + *val_p = 4; + break; + default: + break; + } + + return (ret_val); +} + + +/* + * param_iris_set + */ +int +param_iris_set(dcam_state_t *softc_p, uint_t subparam, uint_t val) +{ + int ret_val; + uint_t feature_csr_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_IRIS_CSR; + + if (subparam == DCAM1394_SUBPARAM_ON_OFF) { + val = 1; + } else if (subparam == DCAM1394_SUBPARAM_VALUE) { + val = 4; + } + ret_val = feature_set(softc_p, feature_csr_offs, subparam, val); + + return (ret_val); +} + + +/* + * param_focus_get + */ +int +param_focus_get(dcam_state_t *softc_p, uint_t subparam, uint_t *val_p) +{ + int ret_val; + uint_t feature_csr_offs; + uint_t feature_elm_inq_reg_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_FOCUS_CSR; + feature_elm_inq_reg_offs = DCAM1394_REG_OFFS_FOCUS_INQ; + + ret_val = feature_get(softc_p, feature_csr_offs, + feature_elm_inq_reg_offs, subparam, val_p); + + return (ret_val); +} + + +/* + * param_focus_set + */ +int +param_focus_set(dcam_state_t *softc_p, uint_t subparam, uint_t val) +{ + int ret_val; + uint_t feature_csr_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_FOCUS_CSR; + + ret_val = feature_set(softc_p, feature_csr_offs, subparam, val); + + return (ret_val); +} + + +/* + * param_zoom_get + */ +int +param_zoom_get(dcam_state_t *softc_p, uint_t subparam, uint_t *val_p) +{ + int ret_val; + uint_t feature_csr_offs; + uint_t feature_elm_inq_reg_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_ZOOM_CSR; + feature_elm_inq_reg_offs = DCAM1394_REG_OFFS_ZOOM_INQ; + + ret_val = feature_get(softc_p, feature_csr_offs, + feature_elm_inq_reg_offs, subparam, val_p); + + return (ret_val); +} + + +/* + * param_zoom_set + */ +int +param_zoom_set(dcam_state_t *softc_p, uint_t subparam, uint_t val) +{ + int ret_val; + uint_t feature_csr_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_ZOOM_CSR; + + ret_val = feature_set(softc_p, feature_csr_offs, subparam, val); + + return (ret_val); +} + + +/* + * param_pan_get + */ +int +param_pan_get(dcam_state_t *softc_p, uint_t subparam, uint_t *val_p) +{ + int ret_val; + uint_t feature_csr_offs; + uint_t feature_elm_inq_reg_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_PAN_CSR; + feature_elm_inq_reg_offs = DCAM1394_REG_OFFS_PAN_INQ; + + ret_val = feature_get(softc_p, feature_csr_offs, + feature_elm_inq_reg_offs, subparam, val_p); + + return (ret_val); +} + + +/* + * param_pan_set + */ +int +param_pan_set(dcam_state_t *softc_p, uint_t subparam, uint_t val) +{ + int ret_val; + uint_t feature_csr_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_PAN_CSR; + + ret_val = feature_set(softc_p, feature_csr_offs, subparam, val); + + return (ret_val); +} + + +/* + * param_tilt_get + */ +int +param_tilt_get(dcam_state_t *softc_p, uint_t subparam, uint_t *val_p) +{ + int ret_val; + uint_t feature_csr_offs; + uint_t feature_elm_inq_reg_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_TILT_CSR; + feature_elm_inq_reg_offs = DCAM1394_REG_OFFS_TILT_INQ; + + ret_val = feature_get(softc_p, feature_csr_offs, + feature_elm_inq_reg_offs, subparam, val_p); + + return (ret_val); +} + + +/* + * param_tilt_set + */ +int +param_tilt_set(dcam_state_t *softc_p, uint_t subparam, uint_t val) +{ + int ret_val; + uint_t feature_csr_offs; + + feature_csr_offs = DCAM1394_REG_OFFS_TILT_CSR; + + ret_val = feature_set(softc_p, feature_csr_offs, subparam, val); + + return (ret_val); +} + + +/* + * feature_csr_val_construct + */ +static uint_t +feature_csr_val_construct(uint_t subparam, uint_t param_val, uint_t init_val) +{ + uint_t ret_val; + + switch (subparam) { + + case DCAM1394_SUBPARAM_ON_OFF: + ret_val = (init_val & ~(DCAM1394_MASK_ON_OFF)) | + (param_val << DCAM1394_SHIFT_ON_OFF); + break; + + case DCAM1394_SUBPARAM_CTRL_MODE: + ret_val = (init_val & ~(DCAM1394_MASK_A_M_MODE)) | + (param_val << DCAM1394_SHIFT_A_M_MODE); + break; + + case DCAM1394_SUBPARAM_VALUE: + ret_val = (init_val & ~(DCAM1394_MASK_VALUE)) | + (param_val << DCAM1394_SHIFT_VALUE); + break; + + case DCAM1394_SUBPARAM_U_VALUE: + ret_val = (init_val & ~(DCAM1394_MASK_U_VALUE)) | + (param_val << DCAM1394_SHIFT_U_VALUE); + break; + + case DCAM1394_SUBPARAM_V_VALUE: + ret_val = (init_val & ~(DCAM1394_MASK_V_VALUE)) | + (param_val << DCAM1394_SHIFT_V_VALUE); + break; + + default: + break; + + } + + return (ret_val); +} + + +/* + * feature_csr_val_subparam_extract + */ +static uint_t +feature_csr_val_subparam_extract(uint_t subparam, uint_t reg_val) +{ + uint_t ret_val; + + switch (subparam) { + + case DCAM1394_SUBPARAM_PRESENCE: + ret_val = (reg_val & DCAM1394_MASK_PRESENCE_INQ) >> + DCAM1394_SHIFT_PRESENCE_INQ; + break; + + case DCAM1394_SUBPARAM_ON_OFF: + ret_val = (reg_val & DCAM1394_MASK_ON_OFF) >> + DCAM1394_SHIFT_ON_OFF; + break; + + case DCAM1394_SUBPARAM_CTRL_MODE: + ret_val = (reg_val & DCAM1394_MASK_A_M_MODE) >> + DCAM1394_SHIFT_A_M_MODE; + break; + + case DCAM1394_SUBPARAM_VALUE: + ret_val = (reg_val & DCAM1394_MASK_VALUE) >> + DCAM1394_SHIFT_VALUE; + break; + + case DCAM1394_SUBPARAM_U_VALUE: + ret_val = (reg_val & DCAM1394_MASK_U_VALUE) >> + DCAM1394_SHIFT_U_VALUE; + break; + + case DCAM1394_SUBPARAM_V_VALUE: + + ret_val = (reg_val & DCAM1394_MASK_V_VALUE) >> + DCAM1394_SHIFT_V_VALUE; + break; + + default: + + ret_val = 0; + + break; + + } + + return (ret_val); + +} + + +/* + * feature_elm_inq_reg_val_subparam_extract + */ +static uint_t +feature_elm_inq_reg_val_subparam_extract(uint_t subparam, + uint_t reg_val) +{ + uint_t ret_val; + + switch (subparam) { + + case DCAM1394_SUBPARAM_CAP_READ: + ret_val = (reg_val & DCAM1394_MASK_READOUT_INQ) >> + DCAM1394_SHIFT_READOUT_INQ; + break; + + case DCAM1394_SUBPARAM_CAP_ON_OFF: + ret_val = (reg_val & DCAM1394_MASK_ON_OFF_INQ) >> + DCAM1394_SHIFT_ON_OFF_INQ; + break; + + case DCAM1394_SUBPARAM_CAP_CTRL_AUTO: + ret_val = (reg_val & DCAM1394_MASK_AUTO_INQ) >> + DCAM1394_SHIFT_AUTO_INQ; + break; + + case DCAM1394_SUBPARAM_CAP_CTRL_MANUAL: + ret_val = (reg_val & DCAM1394_MASK_MANUAL_INQ) >> + DCAM1394_SHIFT_MANUAL_INQ; + break; + + case DCAM1394_SUBPARAM_MIN_VAL: + ret_val = (reg_val & DCAM1394_MASK_MIN_VAL) >> + DCAM1394_SHIFT_MIN_VAL; + break; + + case DCAM1394_SUBPARAM_MAX_VAL: + ret_val = (reg_val & DCAM1394_MASK_MAX_VAL) >> + DCAM1394_SHIFT_MAX_VAL; + break; + + default: + ret_val = 0; + break; + + } + + return (ret_val); +} diff --git a/usr/src/uts/common/io/1394/targets/dcam1394/dcam_reg.c b/usr/src/uts/common/io/1394/targets/dcam1394/dcam_reg.c new file mode 100644 index 0000000000..096a632291 --- /dev/null +++ b/usr/src/uts/common/io/1394/targets/dcam1394/dcam_reg.c @@ -0,0 +1,125 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * dcam_reg.c + * + * dcam1394 driver. Control register access support. + */ + +#include <sys/tnf_probe.h> +#include <sys/1394/targets/dcam1394/dcam_reg.h> + + +/* + * dcam_reg_read + */ +int +dcam_reg_read(dcam_state_t *soft_state, dcam1394_reg_io_t *arg) +{ + cmd1394_cmd_t *cmdp; + + if (t1394_alloc_cmd(soft_state->sl_handle, 1, &cmdp) != DDI_SUCCESS) { + return (-1); + } + + cmdp->cmd_type = CMD1394_ASYNCH_RD_QUAD; + cmdp->cmd_addr = 0x0000FFFFF0F00000 | + (uint64_t)(arg->offs & 0x00000FFC); + cmdp->cmd_options = CMD1394_BLOCKING; + +#ifdef GRAPHICS_DELAY + /* + * This delay should not be necessary, but was added for some + * unknown reason. Should it ever be determined that it + * is necessary, this delay should be reenabled. + */ + delay(drv_usectohz(500)); +#endif + + if (t1394_read(soft_state->sl_handle, cmdp) != DDI_SUCCESS) { + (void) t1394_free_cmd(soft_state->sl_handle, 0, &cmdp); + return (-1); + } + + if (cmdp->cmd_result != DDI_SUCCESS) { + (void) t1394_free_cmd(soft_state->sl_handle, 0, &cmdp); + return (-1); + } + + /* perform endian adjustment */ + cmdp->cmd_u.q.quadlet_data = T1394_DATA32(cmdp->cmd_u.q.quadlet_data); + arg->val = cmdp->cmd_u.q.quadlet_data; + + (void) t1394_free_cmd(soft_state->sl_handle, 0, &cmdp); + + return (0); +} + + +/* + * dcam_reg_write + */ +int +dcam_reg_write(dcam_state_t *soft_state, dcam1394_reg_io_t *arg) +{ + cmd1394_cmd_t *cmdp; + + if (t1394_alloc_cmd(soft_state->sl_handle, 0, &cmdp) != DDI_SUCCESS) { + return (-1); + } + + cmdp->cmd_type = CMD1394_ASYNCH_WR_QUAD; + cmdp->cmd_addr = 0x0000FFFFF0F00000 | + (uint64_t)(arg->offs & 0x00000FFC); + cmdp->cmd_options = CMD1394_BLOCKING; + + /* perform endian adjustment */ + cmdp->cmd_u.q.quadlet_data = T1394_DATA32(arg->val); + +#ifdef GRAPHICS_DELAY + /* + * See the description in dcam_reg_read() above. + */ + delay(drv_usectohz(500)); +#endif + + if (t1394_write(soft_state->sl_handle, cmdp) != DDI_SUCCESS) { + (void) t1394_free_cmd(soft_state->sl_handle, 0, &cmdp); + return (-1); + } + + if (cmdp->cmd_result != DDI_SUCCESS) { + (void) t1394_free_cmd(soft_state->sl_handle, 0, &cmdp); + return (-1); + } + + (void) t1394_free_cmd(soft_state->sl_handle, 0, &cmdp); + + return (0); +} diff --git a/usr/src/uts/common/io/1394/targets/dcam1394/dcam_ring_buff.c b/usr/src/uts/common/io/1394/targets/dcam1394/dcam_ring_buff.c new file mode 100644 index 0000000000..0c0fe0aba9 --- /dev/null +++ b/usr/src/uts/common/io/1394/targets/dcam1394/dcam_ring_buff.c @@ -0,0 +1,350 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * dcam_ring_buff.c + * + * dcam1394 driver. Video frame ring buffer support. + */ + +#include <sys/types.h> +#include <sys/kmem.h> +#include <sys/ddidmareq.h> +#include <sys/types.h> +#include <sys/inttypes.h> +#include <sys/tnf_probe.h> +#include <sys/cmn_err.h> + +#include <sys/1394/targets/dcam1394/dcam.h> + +/* + * ring_buff_create + * + * - alloc ring_buff_t structure + * - init ring_buff's num_buffs, buff_num_bytes, num_read_ptrs, + * read_ptr_pos + * - alloc (num buffs) entries in ring_buff's buff_info_array_p + * + * - for each buff + * - alloc DMA handle; store DMA handle in buff's buff_info_array_p + * - alloc mem for DMA transfer; store base addr, data access handle + * in buff's buff_info_array_p entry + * - bind alloc'ed mem to DMA handle; store assoc info in buff's + * buff_info_array_p entry + */ +ring_buff_t * +ring_buff_create(dcam_state_t *softc_p, size_t num_buffs, + size_t buff_num_bytes) +{ + buff_info_t *buff_info_p; + size_t buff; + int i, rc; + ring_buff_t *ring_buff_p; + size_t num_bytes; + + num_bytes = sizeof (ring_buff_t); + + ring_buff_p = (ring_buff_t *)kmem_alloc(num_bytes, KM_SLEEP); + + ring_buff_p->num_buffs = num_buffs; + ring_buff_p->buff_num_bytes = buff_num_bytes; + ring_buff_p->write_ptr_pos = 0; + ring_buff_p->num_read_ptrs = 0; + ring_buff_p->read_ptr_incr_val = 1; + + for (i = 0; i < MAX_NUM_READ_PTRS; i++) { + ring_buff_p->read_ptr_pos[i] = (size_t)-1; + } + + num_bytes = num_buffs * sizeof (buff_info_t); + + ring_buff_p->buff_info_array_p = + (buff_info_t *)kmem_alloc(num_bytes, KM_SLEEP); + + for (buff = 0; buff < num_buffs; buff++) { + + buff_info_p = &(ring_buff_p->buff_info_array_p[buff]); + + if ((ddi_dma_alloc_handle( + softc_p->dip, + &softc_p->attachinfo.dma_attr, + DDI_DMA_DONTWAIT, + NULL, + &(buff_info_p->dma_handle))) != DDI_SUCCESS) { + ring_buff_free(softc_p, ring_buff_p); + return (NULL); + } + + if (ddi_dma_mem_alloc( + buff_info_p->dma_handle, + buff_num_bytes, + &softc_p->attachinfo.acc_attr, + DDI_DMA_STREAMING, + DDI_DMA_DONTWAIT, + (caddr_t)NULL, + &(buff_info_p->kaddr_p), + &(buff_info_p->real_len), + &(buff_info_p->data_acc_handle)) != DDI_SUCCESS) { + ring_buff_free(softc_p, ring_buff_p); + + /* + * Print a warning, this triggered the bug + * report #4423667. This call can fail if + * the memory tests are being run in sunvts. + * The fact is, this code is doing the right + * thing. I added an error message, so that + * future occurrences can be dealt with directly. + * This is not a bug... The vmem test in sunvts + * can eat up all swap/virtual memory. + */ + cmn_err(CE_WARN, + "ddi_dma_mem_alloc() failed in ring_buff_create(),"\ + " insufficient memory resources.\n"); + return (NULL); + } + + rc = ddi_dma_addr_bind_handle( + buff_info_p->dma_handle, + (struct as *)NULL, + (caddr_t)buff_info_p->kaddr_p, + buff_info_p->real_len, + DDI_DMA_RDWR | DDI_DMA_STREAMING, + DDI_DMA_DONTWAIT, + NULL, + &buff_info_p->dma_cookie, + &buff_info_p->dma_cookie_count); + + if (rc != DDI_DMA_MAPPED) { + ring_buff_free(softc_p, ring_buff_p); + return (NULL); + } + } + + return (ring_buff_p); +} + + +/* + * ring_buff_free + */ +void +ring_buff_free(dcam_state_t *softc_p, ring_buff_t *ring_buff_p) +{ + buff_info_t *buff_info_p; + int i; + + if (ring_buff_p == NULL) { + softc_p->ring_buff_p = NULL; + return; + } + + if (ring_buff_p->buff_info_array_p != NULL) { + for (i = 0; i < ring_buff_p->num_buffs; i++) { + + buff_info_p = &(ring_buff_p->buff_info_array_p[i]); + + (void) ddi_dma_unbind_handle(buff_info_p->dma_handle); + ddi_dma_mem_free(&buff_info_p->data_acc_handle); + ddi_dma_free_handle(&buff_info_p->dma_handle); + } + + kmem_free(ring_buff_p->buff_info_array_p, + ring_buff_p->num_buffs * sizeof (buff_info_t)); + } + + kmem_free(ring_buff_p, sizeof (ring_buff_t)); + + softc_p->ring_buff_p = NULL; +} + + +/* + * ring_buff_read_ptr_add + */ +int +ring_buff_read_ptr_add(ring_buff_t *ring_buff_p) +{ + int i; + int read_ptr_id; + + read_ptr_id = -1; + + for (i = 0; i < MAX_NUM_READ_PTRS; i++) { + + if (ring_buff_p->read_ptr_pos[i] == -1) { + ring_buff_p->read_ptr_pos[i] = 0; + read_ptr_id = i; + break; + } + } + + return (read_ptr_id); +} + + +/* + * ring_buff_read_ptr_remove + */ +int +ring_buff_read_ptr_remove(ring_buff_t *ring_buff_p, int read_ptr_id) +{ + ring_buff_p->read_ptr_pos[read_ptr_id] = (size_t)-1; + + return (0); +} + + +/* + * ring_buff_read_ptr_buff_get + * + * Return pointer to buffer that a read pointer associated with the + * ring buffer is pointing to. + */ +buff_info_t * +ring_buff_read_ptr_buff_get(ring_buff_t *ring_buff_p, int read_ptr_id) +{ + size_t read_ptr_pos; + buff_info_t *buff_info_p; + + read_ptr_pos = ring_buff_p->read_ptr_pos[read_ptr_id]; + buff_info_p = &(ring_buff_p->buff_info_array_p[read_ptr_pos]); + + return (buff_info_p); +} + + +/* + * ring_buff_read_ptr_pos_get + */ +size_t +ring_buff_read_ptr_pos_get(ring_buff_t *ring_buff_p, int read_ptr_id) +{ + return (ring_buff_p->read_ptr_pos[read_ptr_id]); +} + + +/* + * ring_buff_read_ptr_incr + */ +void +ring_buff_read_ptr_incr(ring_buff_t *ring_buff_p, int read_ptr_id) +{ + size_t read_ptr_pos; +#if defined(_ADDL_RING_BUFF_CHECK) + size_t lrp, lwp; /* linear read, write positions */ +#endif /* _ADDL_RING_BUFFER_CHECK */ + + /* + * increment the read pointer based on read_ptr_incr_val + * which can vary from 1 to 10 + */ + + /* get current read pointer pos */ + read_ptr_pos = ring_buff_p->read_ptr_pos[read_ptr_id]; + + ring_buff_p->read_ptr_pos[read_ptr_id] = + (read_ptr_pos + 1) % ring_buff_p->num_buffs; + +#if defined(_ADDL_RING_BUFF_CHECK) + if ((read_ptr_pos == 0) && (ring_buff_p->write_ptr_pos == 0)) { + return; + } + + if (read_ptr_pos < ring_buff_p->write_ptr_pos) { + + /* calculate new read pointer position */ + if ((read_ptr_pos + ring_buff_p->read_ptr_incr_val) < + ring_buff_p->write_ptr_pos) { + + /* there is still some valid frame data */ + ring_buff_p->read_ptr_pos[read_ptr_id] = + (read_ptr_pos + + ring_buff_p->read_ptr_incr_val) % + ring_buff_p->num_buffs; + } else { + /* + * we have skipped beyond available frame + * data, so the buffer is empty + */ + ring_buff_p->read_ptr_pos[read_ptr_id] = + ring_buff_p->write_ptr_pos; + } + } else { + /* + * since read pointer is ahead of write pointer, + * it becomes easier to check for new read + * pointer position if we pretend that our data + * buffer is linear instead of circular + */ + + lrp = read_ptr_pos + ring_buff_p->read_ptr_incr_val; + lwp = ring_buff_p->num_buffs + + ring_buff_p->write_ptr_pos; + + if (lrp < lwp) { + /* there is still some valid frame data */ + ring_buff_p->read_ptr_pos[read_ptr_id] = + (read_ptr_pos + + ring_buff_p->read_ptr_incr_val) % + ring_buff_p->num_buffs; + } else { + /* + * we have skipped beyond available + * frame data, so the buffer is empty + */ + ring_buff_p->read_ptr_pos[read_ptr_id] = + ring_buff_p->write_ptr_pos; + } + } +#endif /* _ADDL_RING_BUFF_CHECK */ +} + + +/* + * ring_buff_write_ptr_pos_get + */ +size_t +ring_buff_write_ptr_pos_get(ring_buff_t *ring_buff_p) +{ + return (ring_buff_p->write_ptr_pos); +} + + +/* + * ring_buff_write_ptr_incr + */ +void +ring_buff_write_ptr_incr(ring_buff_t *ring_buff_p) +{ + size_t write_ptr_pos; + + write_ptr_pos = ring_buff_p->write_ptr_pos; + + ring_buff_p->write_ptr_pos = + ((write_ptr_pos + 1) % ring_buff_p->num_buffs); +} |
