diff options
Diffstat (limited to 'kernel/OS/SunOS')
-rw-r--r-- | kernel/OS/SunOS/.config | 1 | ||||
-rw-r--r-- | kernel/OS/SunOS/module.inc | 389 | ||||
-rw-r--r-- | kernel/OS/SunOS/os_solaris.c | 2401 | ||||
-rw-r--r-- | kernel/OS/SunOS/os_solaris.h | 518 | ||||
-rw-r--r-- | kernel/OS/SunOS/udi.c | 1365 |
5 files changed, 4674 insertions, 0 deletions
diff --git a/kernel/OS/SunOS/.config b/kernel/OS/SunOS/.config new file mode 100644 index 0000000..816ac62 --- /dev/null +++ b/kernel/OS/SunOS/.config @@ -0,0 +1 @@ +mode=kernel diff --git a/kernel/OS/SunOS/module.inc b/kernel/OS/SunOS/module.inc new file mode 100644 index 0000000..a522492 --- /dev/null +++ b/kernel/OS/SunOS/module.inc @@ -0,0 +1,389 @@ +/* + * Purpose: OSS module wrapper for Solaris + * + * This file will be included from the auto-generated drv_cfg.c files. Under + * Solaris this file will be compiled in the development system. + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ +#include <sys/types.h> +#include <sys/modctl.h> +#include <sys/kmem.h> +#include <sys/conf.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> + +static int ossdrv_getinfo (dev_info_t *, ddi_info_cmd_t, void *, void **); +static int ossdrv_attach (dev_info_t *, ddi_attach_cmd_t); +static int ossdrv_detach (dev_info_t *, ddi_detach_cmd_t); + +#ifdef OSS_POWER_MANAGE +static int ossdrv_power (dev_info_t *, int component, int level); +#endif + +/* Entry points structure */ +#if DRIVER_TYPE!=DRV_STREAMS + +static struct cb_ops ossdrv_cb_ops = { + oss_open, + oss_close, + nodev, /* not a block driver */ + nodev, /* no print routine */ + nodev, /* no dump routine */ + oss_read, + oss_write, + oss_ioctl, +#ifdef ALLOW_BUFFER_MAPPING + oss_devmap, +#else + nodev, /* no devmap routine */ +#endif + nodev, + nodev, /* no segmap routine */ + oss_chpoll, /* no chpoll routine */ + ddi_prop_op, + 0, /* not a STREAMS driver */ + D_NEW | D_MP | D_64BIT, /* safe for multi-thread/multi-processor */ + CB_REV +}; +#else +extern struct streamtab DRIVER_STR_INFO; + +struct cb_ops ossdrv_streams_cb_ops = { + nulldev, + nulldev, + nodev, /* not a block driver */ + nodev, /* no print routine */ + nodev, /* no dump routine */ + nodev, + nodev, + nodev, + nodev, /* no devmap routine */ + nodev, + nodev, /* no segmap routine */ + nochpoll, /* no chpoll routine */ + ddi_prop_op, + &DRIVER_STR_INFO, /* cb_str */ + D_NEW | D_MP | D_64BIT, /* safe for multi-thread/multi-processor */ + CB_REV, + nodev, /* cb_aread */ + nodev, /* cb_awrite */ +}; +#endif + +static struct dev_ops ossdrv_dev_ops = { + DEVO_REV, /* devo_rev */ + 0, /* devo_refcnt */ + ossdrv_getinfo, /* devo_getinfo */ + nulldev, /* devo_identify - obsolete */ +#if DRIVER_TYPE==DRV_ISA + ossdrv_probe, +#else + nulldev, /* devo_probe */ +#endif + ossdrv_attach, /* devo_attach */ + ossdrv_detach, /* devo_detach */ + nodev, /* devo_reset */ +#if DRIVER_TYPE==DRV_STREAMS + &ossdrv_streams_cb_ops, /* devi_cb_ops */ +#else + &ossdrv_cb_ops, /* devi_cb_ops */ +#endif + NULL, /* devo_bus_ops */ +#ifdef OSS_POWER_MANAGE + ossdrv_power +#else + NULL /* devo_power */ +#endif +}; + +static struct modldrv ossdrv_modldrv = { + &mod_driverops, /* drv_modops */ + "OSS " OSS_VERSION_STRING, + &ossdrv_dev_ops, /* drv_dev_ops */ +}; + +static struct modlinkage ossdrv_modlinkage = { + MODREV_1, /* ml_rev */ + (void *) &ossdrv_modldrv, /* ml_linkage */ + NULL /* NULL terminates the list */ +}; + +/* + * _init, _info, and _fini support loading and unloading the driver. + */ +int +_init (void) +{ + int error; + + error = mod_install (&ossdrv_modlinkage); + + return error; +} + +int +_fini (void) +{ + int error; + + error = mod_remove (&ossdrv_modlinkage); + return error; +} + +int +_info (struct modinfo *modinfop) +{ + int error; + + error = mod_info (&ossdrv_modlinkage, modinfop); + + return error; +} + +/*ARGSUSED*/ +static int +ossdrv_getinfo (dev_info_t * dontcare_dip, ddi_info_cmd_t cmd, void *arg, + void **result) +{ + dev_t dev; + register int error; + int instance; + dev_info_t *dip; +#ifndef SOL9 + oss_cdev_t *cdev; + int minor; + + dev = (dev_t) arg; + + minor = getminor (dev); + + if (minor >= oss_num_cdevs) + { + *result = NULL; + return DDI_FAILURE; + } + + if ((cdev = oss_cdevs[minor]) == NULL || cdev->osdev == NULL) + { + *result = NULL; + return DDI_FAILURE; + } + + dip = cdev->osdev->dip; +#else + dip = dontcare_dip; +#endif + + if (dip == NULL) + { + /* cmn_err (CE_WARN, "ossdrv_getinfo: dip==NULL\n"); */ + return DDI_FAILURE; + } + + instance = ddi_get_instance (dip); + + switch (cmd) + { + case DDI_INFO_DEVT2DEVINFO: + *result = dip; + error = DDI_SUCCESS; + break; + case DDI_INFO_DEVT2INSTANCE: + *result = (void *) (long)instance; + error = DDI_SUCCESS; + break; + default: + *result = NULL; + error = DDI_FAILURE; + } + + DDB (cmn_err (CE_CONT, + "oss_getinfo: returns %d, result=%lx instance=%d dev=%x\n", + error, (unsigned long)*result, instance, (unsigned int)dev)); + return error; +} + +static int +ossdrv_attach (dev_info_t * dip, ddi_attach_cmd_t cmd) +{ + int instance; + oss_device_t *osdev; + +#ifdef OSS_SUSPEND_RESUME + if (cmd != DDI_RESUME) /* Allow resume */ +#endif + if (cmd != DDI_ATTACH) + { + cmn_err (CE_WARN, "bad attach cmd %d\n", cmd); + return 0; + } + + if (dip == NULL) + { + cmn_err (CE_WARN, "ossdrv_attach: dip==NULL\n"); + return DDI_FAILURE; + } + +#ifdef OSS_SUSPEND_RESUME + if (cmd == DDI_RESUME) + { + if ((osdev = (oss_device_t *) ddi_get_driver_private (dip)) == NULL) + { + cmn_err (CE_WARN, "ddi_get_driver_private() failed\n"); + return DDI_SUCCESS; + } + if (!DRIVER_RESUME(osdev)) + return DDI_FAILURE; + + return DDI_SUCCESS; + } +#endif + + instance = ddi_get_instance (dip); + DDB (cmn_err + (CE_CONT, "Attach started " DRIVER_NICK "%d (%s)\n", instance, + ddi_get_name (dip))); + + if ((osdev = + osdev_create (dip, DRIVER_TYPE, instance, DRIVER_NICK, NULL)) == NULL) + { + return DDI_FAILURE; + } + + oss_load_options (osdev, oss_global_options); + oss_load_options (osdev, local_driver_options); + +#if DRIVER_TYPE==DRV_PCI || DRIVER_TYPE == DRV_ISA + { + int err; + if ((err = + ddi_get_iblock_cookie (dip, 0, &osdev->iblock_cookie)) != DDI_SUCCESS) + { + cmn_err (CE_WARN, "Cannot get iblock cookie (%d)\n", err); + return DDI_FAILURE; + } + } +#else +/* NOTE! The USB driver (actually udi.c) will call usb_get_data() to obtain */ +/* the iblock_cookie. There is no need to do that here. */ + osdev->iblock_cookie = 0; +#endif + + if (!DRIVER_ATTACH (osdev)) + { + cmn_err (CE_WARN, "Attach failed\n"); + osdev_delete (osdev); + return DDI_FAILURE; + } + + ddi_set_driver_private (dip, (caddr_t) osdev); + ddi_report_dev (dip); +#if DRIVER_TYPE != DRV_USB + oss_audio_delayed_attach (); +#endif + + DDB (cmn_err (CE_CONT, "Attach done " DRIVER_NICK "%d\n", instance)); + + return DDI_SUCCESS; +} + +static int +ossdrv_detach (dev_info_t * dip, ddi_detach_cmd_t cmd) +{ + oss_device_t *osdev; + +#ifdef OSS_SUSPEND_RESUME + if (cmd != DDI_SUSPEND) /* Allow suspend */ +#endif + if (cmd != DDI_DETACH) + { + cmn_err (CE_WARN, "bad detach cmd %d\n", cmd); + return 0; + } + + if (dip == NULL) + { + cmn_err (CE_WARN, "ossdrv_detach: dip==NULL\n"); + return DDI_FAILURE; + } +//cmn_err(CE_CONT, "Detach started " DRIVER_NICK "\n"); + + DDB (cmn_err + (CE_CONT, "Detach started " DRIVER_NICK "(%s)\n", ddi_get_name (dip))); + if ((osdev = (oss_device_t *) ddi_get_driver_private (dip)) == NULL) + { + cmn_err (CE_WARN, "ddi_get_driver_private() failed\n"); + return DDI_SUCCESS; + } + + if (osdev==NULL) + { + cmn_err (CE_WARN, "Driver osdev==NULL - cannot detach\n"); + return DDI_FAILURE; + } + +#ifdef OSS_SUSPEND_RESUME + if (cmd == DDI_SUSPEND) + { + if (!DRIVER_SUSPEND(osdev)) + return DDI_FAILURE; + return DDI_SUCCESS; + } +#endif + + if (DRIVER_DETACH (osdev) <= 0) + { + cmn_err (CE_WARN, "Driver busy - cannot detach\n"); + return DDI_FAILURE; + } + +#if DRIVER_TYPE!=DRV_VMIX + osdev_delete (osdev); +#endif + + DDB (cmn_err (CE_CONT, "Detach done " DRIVER_NICK "\n")); + return DDI_SUCCESS; +} + +#ifdef OSS_POWER_MANAGE +static int +ossdrv_power (dev_info_t *dip, int component, int level) +{ + oss_device_t *osdev; + + if (dip == NULL) + { + cmn_err (CE_WARN, "ossdrv_detach: dip==NULL\n"); + return DDI_FAILURE; + } + + DDB (cmn_err + (CE_CONT, "ossdrv_power " DRIVER_NICK "(%s, %d, %d)\n", ddi_get_name (dip), component, level)); + if ((osdev = (oss_device_t *) ddi_get_driver_private (dip)) == NULL) + { + cmn_err (CE_WARN, "ddi_get_driver_private() failed\n"); + return DDI_SUCCESS; + } + + if (osdev==NULL) + { + cmn_err (CE_WARN, "Driver osdev==NULL - cannot detach\n"); + return DDI_FAILURE; + } + + if (!DRIVER_POWER(osdev, component, level)) + return DDI_FAILURE; + + return DDI_SUCCESS; +} +#endif diff --git a/kernel/OS/SunOS/os_solaris.c b/kernel/OS/SunOS/os_solaris.c new file mode 100644 index 0000000..b4ceac3 --- /dev/null +++ b/kernel/OS/SunOS/os_solaris.c @@ -0,0 +1,2401 @@ +/* + * Purpose: Operating system abstraction functions for Solaris + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#include "oss_config.h" +#include "midi_core.h" +#include <oss_pci.h> +#if !defined(SOL9) && !defined(SOL8) +#include <sys/sunldi.h> +#endif +#include <sys/mman.h> + +#if 1 +/* + * Some older DDI routines are used by OSS instead of the latest ones. + * In this way the same OSS binary can be run both under Sol10 and Sol11. + */ +uint16_t ddi_mem_get16 (ddi_acc_handle_t handle, uint16_t * host_addr); +uint32_t ddi_mem_get32 (ddi_acc_handle_t handle, uint32_t * host_addr); +void ddi_mem_put16 (ddi_acc_handle_t handle, uint16_t * dev_addr, + uint16_t value); +void ddi_mem_put32 (ddi_acc_handle_t handle, uint32_t * dev_addr, + uint32_t value); +int ddi_mem_alloc (dev_info_t * dip, ddi_dma_lim_t * limits, uint_t length, + uint_t flags, caddr_t * kaddrp, uint_t * real_length); +void ddi_mem_free (caddr_t kaddr); +#endif + +#if 0 +/* TODO: Disable this debugging stuff */ +unsigned char tmp_status = 0; +# define UP_STATUS(v) OUTB(NULL, (tmp_status=tmp_status|(v)), 0x378) +# define DOWN_STATUS(v) OUTB(NULL, (tmp_status=tmp_status&~(v)), 0x378) +#else +# define UP_STATUS(v) +# define DOWN_STATUS(v) +#endif + +static int oss_expired = 0; + +static oss_mutex_t osscore_mutex; + +#define TRC(x) +#define TRC2(x) + +#define FIX_RET_VALUE(ret) (((ret)<0) ? -(ret) : 0) + +static volatile int open_devices = 0; +static volatile int do_forceload = 0; +static volatile int forceload_in_progress = 0; + +/* + * MAX_CARDS must be larger than life. The system will panic if there are + * more sound devices (cards os sound chips) than MAX_CARDS. + */ +#define MAX_CARDS 32 + +static oss_device_t *cards[MAX_CARDS]; +int oss_num_cards = 0; + +int oss_detach_enabled = 0; + +#ifdef MUTEX_CHECKS +static volatile int inside_intr = 0; /* For mutex debugging only */ +#endif + +/* + * These are the entry points into our driver that are called when the + * driver is loaded, during a system call, or in response to an interrupt. + */ +static int oss_getinfo (dev_info_t * dip, ddi_info_cmd_t infocmd, void *arg, + void **result); +static int oss_attach (dev_info_t * dip, ddi_attach_cmd_t cmd); +static int oss_detach (dev_info_t * dip, ddi_detach_cmd_t cmd); +static void free_all_contig_memory (void); + +/* + * DMA memory management + */ + +typedef struct contig_desc +{ + int is_special; + struct contig_desc *next; + unsigned char *first_addr, *last_addr; + unsigned long physaddr; + void *orig_buf; + + oss_device_t *osdev; + ddi_dma_handle_t handle; + ddi_dma_handle_t dhandle; + ddi_acc_handle_t dma_acc_handle; + ddi_dma_cookie_t cookie; + ddi_dma_win_t win; + ddi_dma_seg_t seg; + size_t size; +} +contig_desc; + +static contig_desc *contig_list = NULL; + +#ifdef MEMDEBUG +typedef struct +{ + void *addr; + int size; + char file[40]; + int line; +} mem_block_t; + +#define MAX_MEMBLOCKS 1024 + +static mem_block_t memblocks[MAX_MEMBLOCKS]; +static int num_memblocks = 0; +#endif + +#define CDEV_NUMHASH 79 // Prime number +static oss_cdev_t *cdev_hash[CDEV_NUMHASH] = {NULL}; +#define compute_cdev_hash(dev_class, instance) (dev_class*13+instance) % CDEV_NUMHASH + +static void +unlink_cdev_hash(oss_cdev_t *this_cdev) +{ + oss_cdev_t *cdev = cdev_hash[compute_cdev_hash(this_cdev->dev_class, this_cdev->instance)]; + + if (cdev == this_cdev) // First in the hash chain + { + cdev_hash[compute_cdev_hash(this_cdev->dev_class, this_cdev->instance)] = cdev->hl; + return; + } + + while (cdev != NULL) + { + if (cdev->hl == this_cdev) + { + cdev->hl = this_cdev->hl; + return; + } + + cdev = cdev->hl; + } + + cmn_err(CE_WARN, "unlink_cdev_hash: Cannot find cdev %p\n", this_cdev); +} + +#if 1 +/* + * Unfortunately this handy function is not safe (incompatibilities + * between kenel versions/builds). So let's hope it gets moved to + * the kernel. + */ +char * +oss_get_procname (void) /* Return the command name of the current thread */ +{ + return ttoproc (curthread)->p_user.u_comm; +} +#endif + +#ifdef MEMDEBUG +void * +oss_kmem_alloc (size_t size, int flags, char *file, int line) +#else +void * +oss_kmem_alloc (size_t size, int flags) +#endif +{ +/* + * This routine allocates a memory block and stores length of it in + * the beginning. This length information can be used when later unallocating + * the memory. + */ + + char *ptr; + uint64_t *len; + + ptr = kmem_zalloc (size + sizeof (uint64_t), flags); + + if (ptr == NULL) + return NULL; + + len = (uint64_t *) ptr; + + ptr += sizeof (uint64_t); + *len = size + sizeof (uint64_t); + +#ifdef MEMDEBUG +#if 1 + { + int i; + for (i = 0; i < num_memblocks; i++) + if (memblocks[i].addr == NULL) + { + memblocks[i].addr = ptr; + memblocks[i].size = size; + strncpy (memblocks[i].file, file, 39); + memblocks[i].line = line; + return ptr; + } + } +#endif + + if (num_memblocks < MAX_MEMBLOCKS) + { + memblocks[num_memblocks].addr = ptr; + memblocks[num_memblocks].size = size; + strncpy (memblocks[num_memblocks].file, file, 39); + memblocks[num_memblocks].line = line; + num_memblocks++; + } +#endif + + return ptr; +} + +void +oss_kmem_free (void *addr) +{ + uint64_t *len; + + char *ptr = addr; + + if (addr == NULL) + return; + + ptr -= sizeof (uint64_t); + + len = (uint64_t *) ptr; + + kmem_free (ptr, *len); +#ifdef MEMDEBUG + { + int i; + + for (i = 0; i < num_memblocks; i++) + if (addr == memblocks[i].addr) + { + memblocks[i].addr = NULL; + break; + } + } +#endif +} + +static const ddi_dma_attr_t dma_attr_pci = { + DMA_ATTR_V0, // Version + 0x00000000ULL, // Address low + 0xfffffff0ULL, // Address high + 0xffffffffULL, // Counter max + 1ULL, // Default byte align + 0x7f, // Burst size + 0x1, // Minimum xfer size + 0xffffffffULL, // Maximum xfer size + 0xffffffffULL, // Max segment size + 1, // S/G list length + 1, // Granularity + 0 // Flag +}; + +#if !defined(SOL9) && !defined(SOL8) +static void +forceload_drivers (dev_t dev, cred_t * credp) +{ +/* + * This routine will be called whenever the first application tries + * to access OSS devices. It's purpose is to load all the drivers + * just in case they have not been loaded yet. In this way it's possible to + * guarantee that the audio device numbering doesn't change between + * reboots. + */ + char path[20]; + + int i, err; + ldi_handle_t lh; + ldi_ident_t li; + DDB (cmn_err (CE_NOTE, "Forceloading OSS devices\n")); + + if (ldi_ident_from_dev (dev, &li) != 0) + { + cmn_err (CE_NOTE, "ldi_ident_from_dev failed\n"); + return; + } + + if ((err = ldi_open_by_name ("/dev/sndstat", FWRITE, credp, &lh, li)) != 0) + { + if (err != ENODEV) + cmn_err (CE_NOTE, "Forceload error %d (/dev/sndstat)\n", err); + } + else + err = ldi_close (lh, FWRITE, credp); + + for (i = 0; i < MAX_MIXER_DEV; i++) + { + sprintf (path, "/dev/mixer%d", i); + + if ((err = ldi_open_by_name (path, FWRITE, credp, &lh, li)) != 0) + { + if (err != ENODEV) + cmn_err (CE_NOTE, "Forceload error %d\n", err); + } + else + err = ldi_close (lh, FWRITE, credp); + } + + for (i = 0; i < MAX_AUDIO_DEVFILES; i++) + { + sprintf (path, "/dev/dsp%d", i); + + if ((err = ldi_open_by_name (path, FWRITE, credp, &lh, li)) != 0) + { + if (err != ENODEV) + cmn_err (CE_NOTE, "Forceload error %d\n", err); + } + else + err = ldi_close (lh, FWRITE, credp); + } + + ldi_ident_release (li); + DDB (cmn_err (CE_NOTE, "Forceloading OSS devices done\n")); +} + +void +oss_forceload_drivers (int dev, cred_t * cred_p) +{ + + if (do_forceload) + { + do_forceload = 0; + forceload_in_progress = 1; + forceload_drivers (dev, cred_p); + forceload_in_progress = 0; + } +} +#endif + +/*ARGSUSED*/ +int +oss_open (dev_t * dev_p, int open_flags, int otyp, cred_t * cred_p) +{ + int retval; + dev_t dev = *dev_p; + oss_cdev_t *cdev; + int tmpdev, maj; + oss_native_word flags; + +#ifdef DO_TIMINGS + oss_timing_printf ("**** oss_open(%x) ****", getminor (dev)); +#endif +//cmn_err(CE_CONT, "**** oss_open(%x) ****\n", getminor (dev)); +//cmn_err(CE_CONT, " PID %d, cmd %s\n", GET_PROCESS_PID(x), GET_PROCESS_NAME(x)); + maj = getmajor (dev); + dev = getminor (dev); + +#if !defined(SOL9) && !defined(SOL8) +/* + * Handle driver forceload + */ + + if (forceload_in_progress) + return ENODEV; + + oss_forceload_drivers (dev, cred_p); +#endif + + if (dev >= OSS_MAX_CDEVS) + return ENXIO; + + if (dev >= oss_num_cdevs) + { + return ENODEV; + } + + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d == NULL) + return ENODEV; + + if (cdev->d->open == NULL) + { + return ENODEV; + } + + memset (&cdev->file, 0, sizeof (cdev->file)); + cdev->file.mode = 0; + cdev->file.acc_flags = open_flags; + + if (open_flags & FREAD && open_flags & FWRITE) + cdev->file.mode = OPEN_READWRITE; + else if (open_flags & FREAD) + cdev->file.mode = OPEN_READ; + else if (open_flags & FWRITE) + cdev->file.mode = OPEN_WRITE; + + tmpdev = dev; + retval = + cdev->d->open (cdev->instance, cdev->dev_class, &cdev->file, 0, 0, &tmpdev); + *dev_p = makedevice (maj, tmpdev); + dev = tmpdev; + + if (retval < 0) + { + return -retval; + } + + MUTEX_ENTER_IRQDISABLE (osscore_mutex, flags); + open_devices++; + cdev = oss_cdevs[dev]; /* Switch to the cdev that was actually opened */ + + cdev->open_count++; + if (open_flags & FREAD && open_flags & FWRITE) + cdev->file.mode = OPEN_READWRITE; + else if (open_flags & FREAD) + cdev->file.mode = OPEN_READ; + else if (open_flags & FWRITE) + cdev->file.mode = OPEN_WRITE; + + oss_reserve_device (cdev->osdev); +//cmn_err(CE_CONT, "Increment open_devices=%d, refcount=%d\n", open_devices, cdev->osdev->refcount); + MUTEX_EXIT_IRQRESTORE (osscore_mutex, flags); + + return 0; +} + +/*ARGSUSED*/ +int +oss_close (dev_t dev, int flag, int otyp, cred_t * cred_p) +{ + oss_cdev_t *cdev; + oss_native_word flags; +#ifdef DO_TIMINGS + oss_timing_printf ("***** oss_close(%x) ****", getminor (dev)); +#endif +//cmn_err(CE_CONT, "***** oss_close(%x) ****\n", getminor (dev)); +//cmn_err(CE_CONT, " PID %d, cmd %s\n", GET_PROCESS_PID(x), GET_PROCESS_NAME(x)); + + if (getminor (dev) >= OSS_MAX_CDEVS) + { + cmn_err (CE_NOTE, "Closing bad minor %d\n", getminor (dev)); + return ENXIO; + } + + dev = getminor (dev); + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d == NULL) + { + cmn_err (CE_NOTE, "Closing undefined minor %d\n", getminor (dev)); + return ENXIO; + } + + + if (cdev->open_count == 0) /* Not opened */ + { + cmn_err (CE_NOTE, "Closing minor %d that is not open\n", dev); + return 0; + } + + cdev->d->close (cdev->instance, &cdev->file); + + MUTEX_ENTER_IRQDISABLE (osscore_mutex, flags); + open_devices -= cdev->open_count; +//cmn_err(CE_CONT, "Decrement open_devices=%d\n", open_devices); + oss_unreserve_device (cdev->osdev, cdev->open_count); + cdev->open_count = 0; + MUTEX_EXIT_IRQRESTORE (osscore_mutex, flags); + return 0; +} + +/*ARGSUSED*/ +int +oss_ioctl (dev_t dev, int cmd, intptr_t arg, int mode, cred_t * cred_p, + int *rval_p) +{ + int retval; + int len = 0; + char b[4096], *buf = b; + oss_cdev_t *cdev; +#ifdef DO_TIMINGS + oss_timing_printf ("OSS ioctl(%x, %x, %x)", getminor (dev), cmd, arg); +#endif + + *rval_p = 0; + + dev = getminor (dev); + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d->ioctl == NULL) + return ENXIO; + + if (cdev->open_count == 0) /* Not opened */ + { + cmn_err (CE_NOTE, "Call to ioctl on minor %d that is not open\n", dev); + return ENXIO; + } + + if (oss_expired) + return ENODEV; + + if (mode & FKIOCTL) /* Called from kernel space */ + buf = (char *) arg; + else if (cmd & (SIOC_OUT | SIOC_IN)) + { + + len = (cmd >> 16) & SIOCPARM_MASK; + if (len < 0) + len = 0; + if (len > sizeof (b)) + { + cmn_err (CE_WARN, "Bad ioctl buffer size %d\n", len); + return EFAULT; + } + + if ((cmd & SIOC_IN) && len > 0) + { + if (copyin ((char *) arg, buf, len) == -1) + return EFAULT; + } + + } + + retval = cdev->d->ioctl (cdev->instance, &cdev->file, cmd, (ioctl_arg) buf); + + if (!(mode & FKIOCTL)) /* Not called from kernel space */ + if ((cmd & SIOC_OUT) && len > 0) + { + if (copyout (buf, (char *) arg, len) == -1) + return EFAULT; + } + + return FIX_RET_VALUE (retval); +} + +/*ARGSUSED*/ +int +oss_read (dev_t dev, struct uio *uiop, cred_t * credp) +{ + int count = uiop->uio_resid; + int retval; + oss_cdev_t *cdev; + + dev = getminor (dev); + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d->read == NULL) + return ENXIO; + + if (cdev->open_count == 0) /* Not opened */ + { + cmn_err (CE_NOTE, "Call to read on minor %d that is not open\n", dev); + return ENXIO; + } + + cdev->file.acc_flags = uiop->uio_fmode; + + retval = cdev->d->read (cdev->instance, &cdev->file, uiop, count); + + return FIX_RET_VALUE (retval); +} + +/*ARGSUSED*/ +int +oss_write (dev_t dev, struct uio *uiop, cred_t * cred_p) +{ + int count = uiop->uio_resid; + int retval; + oss_cdev_t *cdev; + + dev = getminor (dev); + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d->write == NULL) + return ENXIO; + + if (cdev->open_count == 0) /* Not opened */ + { + cmn_err (CE_NOTE, "Call to write on minor %d that is not open\n", dev); + return ENXIO; + } + cdev->file.acc_flags = uiop->uio_fmode; + + retval = cdev->d->write (cdev->instance, &cdev->file, uiop, count); + return FIX_RET_VALUE (retval); +} + +int +oss_chpoll (dev_t dev, short events, int anyyet, short *reventsp, + struct pollhead **phpp) +{ + oss_cdev_t *cdev; + oss_poll_event_t ev; + int ret; + +#ifdef DO_TIMINGS + oss_timing_printf ("***** oss_chpoll(%x) ****", getminor (dev)); +#endif + + if (getminor (dev) >= OSS_MAX_CDEVS) + return ENXIO; + + dev = getminor (dev); + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d->chpoll == NULL) + return ENXIO; + + if (cdev->open_count == 0) /* Not opened */ + { + cmn_err (CE_NOTE, "Call to chpoll on minor %d that is not open\n", dev); + return ENXIO; + } + + *reventsp = 0; + ev.events = events; + ev.anyyet = anyyet; + ev.revents = 0; + ev.php = NULL; + + ret = cdev->d->chpoll (cdev->instance, &cdev->file, &ev); + if (ret < 0) + { + return -ret; + } + + *reventsp |= ev.revents; + if (ev.php != NULL) + *phpp = ev.php; + + return 0; +} + +#ifdef ALLOW_BUFFER_MAPPING +int +oss_devmap (dev_t dev, devmap_cookie_t handle, offset_t off, size_t len, + size_t * maplen, uint_t model) +{ + oss_cdev_t *cdev; + oss_poll_event_t ev; + int ret; + +#ifdef DO_TIMINGS + oss_timing_printf ("***** oss_devmap(%x) ****", getminor (dev)); +#endif + + if (getminor (dev) >= OSS_MAX_CDEVS) + { + return ENXIO; + } + + dev = getminor (dev); + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d == NULL) + { + return ENXIO; + } + + if (cdev->open_count == 0) /* Not opened */ + { + cmn_err (CE_NOTE, "Call to devmap on minor %d that is not open\n", dev); + return EPERM; + } + + if (cdev->dev_class != OSS_DEV_DSP && cdev->dev_class != OSS_DEV_DSP_ENGINE) /* Only audio devices can be mapped */ + { + return ENXIO; + } + + dev = cdev->instance; + if (dev < 0 || dev >= num_audio_engines) + return ENXIO; + + return ENOTSUP; +} +#endif + +#if 0 +// Not used in misc modules. +static struct cb_ops oss_cb_ops = { + oss_open, + oss_close, + nodev, /* not a block driver */ + nodev, /* no print routine */ + nodev, /* no dump routine */ + oss_read, + oss_write, + oss_ioctl, +#ifdef ALLOW_BUFFER_MAPPING + oss_devmap, +#else + nodev, /* no devmap routine */ +#endif + nodev, + nodev, /* no segmap routine */ + oss_chpoll, /* no chpoll routine */ + ddi_prop_op, + 0, /* not a STREAMS driver */ + D_NEW | D_MP | D_64BIT, /* safe for multi-thread/multi-processor */ + CB_REV +}; + +static struct dev_ops oss_ops = { + DEVO_REV, /* DEVO_REV indicated by manual */ + 0, /* device reference count */ + oss_getinfo, + nulldev, + nulldev, + oss_attach, + oss_detach, + nodev, /* device reset routine */ + &oss_cb_ops, + NULL, /* bus operations */ + NULL /* TODO: Power management */ +}; +#endif + +extern struct mod_ops mod_miscops; +static struct modldrv modldrv = { + &mod_miscops, + "Open Sound System " OSS_VERSION_STRING " framework" + // &oss_ops, +}; + +static struct modlinkage modlinkage = { + MODREV_1, /* MODREV_1 indicated by manual */ + {(void *) &modldrv, + NULL} /* termination of list of linkage structures */ +}; + +static ddi_device_acc_attr_t acc_attr_neverswap = { + DDI_DEVICE_ATTR_V0, + DDI_NEVERSWAP_ACC, + DDI_STRICTORDER_ACC +}; + +#ifdef OSS_BIG_ENDIAN +static ddi_device_acc_attr_t acc_attr_le_swap = { + DDI_DEVICE_ATTR_V0, + DDI_STRUCTURE_LE_ACC, + DDI_STRICTORDER_ACC +}; +#endif + + +/*ARGSUSED*/ +static ddi_device_acc_attr_t * +get_acc_attr (oss_device_t * osdev) +{ +#ifdef OSS_BIG_ENDIAN + if (osdev->swap_mode == 1) + { + return &acc_attr_le_swap; + } + else +#endif + { + return &acc_attr_neverswap; + } +} + +void +oss_load_options (oss_device_t * osdev, oss_option_map_t map[]) +{ + int i, val; + + for (i = 0; map[i].name != NULL; i++) + { + if ((val = + ddi_prop_get_int (DDI_DEV_T_ANY, osdev->dip, + DDI_PROP_NOTPROM, + map[i].name, -12345)) != -12345) + { + *map[i].ptr = val; + } + } +} + +static int +oss_attach (dev_info_t * dip, ddi_attach_cmd_t cmd) +{ + oss_device_t *osdev; + + if (cmd != DDI_ATTACH) + { + cmn_err (CE_WARN, "oss_attach: Command %x\n", cmd); + return (DDI_FAILURE); + } + + if ((osdev = osdev_create (dip, DRV_VIRTUAL, 0, "osscore", NULL)) == NULL) + { + cmn_err (CE_WARN, "Creating osdev failed\n"); + return DDI_FAILURE; + } + + MUTEX_INIT (osdev, osscore_mutex, 0); + + oss_load_options (osdev, oss_global_options); + + oss_common_init (osdev); + + ddi_report_dev (dip); + oss_register_device (osdev, "OSS core services"); + + return (DDI_SUCCESS); +} + +/* + * This is a pretty generic getinfo routine as describe in the manual. + */ + +/*ARGSUSED*/ +static int +oss_getinfo (dev_info_t * dip, ddi_info_cmd_t infocmd, void *arg, + void **result) +{ + dev_t dev; + register int error; + int instance; + + dev = (dev_t) arg; + instance = getminor (dev) >> 4; + + switch (infocmd) + { + case DDI_INFO_DEVT2DEVINFO: + *result = dip; + error = DDI_SUCCESS; + break; + case DDI_INFO_DEVT2INSTANCE: + *result = (void *) (uintptr_t) instance; + error = DDI_SUCCESS; + break; + default: + *result = NULL; + error = DDI_FAILURE; + } + +#if 0 + DDB (cmn_err (CE_CONT, + "oss_getinfo: returns %d, result=%x minor=%d instance=%d dev=%x\n", + error, *result, minor_num, instance, dev)); +#endif + return (error); +} + +/* + * _init, _info, and _fini support loading and unloading the driver. + */ +int +_init (void) +{ + register int error = 0; + error = mod_install (&modlinkage); + return error; +} + +int +_info (struct modinfo *modinfop) +{ + return (mod_info (&modlinkage, modinfop)); +} + +int +_fini (void) +{ + int status; + +#ifdef MEMDEBUG + int i; +#endif + + if ((status = mod_remove (&modlinkage)) != 0) + return (status); + + free_all_contig_memory (); + +#ifdef MEMDEBUG + if (num_memblocks >= MAX_MEMBLOCKS) + cmn_err (CE_NOTE, "All memory allocations were not checked\n"); + + for (i = 0; i < num_memblocks; i++) + { + if (memblocks[i].addr != NULL) + { + cmn_err (CE_NOTE, "Memory leak in %s:%d\n", + memblocks[i].file, memblocks[i].line); + } + } +#endif + + return status; +} + +/* + * When the osscore module is unloaded, oss_detach cleans up and frees the + * resources we allocated in oss_attach. + */ +static int +oss_detach (dev_info_t * dip, ddi_detach_cmd_t cmd) +{ + static int already_unloaded = 0; + + if (cmd != DDI_DETACH) + return DDI_FAILURE; + + if (already_unloaded) + return DDI_SUCCESS; + already_unloaded = 1; +//cmn_err(CE_CONT, "Oss detach\n"); + + /* instance = ddi_get_instance (dip); */ + + /* + * Remove the minor nodes created in attach + */ + ddi_remove_minor_node (dip, NULL); + + oss_unload_drivers (); + + MUTEX_CLEANUP (osscore_mutex); + + return (DDI_SUCCESS); +} + +/* + * Some support routines + */ +void * +memset (void *t, int c, size_t l) +{ + int i; + + for (i = 0; i < l; i++) + ((char *) t)[i] = c; + + return t; +} + +#ifdef sparc +/* + * I/O functions that do byte swapping (for Sparc) + */ +void +oss_put16 (ddi_acc_handle_t handle, unsigned short *addr, unsigned short val) +{ + val = ((val >> 8) & 0xff) | ((val & 0xff) << 8); + ddi_put16 (handle, addr, val); +} + +void +oss_put32 (ddi_acc_handle_t handle, unsigned int *addr, unsigned int val) +{ +#ifdef OSS_BIG_ENDIAN + val = ((val & 0x000000ff) << 24) | + ((val & 0x0000ff00) << 8) | + ((val & 0x00ff0000) >> 8) | ((val & 0xff000000) >> 24); +#endif + ddi_put32 (handle, addr, val); +} + +unsigned short +oss_get16 (ddi_acc_handle_t handle, unsigned short *addr) +{ + unsigned short val; + + val = ddi_get16 (handle, addr); +#ifdef OSS_BIG_ENDIAN + val = ((val >> 8) & 0xff) | ((val & 0xff) << 8); +#endif + return val; +} + +unsigned int +oss_get32 (ddi_acc_handle_t handle, unsigned int *addr) +{ + unsigned int val; + + val = ddi_get32 (handle, addr); +#ifdef OSS_BIG_ENDIAN + val = ((val & 0x000000ff) << 24) | + ((val & 0x0000ff00) << 8) | + ((val & 0x00ff0000) >> 8) | ((val & 0xff000000) >> 24); +#endif + return val; +} + +uint16_t +oss_mem_get16 (ddi_acc_handle_t handle, uint16_t * addr) +{ + uint16_t val; + + val = ddi_mem_get16 (handle, addr); +#ifdef OSS_BIG_ENDIAN + val = ((val >> 8) & 0xff) | ((val & 0xff) << 8); +#endif + return val; +} + +uint32_t +oss_mem_get32 (ddi_acc_handle_t handle, uint32_t * addr) +{ + + uint32_t val; + + val = ddi_mem_get32 (handle, addr); +#ifdef OSS_BIG_ENDIAN + val = ((val & 0x000000ff) << 24) | + ((val & 0x0000ff00) << 8) | + ((val & 0x00ff0000) >> 8) | ((val & 0xff000000) >> 24); +#endif + return val; +} + +void +oss_mem_put16 (ddi_acc_handle_t handle, uint16_t * addr, uint16_t val) +{ +#ifdef OSS_BIG_ENDIAN + val = ((val >> 8) & 0xff) | ((val & 0xff) << 8); +#endif + + ddi_mem_put16 (handle, addr, val); +} + +void +oss_mem_put32 (ddi_acc_handle_t handle, uint32_t * addr, uint32_t val) +{ +#ifdef OSS_BIG_ENDIAN + val = ((val & 0x000000ff) << 24) | + ((val & 0x0000ff00) << 8) | + ((val & 0x00ff0000) >> 8) | ((val & 0xff000000) >> 24); +#endif + ddi_mem_put32 (handle, addr, val); +} +#endif + +void +oss_pci_byteswap (oss_device_t * osdev, int mode) +{ + osdev->swap_mode = mode; +} + +void +oss_pcie_init (oss_device_t * osdev, int flags) +{ + /* TODO: Should we do something? */ +} + +/*ARGSUSED*/ +caddr_t +oss_map_pci_ioaddr (oss_device_t * osdev, int nr, int io) +{ + caddr_t addr; + off_t region_size; + int err; + ddi_device_acc_attr_t *acc_attr; + + if (nr >= OSS_MAX_ACC_HANDLE) + { + cmn_err(CE_WARN, "Too large I/O region number %d\n", nr); + + return 0; + } + + acc_attr = get_acc_attr (osdev); + + if ((err = + ddi_dev_regsize (osdev->dip, nr + 1, ®ion_size)) != DDI_SUCCESS) + { + cmn_err (CE_WARN, "Getting device regsize failed (%d)\n", err); + return 0; + } + + if ((err = ddi_regs_map_setup + (osdev->dip, nr + 1, &addr, 0, region_size, acc_attr, + &osdev->acc_handle[nr])) != DDI_SUCCESS) + { + cmn_err (CE_WARN, "Register setup failed (%d)\n", err); + return 0; + } + + return addr; +} + +void +oss_unmap_pci_ioaddr(oss_device_t * osdev, int nr) +{ + if (nr >= OSS_MAX_ACC_HANDLE) + { + cmn_err(CE_WARN, "Too large I/O region number %d\n", nr); + return; + } + + ddi_regs_map_free(&osdev->acc_handle[nr]); +} + +int +pci_read_config_byte (oss_device_t * osdev, offset_t where, + unsigned char *val) +{ + if (osdev->dev_type != DRV_PCI) + return PCIBIOS_FAILED; +#if defined (sparc) + if (where == PCI_INTERRUPT_LINE) + *val = 7; /* PC emulation hack */ + else +#endif + *val = pci_config_get8 (osdev->pci_config_handle, where); + return PCIBIOS_SUCCESSFUL; +} + +int +pci_read_config_irq (oss_device_t * osdev, offset_t where, unsigned char *val) +{ + int ret; + + if (osdev->dev_type != DRV_PCI) + return PCIBIOS_FAILED; + ret = pci_read_config_byte (osdev, where, val); + return ret; +} + + +int +pci_read_config_word (oss_device_t * osdev, offset_t where, + unsigned short *val) +{ + if (osdev->dev_type != DRV_PCI) + return PCIBIOS_FAILED; + *val = pci_config_get16 (osdev->pci_config_handle, where); + return PCIBIOS_SUCCESSFUL; +} + +int +pci_read_config_dword (oss_device_t * osdev, offset_t where, + unsigned int *val) +{ + if (osdev->dev_type != DRV_PCI) + return PCIBIOS_FAILED; + *val = pci_config_get32 (osdev->pci_config_handle, where); + return PCIBIOS_SUCCESSFUL; +} + +int +pci_write_config_byte (oss_device_t * osdev, offset_t where, + unsigned char val) +{ + if (osdev->dev_type != DRV_PCI) + return PCIBIOS_FAILED; + pci_config_put8 (osdev->pci_config_handle, where, val); + return PCIBIOS_SUCCESSFUL; +} + +int +pci_write_config_word (oss_device_t * osdev, offset_t where, + unsigned short val) +{ + if (osdev->dev_type != DRV_PCI) + return PCIBIOS_FAILED; + pci_config_put16 (osdev->pci_config_handle, where, val); + return PCIBIOS_SUCCESSFUL; +} + +int +pci_write_config_dword (oss_device_t * osdev, offset_t where, + unsigned int val) +{ + if (osdev->dev_type != DRV_PCI) + return PCIBIOS_FAILED; + pci_config_put32 (osdev->pci_config_handle, where, val); + return PCIBIOS_SUCCESSFUL; +} + +void * +memcpy (void *t, const void *s, size_t l) +{ + bcopy (s, t, l); + return t; +} + +#ifdef MEMDEBUG +void * +oss_contig_malloc (oss_device_t * osdev, int size, oss_uint64_t memlimit, + oss_native_word * phaddr, oss_dma_handle_t *dma_handle, char *file, int line) +#else +void * +oss_contig_malloc (oss_device_t * osdev, int size, oss_uint64_t memlimit, + oss_native_word * phaddr, oss_dma_handle_t *dma_handle) +#endif +{ +/* + * Allocate physically contiguous memory (suitable for DMA). + * + * The memlimit parameter is equal to oss_alloc_dmabuf(). + */ + int err; +#if defined(sparc) + uint_t len; +#else + size_t len; +#endif + uint_t count; + contig_desc *desc; + ddi_dma_attr_t dma_attr; + ddi_device_acc_attr_t *acc_attr; + + int flags = + DDI_DMA_REDZONE | DDI_DMA_CONSISTENT | DDI_DMA_READ | DDI_DMA_WRITE; + + if (osdev == NULL) + { + cmn_err (CE_WARN, "oss_contig_malloc: osdev==NULL\n"); + return NULL; + } + + acc_attr = get_acc_attr (osdev); + + memcpy (&dma_attr, &dma_attr_pci, sizeof (ddi_dma_attr_t)); + dma_attr.dma_attr_addr_hi = memlimit; + + desc = KERNEL_MALLOC (sizeof (contig_desc)); + if (desc == NULL) + { + cmn_err (CE_WARN, "Failed to allocate contig buffer descriptor\n"); + return NULL; + } + + desc->osdev = osdev; + desc->next = NULL; + desc->is_special = 0; + + if ((err = ddi_dma_alloc_handle (osdev->dip, + &dma_attr, + DDI_DMA_SLEEP, + NULL, &desc->dhandle)) != DDI_SUCCESS) + { + cmn_err (CE_WARN, "Failed to allocate pci DMA handle (%d)\n", err); + return NULL; + } + + if (dma_handle != NULL) + *dma_handle = desc->dhandle; + +#ifndef IOMEM_DATA_UNCACHED +#define IOMEM_DATA_UNCACHED 0 // Fix for Solaris 10 +#endif + +#if defined(sparc) + if ((err = ddi_mem_alloc (osdev->dip, + NULL, + size + 4096, + 0, + (caddr_t *) & desc->first_addr, + (uint_t *) & len)) != DDI_SUCCESS) + + +#else + if ((err = ddi_dma_mem_alloc (desc->dhandle, + size + 4096, + acc_attr, + flags, + DDI_DMA_SLEEP, + NULL, + (caddr_t *) & desc->first_addr, + (size_t *) & len, + &desc->dma_acc_handle)) != DDI_SUCCESS) +#endif + { + cmn_err (CE_WARN, "Failed to allocate %d bytes of contig memory (%d)\n", + size, err); + return NULL; + + } + + desc->size = len; + desc->orig_buf = desc->first_addr; + + if (desc->first_addr == NULL) + { + cmn_err (CE_WARN, "Can't allocate a contig buffer\n"); + return NULL; + } + + DDB (cmn_err + (CE_CONT, "Allocated contig memory, addr=%x, len=%d\n", + (unsigned int) desc->first_addr, desc->size)); + + if ((err = ddi_dma_addr_bind_handle (desc->dhandle, + NULL, + (char *) desc->first_addr, + desc->size, + flags | DDI_DMA_STREAMING, + DDI_DMA_DONTWAIT, + NULL, + &desc->cookie, &count)) != DDI_SUCCESS) + { + cmn_err (CE_WARN, "Contig address setup failed (%d)\n", err); + return NULL; + } + + desc->physaddr = desc->cookie.dmac_address; + + desc->last_addr = desc->first_addr + desc->size - 1; + desc->first_addr = + (void *) (((unsigned long) desc->first_addr + 4095) & ~4095); + desc->physaddr = (desc->physaddr + 4095) & ~4095; + *phaddr = desc->physaddr; + + desc->next = contig_list; + contig_list = desc; +/* HW_PRINTF(("Alloc contig: %x-%x, ph=%x\n", desc->first_addr, desc->last_addr, desc->physaddr)); */ + + return desc->first_addr; +} + +/*ARGSUSED*/ +void +oss_contig_free (oss_device_t * osdev, void *p, int sz) +{ + int err; + contig_desc *d, *desc = NULL, *prev = NULL; + + if (p == NULL) + return; + + d = contig_list; + + while (d && desc == NULL) + { + if (d->is_special) + { + prev = d; + d = d->next; + continue; + } + + if (d->first_addr == p) + { + desc = d; + break; + } + + prev = d; + d = d->next; + } + + if (desc == NULL) + { + cmn_err (CE_WARN, "OSS: Can't free memory\n"); + return; + } + + if ((err = ddi_dma_unbind_handle (desc->dhandle)) != DDI_SUCCESS) + cmn_err (CE_WARN, "Failed to free DMA handle (%d)\n", err); + +#if defined(sparc) + ddi_mem_free (desc->orig_buf); +#else + if (desc->dma_acc_handle == NULL) + cmn_err (CE_WARN, "desc->dma_acc_handle==NULL\n"); + else + ddi_dma_mem_free (&desc->dma_acc_handle); +#endif + ddi_dma_free_handle (&desc->dhandle); + + + if (desc == contig_list) + contig_list = desc->next; + else + { + prev->next = desc->next; + } + KERNEL_FREE (desc); + +} + +static void +free_all_contig_memory (void) +{ + contig_desc *p, *desc = contig_list; + + while (desc != NULL) + { + p = desc; + desc = desc->next; + + if (p->is_special) + continue; + + oss_contig_free (p->osdev, p->orig_buf, 0); + } + + contig_list = NULL; +} + +#if !defined(SOL9) && !defined(DISABLE_FMA) +/*ARGSUSED*/ +static int +oss_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *osdev_) +{ + pci_ereport_post(dip, err, NULL); + return err->fme_status; +} +#endif + +oss_device_t * +osdev_create (dev_info_t * dip, int dev_type, int instance, const char *nick, + const char *handle) +{ + oss_device_t *osdev = NULL; + int i; + ddi_iblock_cookie_t iblk; + static int license_checked=0; + +#ifdef LICENSED_VERSION + if (!license_checked) + { + timestruc_t ts; + + license_checked = 1; + gethrestime (&ts); + if (!oss_license_handle_time (ts.tv_sec)) + { + cmn_err (CE_WARN, "This version of Open Sound System has expired\n"); + cmn_err (CE_CONT, + "Please download the latest version from www.opensound.com\n"); + oss_expired = 1; + } + } +#endif + + if (handle == NULL) + handle = nick; + + /* + * Don't accept any more drivers if expired + */ + if (oss_expired && oss_num_cards > 0) + return NULL; + + /* + * Check if the same device is being reinserted. + */ + for (i = 0; i < oss_num_cards; i++) + { + if (cards[i]->available) /* Not deleted */ + continue; + + if (cards[i]->dip == dip) + { + osdev = cards[i]; + break; + } + } + +#if 1 + if (osdev == NULL) + { + /* + * Check if there are some deleted devices. + */ + for (i = 0; i < oss_num_cards; i++) + { + if (cards[i]->available) /* Not deleted */ + continue; + + osdev = cards[i]; + break; + } + } +#endif + + if (osdev == NULL) + { + for (i=0;i<oss_num_cards;i++) + if (!cards[i]->available) + { + osdev = cards[i]; + break; + } + } + + if (osdev == NULL) + { + if (oss_num_cards >= MAX_CARDS) + { + cmn_err (CE_WARN, "Too many OSS devices. At most %d permitted.\n", + MAX_CARDS); + return NULL; + } + + if ((osdev = PMALLOC (NULL, sizeof (*osdev))) == NULL) + { + cmn_err (CE_WARN, "osdev_create: Out of memory\n"); + return NULL; + } + memset (osdev, 0, sizeof (*osdev)); + + osdev->cardnum = oss_num_cards; + cards[oss_num_cards++] = osdev; + } + + osdev->dip = dip; + osdev->osid = dip; + osdev->available = 1; + osdev->instance = instance; + osdev->dev_type = dev_type; + osdev->devc = NULL; + sprintf (osdev->nick, "%s%d", nick, instance); + strcpy (osdev->modname, nick); + osdev->num_audio_engines = 0; + osdev->num_audioplay = 0; + osdev->num_audiorec = 0; + osdev->num_audioduplex = 0; + osdev->num_mididevs = 0; + osdev->num_mixerdevs = 0; + osdev->first_mixer = -1; + + switch (dev_type) + { + case DRV_PCI: + if (pci_config_setup (dip, &osdev->pci_config_handle) != DDI_SUCCESS) + cmn_err (CE_NOTE, "pci_config_setup() failed\n"); +#if !defined(SOL9) && !defined(DISABLE_FMA) + osdev->fm_capabilities = DDI_FM_EREPORT_CAPABLE | DDI_FM_DMACHK_CAPABLE | + DDI_FM_ERRCB_CAPABLE; + ddi_fm_init(dip, &osdev->fm_capabilities, &iblk); + ddi_fm_handler_register(dip, oss_fm_error_cb, (void*)osdev); + pci_ereport_setup(dip); +#endif + break; + + case DRV_VIRTUAL: + case DRV_VMIX: + case DRV_STREAMS: +#if !defined(SOL9) && !defined(DISABLE_FMA) + osdev->fm_capabilities=DDI_FM_EREPORT_CAPABLE; + ddi_fm_init(dip, &osdev->fm_capabilities, &iblk); +#endif + break; + + case DRV_USB: + /* NOP */ + break; + + default: + cmn_err (CE_WARN, "Bad device type\n"); + return NULL; + } + +/* + * Create the device handle + */ + switch (dev_type) + { + case DRV_PCI: + { + unsigned int subvendor; + pci_read_config_dword (osdev, 0x2c, &subvendor); + + sprintf (osdev->handle, "PCI%08x-%d", subvendor, instance); + } + break; + + case DRV_USB: + /* TODO: Get the vendor information */ + sprintf (osdev->handle, "USB-%s%d", handle, instance); + break; + + default: + sprintf (osdev->handle, "%s%d", handle, instance); + } + + return osdev; +} + +oss_device_t * +osdev_clone (oss_device_t * orig_osdev, int new_instance) +{ + oss_device_t *osdev; + + osdev = PMALLOC (NULL, sizeof (*osdev)); + if (osdev == NULL) + { + cmn_err (CE_WARN, "osdev_create: Out of memory\n"); + return NULL; + } + memcpy (osdev, orig_osdev, sizeof (*osdev)); + osdev->dev_type = DRV_CLONE; + osdev->instance = new_instance; + sprintf (osdev->nick, "%s%d", orig_osdev->modname, new_instance); + sprintf (osdev->handle, "%s%d", orig_osdev->modname, new_instance); + + return osdev; +} + +void +osdev_delete (oss_device_t * osdev) +{ + int i; + + if (osdev == NULL) + return; + + if (!osdev->available) /* Already deleted */ + return; + + osdev->available = 0; + + switch (osdev->dev_type) + { + case DRV_PCI: +#if !defined(SOL9) && !defined(DISABLE_FMA) + ddi_fm_handler_unregister(osdev->dip); + pci_ereport_teardown(osdev->dip); +#endif + pci_config_teardown (&osdev->pci_config_handle); +#if !defined(SOL9) && !defined(DISABLE_FMA) + ddi_fm_fini(osdev->dip); +#endif + osdev->pci_config_handle = NULL; + break; + + case DRV_VIRTUAL: + case DRV_VMIX: + case DRV_STREAMS: +#if !defined(SOL9) && !defined(DISABLE_FMA) + ddi_fm_fini(osdev->dip); +#endif + break; + } + + ddi_remove_minor_node (osdev->dip, NULL); + +/* + * Mark all minor nodes for this module as invalid. + */ + for (i = 0; i < oss_num_cdevs; i++) + if (oss_cdevs[i] != NULL) + if (oss_cdevs[i]->osdev == osdev) + { + unlink_cdev_hash(oss_cdevs[i]); + oss_cdevs[i]->d = NULL; + oss_cdevs[i]->osdev = NULL; + oss_cdevs[i]->active = 0; /* Device removed */ + } +} + +int +oss_get_cardinfo (int cardnum, oss_card_info * ci) +{ +/* + * Print information about a 'card' in a format suitable for /dev/sndstat + */ + + if (cardnum < 0 || cardnum >= oss_num_cards) + return OSS_ENXIO; + + if (cards[cardnum]->name != NULL) + strncpy (ci->longname, cards[cardnum]->name, 128); + ci->shortname[127] = 0; + + if (cards[cardnum]->nick != NULL) + strncpy (ci->shortname, cards[cardnum]->nick, 16); + ci->shortname[15] = 0; + + if (cards[cardnum]->hw_info != NULL) + strncpy (ci->hw_info, cards[cardnum]->hw_info, sizeof (ci->hw_info) - 1); + ci->hw_info[sizeof (ci->hw_info) - 1] = 0; + ci->intr_count = cards[cardnum]->intrcount; + ci->ack_count = cards[cardnum]->ackcount; + + return 0; +} + +static int +grow_array(oss_device_t *osdev, oss_cdev_t ***arr, int *size, int element_size, int increment) +{ + oss_cdev_t **old=*arr, **new = *arr; + int old_size = *size; + int new_size = *size; + + new_size += increment; + + if ((new=PMALLOC(osdev, new_size * element_size))==NULL) + return 0; + + memset(new, 0, new_size * element_size); + if (old != NULL) + memcpy(new, old, old_size * element_size); + + *size = new_size; + *arr = new; + + if (old != NULL) + PMFREE(osdev, old); + + return 1; +} + +/*ARGSUSED*/ +void +oss_install_chrdev (oss_device_t * osdev, char *name, int dev_class, + int instance, oss_cdev_drv_t * drv, int flags) +{ +/* + * oss_install_chrdev creates a character device (minor). However if + * name==NULL the device will not be exported (made visible to userland + * clients). + */ + + int num; + int hash_link; + oss_cdev_t *cdev = NULL; + + if (dev_class != OSS_DEV_STATUS) + if (oss_expired && instance > 0) + return; +/* + * Find if this dev_class&instance already exists (after previous module + * detach). + */ + + if (flags & CHDEV_REPLUG) + for (num = 0; num < oss_num_cdevs; num++) + if (oss_cdevs[num]->d == NULL) /* Unloaded driver */ + if (oss_cdevs[num]->dev_class == dev_class + && oss_cdevs[num]->instance == instance) + { + cdev = oss_cdevs[num]; + break; + } + + if (cdev == NULL) + { + if (oss_num_cdevs >= OSS_MAX_CDEVS) + { + if (!grow_array(osdev, &oss_cdevs, &oss_max_cdevs, sizeof(oss_cdev_t*), 100)) + { + cmn_err (CE_WARN, "Cannot allocate new minor numbers.\n"); + return; + } + } + + if ((cdev = PMALLOC (NULL, sizeof (*cdev))) == NULL) + { + cmn_err (CE_WARN, "Cannot allocate character device desc.\n"); + return; + } + num = oss_num_cdevs++; + } + + memset (cdev, 0, sizeof (*cdev)); + cdev->dev_class = dev_class; + cdev->instance = instance; + cdev->d = drv; + cdev->active = 1; + cdev->osdev = osdev; + if (name != NULL) + strncpy (cdev->name, name, sizeof (cdev->name) - 1); + else + strcpy (cdev->name, "NONE"); + cdev->name[sizeof (cdev->name) - 1] = 0; + oss_cdevs[num] = cdev; + + cdev->minor = num; + + /* + * Add to the cdev_hash list. + */ + hash_link = compute_cdev_hash (dev_class, instance); + cdev->hl = cdev_hash[hash_link]; + cdev_hash[hash_link] = cdev; + +/* + * Export the device only if name != NULL + */ + if (name != NULL) + { + char tmp[64], *s; + char *dev_type = "oss_sysdev"; + +/* + * Convert "oss/device/node" style names to the + * "device,node" style naming required by Solaris. + */ + if (name[0] == 'o' && name[3] == '/') /* oss/ prefix */ + { + strcpy (tmp, name + 4); + name = tmp; + + s = tmp; + while (*s) + { + if (*s == '/') + *s = ','; + s++; + } + dev_type = "oss_audio"; + } + + if (ddi_create_minor_node (osdev->dip, name, S_IFCHR, num, + dev_type, 0) == DDI_FAILURE) + { + cmn_err (CE_WARN, "ddi_create_minor_node failed\n"); + } + } +} + +int +oss_find_minor (int dev_class, int instance) +{ + oss_cdev_t *cdev; + + cdev = cdev_hash[compute_cdev_hash(dev_class, instance)]; + + while (cdev != NULL) + { + if (cdev->d != NULL && cdev->dev_class == dev_class + && cdev->instance == instance) + return cdev->minor; + cdev = cdev->hl; /* Next in the hash chain */ + } + + return OSS_EIO; +} + +int +__oss_alloc_dmabuf (int dev, dmap_p dmap, unsigned int alloc_flags, + oss_uint64_t maxaddr, int direction) +{ + int err; +#if defined(sparc) + uint_t len; +#else + size_t len; +#endif + uint_t ncookies; + contig_desc *desc; + oss_device_t *osdev = dmap->osdev; + ddi_dma_attr_t dma_attr; + int size = 32 * 1024; + extern int dma_buffsize; + ddi_device_acc_attr_t *acc_attr; + + int flags = DDI_DMA_REDZONE | DDI_DMA_CONSISTENT | + (direction == OPEN_READ) ? DDI_DMA_READ : DDI_DMA_WRITE; + + if (osdev == NULL) + { + cmn_err (CE_WARN, "oss_alloc_dmabuf: osdev==NULL\n"); + return OSS_EIO; + } + + acc_attr = get_acc_attr (osdev); + + if (dma_buffsize > 16 && dma_buffsize <= 128) + size = dma_buffsize * 1024; + +/* + * Some applications and virtual drivers need shorter buffer. + */ + if (dmap->flags & DMAP_SMALLBUF) + { + size = SMALL_DMABUF_SIZE; + } + else if (dmap->flags & DMAP_MEDIUMBUF) + { + size = MEDIUM_DMABUF_SIZE; + } + + if (alloc_flags & DMABUF_LARGE) + size = 256 * 1024; + + if ((alloc_flags & DMABUF_SIZE_16BITS) && size > 32 * 1024) + size = 32 * 1024; + + memcpy (&dma_attr, &dma_attr_pci, sizeof (ddi_dma_attr_t)); + dma_attr.dma_attr_addr_hi = maxaddr; + +#ifndef SOL9 + if (osdev->dev_type == DRV_PCI) + dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR; +#endif + + if (dmap->dmabuf != NULL) + return 0; /* Already done */ + + dmap->dma_parms.state = 0; + dmap->dma_parms.enabled = 1; + dmap->dma_parms.ignore = 0; + + if (osdev->dip == NULL) + { + cmn_err (CE_WARN, "oss_alloc_dmabuf: osdev->dip==NULL\n"); + return OSS_EIO; + } + + if (dmap == NULL) + { + cmn_err (CE_WARN, "oss_alloc_dmabuf: dmap==NULL\n"); + return OSS_EIO; + } + + if ((err = ddi_dma_alloc_handle (osdev->dip, + &dma_attr, + DDI_DMA_SLEEP, + NULL, + &dmap->dmabuf_dma_handle)) != DDI_SUCCESS) + { + cmn_err (CE_WARN, "Failed to allocate DMA handle (error %d)\n", err); + return OSS_ENOMEM; + } + + + dmap->dmabuf = NULL; + dmap->buffsize = size; + + err = -1; + + while (err != DDI_SUCCESS && dmap->dmabuf == NULL && dmap->buffsize >= 4096) + { +#if defined(sparc) + if ((err = ddi_mem_alloc (osdev->dip, + NULL, + dmap->buffsize, + 0, + (caddr_t *) & dmap->dmabuf, + (uint_t *) & len)) != DDI_SUCCESS) +#else + if ((err = ddi_dma_mem_alloc (dmap->dmabuf_dma_handle, + dmap->buffsize, + acc_attr, + flags, + DDI_DMA_SLEEP, + NULL, + (caddr_t *) & dmap->dmabuf, + (size_t *) & len, + &dmap->dma_parms.dma_acc_handle)) != + DDI_SUCCESS) +#endif + { + if (!(alloc_flags & DMABUF_QUIET)) + DDB (cmn_err (CE_WARN, + "failed to allocate %d bytes of DMA memory (%d)\n", + dmap->buffsize, err)); + dmap->dmabuf = NULL; + dmap->buffsize /= 2; + } + } + + if (dmap->dmabuf == NULL) + { + cmn_err (CE_WARN, "Can't allocate a DMA buffer for device %d\n", dev); + ddi_dma_free_handle (&dmap->dmabuf_dma_handle); + return OSS_ENOMEM; + } + + DDB (cmn_err (CE_CONT, "Allocated DMA memory, addr=%x, len=%d\n", + (int) dmap->dmabuf, (int) dmap->buffsize)); + + dmap->dma_parms.orig_buf = (caddr_t) dmap->dmabuf; + + if ((err = ddi_dma_addr_bind_handle (dmap->dmabuf_dma_handle, + NULL, + (char *) dmap->dmabuf, + dmap->buffsize, + flags | DDI_DMA_STREAMING, + DDI_DMA_DONTWAIT, + NULL, + &dmap->dma_parms.cookie, + &ncookies)) != DDI_SUCCESS) + { + cmn_err (CE_WARN, "DMA address setup failed (%d)\n", err); + + return OSS_EIO; + } + + dmap->dmabuf = + (unsigned char *) ((((unsigned long) dmap->dmabuf) + 4095) & ~4095); + dmap->dmabuf_phys = (dmap->dma_parms.cookie.dmac_address + 4095) & ~4095; + + desc = PMALLOC (osdev, sizeof (contig_desc)); + if (desc == NULL) + return OSS_ENOMEM; + + desc->osdev = osdev; + desc->next = NULL; + desc->is_special = 1; + desc->first_addr = dmap->dmabuf; + desc->last_addr = dmap->dmabuf + dmap->buffsize - 1; + desc->physaddr = dmap->dmabuf_phys; + desc->orig_buf = dmap->dma_parms.orig_buf; + + if (contig_list != NULL) + desc->next = contig_list; +/* HW_PRINTF(("Alloc DMA: %x-%x, ph=%x\n", desc->first_addr, desc->last_addr, desc->physaddr)); */ + contig_list = desc; + + return 0; +} + +/*ARGSUSED*/ +void +oss_free_dmabuf (int dev, dmap_p dmap) +{ + int err; + + if (dmap->dmabuf == NULL) + return; + + if ((err = ddi_dma_unbind_handle (dmap->dmabuf_dma_handle)) != DDI_SUCCESS) + cmn_err (CE_WARN, "Failed to free DMA handle (%d)\n", err); +#if defined(sparc) + ddi_mem_free (dmap->dma_parms.orig_buf); +#else + ddi_dma_mem_free (&dmap->dma_parms.dma_acc_handle); +#endif + ddi_dma_free_handle (&dmap->dmabuf_dma_handle); + + dmap->dmabuf = NULL; + dmap->dmabuf_phys = 0; +} + +/* + * Interrupt management + */ +static u_int +oss_intr (caddr_t arg) /* Global interrupt handler */ +{ + oss_device_t *osdev = (oss_device_t *) arg; + int serviced; +#ifdef MUTEX_CHECKS + int x = inside_intr; + inside_intr = 1; /* For mutex debugging only */ +#endif + + if (osdev == NULL) + { +#ifdef MUTEX_CHECKS + inside_intr = x; +#endif + return DDI_INTR_UNCLAIMED; + } + osdev->intrcount++; + + UP_STATUS (0x01); + serviced = osdev->tophalf_handler (osdev); + DOWN_STATUS (0x01); + + if (serviced == 0) + return DDI_INTR_UNCLAIMED; + osdev->ackcount++; + + if (osdev->bottomhalf_handler == NULL) + return DDI_INTR_CLAIMED; + +#if 0 + if (osdev->intr_is_hilevel) + { +/* TODO: Schedule the bottom half handler */ + } + else +#endif + osdev->bottomhalf_handler (osdev); + +#ifdef MUTEX_CHECKS + inside_intr = x; +#endif + return DDI_INTR_CLAIMED; +} + +int +oss_register_interrupts (oss_device_t * osdev, int intrnum, + oss_tophalf_handler_t top, + oss_bottomhalf_handler_t bottom) +{ + int err; + ddi_idevice_cookie_t ic; + + + if (intrnum != 0) + { + cmn_err (CE_WARN, "Bad interrupt index (%d) for %s\n", intrnum, + osdev->name); + return OSS_EINVAL; + } + + if (osdev == NULL) + { + cmn_err (CE_WARN, "oss_register_interrupts: Bad osdev\n"); + return OSS_EINVAL; + } + + if (osdev->tophalf_handler != NULL || osdev->bottomhalf_handler != NULL) + { + cmn_err (CE_WARN, "Interrupts already registered for %s\n", + osdev->name); + return OSS_EINVAL; + } + + if (top == NULL) + { + cmn_err (CE_WARN, "Bad interrupt handler for %s\n", osdev->name); + return OSS_EINVAL; + } + + osdev->tophalf_handler = top; + osdev->bottomhalf_handler = bottom; + osdev->intr_is_hilevel = ddi_intr_hilevel (osdev->dip, intrnum); + if (osdev->intr_is_hilevel) + { + if (bottom == NULL) + { + cmn_err (CE_WARN, + "The driver for %s doesn't support hilevel interrupts\n", + osdev->name); + return OSS_EINVAL; + } + + DDB (cmn_err (CE_NOTE, "Using hilevel intr for %s\n", osdev->name)); + + /* TODO: Fix hilevel intr handling */ + cmn_err (CE_WARN, "Hilevel interrupts are not supported yet.\n"); + return OSS_EINVAL; + } + + ddi_get_iblock_cookie (osdev->dip, intrnum, &osdev->iblock_cookie); + + if ((err = ddi_add_intr (osdev->dip, intrnum, NULL, &ic, + oss_intr, (caddr_t) osdev)) != DDI_SUCCESS) + { + cmn_err (CE_WARN, "ddi_add_intr() failed, error=%d\n", err); + return OSS_EIO; + } + + return 0; +} + +void +oss_unregister_interrupts (oss_device_t * osdev) +{ + ddi_remove_intr (osdev->dip, 0, osdev->iblock_cookie); + osdev->tophalf_handler = NULL; + osdev->bottomhalf_handler = NULL; +} + +/*ARGSUSED*/ +int +oss_register_device (oss_device_t * osdev, const char *name) +{ + + if ((osdev->name = PMALLOC (NULL, strlen (name) + 1)) == NULL) + { + cmn_err (CE_WARN, "Cannot allocate memory for device name\n"); + osdev->name = "Unknown device"; + } + strcpy (osdev->name, name); + return 0; +} + +int +oss_disable_device (oss_device_t * osdev) +{ + int i; +/* + * This routine should check if the device is ready to be unloaded (no devices are in use). + * If the device cannot be unloaded this routine must return OSS_EBUSY. + * + * If the device can be unloaded then disable any timers or other features that may cause the + * device to be called. Also mark the audio/midi/mixer/etc devices of this device to be disabled. + * However the interrupt handler should still stay enabled. The low level driver will call + * oss_unregister_interrupts() after it has cleared the interrupt enable register. + */ + if (osdev->refcount > 0 || open_devices > 0) + { + cmn_err (CE_CONT, "Refcount %d (%s) , open_devices %d\n", + osdev->refcount, osdev->nick, open_devices); + if (open_devices > 0) + { + int i; + + for (i = 0; i < oss_num_cdevs; i++) + if (oss_cdevs[i]->open_count) + { + cmn_err (CE_CONT, "%s is opened\n", oss_cdevs[i]->name); + } + } + return OSS_EBUSY; + } +//cmn_err(CE_CONT, "oss_disable_device %s\n", osdev->nick); + +/* + * Now mark all devices unavailable (for the time being) + */ + + for (i = 0; i < num_mixers; i++) + if (mixer_devs[i]->osdev == osdev) + { + mixer_devs[i]->unloaded = 1; + } + + for (i = 0; i < num_mididevs; i++) + { + if (midi_devs[i]->osdev == osdev) + { + midi_devs[i]->unloaded = 1; + } + } + + for (i = 0; i < num_audio_engines; i++) + if (audio_engines[i]->osdev == osdev) + { + audio_uninit_device (i); + } + + return 0; +} + +/*ARGSUSED*/ +void +oss_unregister_device (oss_device_t * osdev) +{ +/* + * Notice! The driver calling this routine (the owner of the osdev parameter) + * has already uninitialized itself. Do not do any actions that may call this + * driver directly or indirectly. + */ + +/* + * Force reload of all drivers if any application tries to open any + * of the devices. + */ + do_forceload = 1; +} + +void +oss_reserve_device (oss_device_t * osdev) +{ + osdev->refcount++; +} + +void +oss_unreserve_device (oss_device_t * osdev, int decrement) +{ + osdev->refcount -= decrement; +} + +/* + * Wait queue support + */ + /*ARGSUSED*/ + oss_wait_queue_t * +oss_create_wait_queue (oss_device_t * osdev, const char *name) +{ + oss_wait_queue_t *wq; + + if ((wq = PMALLOC (NULL, sizeof (*wq))) == NULL) + return NULL; + + cv_init (&wq->cv, NULL, CV_DRIVER, NULL); + + return wq; +} + +void * +oss_get_osid (oss_device_t * osdev) +{ + return osdev->osid; +} + +/*ARGSUSED*/ +void +oss_reset_wait_queue (oss_wait_queue_t * wq) +{ + /* NOP */ +} + +void +oss_remove_wait_queue (oss_wait_queue_t * wq) +{ + cv_destroy (&wq->cv); +} + +/*ARGSUSED*/ +int +oss_sleep (oss_wait_queue_t * wq, oss_mutex_t * mutex, int ticks, + oss_native_word * flags, unsigned int *status) +{ +/* + * oss_sleep will return 0 if timeout occurred and 1 otherwise. The WK_SIGNAL bit will be reported on + * status if a signal was caught. + */ + *status = 0; + + if (ticks > 0) + { + int res; + + if ((res = + cv_timedwait_sig (&wq->cv, mutex, ddi_get_lbolt () + ticks)) == 0) + *status |= WK_SIGNAL; /* Got signal */ + if (res < 0) /* Timeout */ + return 0; + } + else + { + if (cv_wait_sig (&wq->cv, mutex) == 0) + *status |= WK_SIGNAL; /* Got signal */ + } + return 1; +} + +/*ARGSUSED*/ +int +oss_register_poll (oss_wait_queue_t * wq, oss_mutex_t * mutex, + oss_native_word * flags, oss_poll_event_t * ev) +{ + ev->php = &wq->ph; + wq->pollevents |= ev->events; + return 0; +} + +/*ARGSUSED*/ +void +oss_wakeup (oss_wait_queue_t * wq, oss_mutex_t * mutex, + oss_native_word * flags, short events) +{ + cv_broadcast (&wq->cv); + + if (wq->pollevents & events) + { + wq->pollevents &= ~events; + MUTEX_EXIT_IRQRESTORE (*mutex, *flags); + pollwakeup (&wq->ph, events); + MUTEX_ENTER_IRQDISABLE (*mutex, *flags); + } +} + +#ifdef MUTEX_CHECKS +void +debug_mutex_init (oss_mutex_t * mutex, void *dummy, int typ, void *arg, + char *file, int line) +{ + memset (mutex, 0, sizeof (mutex)); + mutex_init (&mutex->mu, dummy, typ, arg); +} + +void +debug_mutex_destroy (oss_mutex_t * mutex, char *file, int line) +{ + mutex_destroy (&mutex->mu); +} + +void +debug_mutex_enter (oss_mutex_t * mutex, char *file, int line) +{ + if (mutex_owned (&mutex->mu)) + { + cmn_err (CE_NOTE, "%s:%d: Re-entrant mutex (%s:%d %d)\n", file, line, + mutex->file, mutex->line, mutex->busy_flags); + return; + } + + mutex->file = file; + mutex->line = line; + mutex_enter (&mutex->mu); +} + +void +debug_mutex_exit (oss_mutex_t * mutex, char *file, int line) +{ + if (!mutex_owned (&mutex->mu)) + { + cmn_err (CE_NOTE, "Mutex not owned %s:%d\n", file, line); + } + else + mutex_exit (&mutex->mu); + + mutex->file = NULL; + mutex->line = 0; +} +#endif + +int +oss_get_procinfo(int what) +{ + + switch (what) + { + case OSS_GET_PROCINFO_UID: + return ddi_get_cred()->cr_uid; + break; + + case OSS_GET_PROCINFO_GID: + return ddi_get_cred()->cr_gid; + break; + +#if 0 + case OSS_GET_PROCINFO_PGID: + return ddi_get_cred()->cr_pgid; + break; +#endif + + } + return OSS_EINVAL; +} diff --git a/kernel/OS/SunOS/os_solaris.h b/kernel/OS/SunOS/os_solaris.h new file mode 100644 index 0000000..2b36b86 --- /dev/null +++ b/kernel/OS/SunOS/os_solaris.h @@ -0,0 +1,518 @@ +#ifndef _OS_H_ +#define _OS_H_ + +/* + * Purpose: OS specific definitions for Solaris + * + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#define OS_VERSION "10" +#define SUNDDI +#define __EXTENDED__ +#define Solaris + +#if (!defined(i386) && !defined(x86_64)) || defined(CONFIG_OSS_FIXDEPOINT) +// Floating point is not supported or it's disabled +#undef CONFIG_OSS_VMIX_FLOAT +#endif + +#define VDEV_SUPPORT +#define USE_DEVICE_SUBDIRS + +#define __inline__ inline +#define __inline inline +#define EXTERN_C extern "C" + +/* + * Debugging and misc settings + */ +#undef MUTEX_CHECKS +#undef MEMDEBUG + +/* + * Disable support for per-application features such as /dev/dsp device + * selection based on command name. Requires working GET_PROCESS_NAME + * macro implementation. + */ +#undef APPLIST_SUPPORT + +/* + * Some integer types + */ +#if defined(amd64) || defined(sparc) +typedef unsigned long long oss_native_word; /* Same as the address and status register size */ +#else +typedef unsigned long oss_native_word; /* Same as the address and status register size */ +#endif + +typedef long long oss_int64_t; /* Signed 64 bit integer */ +typedef unsigned long long oss_uint64_t; /* Unsigned 64 bit integer */ + +#include <stdarg.h> +#include <sys/types.h> +#include <sys/types32.h> +#include <sys/param.h> +#include <sys/signal.h> +#include <oss_errno.h> +#include <sys/file.h> +#include <sys/conf.h> +#include <sys/uio.h> +#include <sys/map.h> +#include <sys/debug.h> +#include <sys/modctl.h> +#include <sys/kmem.h> +#include <sys/cmn_err.h> +#include <sys/open.h> +#include <sys/stat.h> +#ifndef sparc +#include <sys/dditypes.h> +#include <sys/ddidmareq.h> +#include <sys/dma_engine.h> +#endif +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/fcntl.h> +#include <sys/ddidmareq.h> +#include <sys/ksynch.h> +#include <sys/poll.h> +#ifdef SOL9 +#include <sys/cred.h> +#else +#include <sys/cred_impl.h> +#endif + +#ifndef SOL9 +#include <sys/ddifm.h> +#include <sys/fm/protocol.h> +#include <sys/fm/util.h> +#include <sys/fm/io/ddi.h> +#endif + +#undef HZ +#define OSS_HZ hz + +#ifdef sparc +/* + * It's not possible to use SB Live! under Sparc architecture. + */ +#define AUDIGY_ONLY +#endif + +/* + * Need to use slightly larger buffer because otherwise the interrupt + * rate will become too high for Solaris. + */ +#undef SMALL_DMABUF_SIZE +#define SMALL_DMABUF_SIZE (16*1024) + +/* The soundcard.h could be in a nonstandard place so include it here. */ +#include "soundcard.h" + +typedef struct udi_usb_devc udi_usb_devc; + +struct _oss_device_t +{ + int cardnum; + int dev_type; + int instance; + int available; + dev_info_t *dip; + void *osid; + void *devc; + char *name; + char nick[16]; + char modname[32]; + char handle[32]; + int num_audio_engines; + int num_audioplay, num_audiorec, num_audioduplex; + int num_mididevs; + int num_mixerdevs; + int num_loopdevs; + int first_mixer; /* This must be set to -1 by osdev_create() */ + char *hw_info; + + volatile int refcount; /* Nonzero means that the device is needed by some other (virtual) driver. */ + +/* Interrupts */ + + ddi_iblock_cookie_t iblock_cookie; + int intr_is_hilevel; + oss_tophalf_handler_t tophalf_handler; + oss_bottomhalf_handler_t bottomhalf_handler; + int intrcount, ackcount; + +#ifdef _KERNEL +/* PCI related fields */ + ddi_acc_handle_t pci_config_handle; +#define OSS_MAX_ACC_HANDLE 10 + ddi_acc_handle_t acc_handle[OSS_MAX_ACC_HANDLE]; + int swap_mode; /* 0=DDI_STRUCTURE_NEVERSWAP_ACC, 1=DDI_STRUCTURE_LE_ACC */ + +/* USB fields */ + udi_usb_devc *usbdev; + +#ifndef SOL9 +/* Fault management (FMA) */ + int fm_capabilities; +#endif +#endif +}; + +/* + * Support for select() + */ + +struct _oss_poll_event_t +{ + short events, revents; + struct pollhead *php; + int anyyet; +}; +typedef struct _oss_poll_event_t oss_poll_event_t; + +#define ALLOW_SELECT +#define SEL_IN 0 +#define SEL_OUT 1 +#define SEL_EX 0xffffffff + +/* + * Sleep/wakeup + */ + +#ifdef _KERNEL +struct oss_wait_queue +{ + kcondvar_t cv; + short pollevents; + struct pollhead ph; +}; +#endif + +/* Busy wait routine */ +#define oss_udelay drv_usecwait +/* System wall timer access */ +#define GET_JIFFIES() ddi_get_lbolt() + +/* + * Mutexes + */ + +#ifdef MUTEX_CHECKS +/* Debugging version */ +struct _oss_mutex_t +{ + kmutex_t mu; + char *file; + int line; + int busy_flags; +#define CNTX_INTR 1 +#define CNTX_USER 2 +}; + +typedef struct _oss_mutex_t oss_mutex_t; +extern void debug_mutex_init (oss_mutex_t * mutex, void *dummy, int typ, + void *arg, char *file, int line); +extern void debug_mutex_destroy (oss_mutex_t * mutex, char *file, int line); +extern void debug_mutex_enter (oss_mutex_t * mutex, char *file, int line); +extern void debug_mutex_exit (oss_mutex_t * mutex, char *file, int line); + +#define MUTEX_INIT(osdev, mutex, hier) debug_mutex_init(&mutex, NULL, MUTEX_DRIVER, osdev->iblock_cookie, __FILE__, __LINE__) +#define MUTEX_CLEANUP(mutex) debug_mutex_destroy(&mutex, __FILE__, __LINE__) +#define MUTEX_ENTER_IRQDISABLE(mutex, flags) flags=0;debug_mutex_enter(&mutex, __FILE__, __LINE__) +#define MUTEX_ENTER(mutex, flags) flags=0;debug_mutex_enter(&mutex, __FILE__, __LINE__) +#define MUTEX_EXIT_IRQRESTORE(mutex, flags) debug_mutex_exit(&mutex, __FILE__, __LINE__);(flags)++ +#define MUTEX_EXIT(mutex, flags) debug_mutex_exit(&mutex, __FILE__, __LINE__);(flags)++ +#else +typedef kmutex_t oss_mutex_t; +#define MUTEX_INIT(osdev, mutex, hier) mutex_init(&mutex, NULL, MUTEX_DRIVER, osdev->iblock_cookie) +#define MUTEX_CLEANUP(mutex) mutex_destroy(&mutex) +#define MUTEX_ENTER_IRQDISABLE(mutex, flags) flags=0;mutex_enter(&mutex) +#define MUTEX_ENTER(mutex, flags) flags=0;mutex_enter(&mutex) +#define MUTEX_EXIT_IRQRESTORE(mutex, flags) mutex_exit(&mutex);(flags)++ +#define MUTEX_EXIT(mutex, flags) mutex_exit(&mutex);(flags)++ +#endif + +/* + * Move bytes from the buffer which the application given in a + * write() call. + * offs is position relative to the beginning of the buffer in + * user space. The count is number of bytes to be moved. + */ +#define COPY_FROM_USER(target, source, offs, count) \ + if (uiomove((target), count, UIO_WRITE, source)) { \ + cmn_err(CE_WARN, "Bad copyin()!\n"); \ + } +/* Like COPY_FOM_USER but for writes. */ +#define COPY_TO_USER(target, offs, source, count) \ + if (uiomove((source), count, UIO_READ, target)) { \ + cmn_err(CE_WARN, "Bad copyout()!\n"); \ + } + +/* + * INB() and OUTB() should be obvious. NOTE! The order of + * paratemeters of OUTB() is different than on some other + * operating systems. + */ + +#ifdef sparc +#ifdef _KERNEL +extern void oss_put16 (ddi_acc_handle_t handle, unsigned short *addr, + unsigned short value); +extern void oss_put32 (ddi_acc_handle_t handle, unsigned int *addr, + unsigned int value); +extern unsigned short oss_get16 (ddi_acc_handle_t handle, + unsigned short *addr); +extern unsigned int oss_get32 (ddi_acc_handle_t handle, unsigned int *addr); + +#define INB(osdev,port) ddi_get8((osdev)->acc_handle[0], (uint8_t*)(port)) +#define OUTB(osdev, d, port) ddi_put8((osdev)->acc_handle[0], (uint8_t*)(port), (d)) +#define INW(osdev,port) oss_get16((osdev)->acc_handle[0], (uint16_t*)(port)) +#define OUTW(osdev, d, port) oss_put16((osdev)->acc_handle[0], (uint16_t*)(port), (d)) +#define INL(osdev,port) oss_get32((osdev)->acc_handle[0], (uint32_t*)(port)) +#define OUTL(osdev,d, port) oss_put32((osdev)->acc_handle[0], (uint32_t*)(port), (d)) + +/* Memory Mapped devices */ +extern uint16_t oss_mem_get16 (ddi_acc_handle_t handle, uint16_t * addr); +extern uint32_t oss_mem_get32 (ddi_acc_handle_t handle, uint32_t * addr); +extern void oss_mem_put16 (ddi_acc_handle_t handle, uint16_t * add, + uint16_t v); +extern void oss_mem_put32 (ddi_acc_handle_t handle, uint32_t * add, + uint32_t v); +#define PCI_READW(osdev, addr) oss_mem_get16((osdev)->acc_handle[0], (uint16_t*)(addr)) +#define PCI_READL(osdev, addr) oss_mem_get32((osdev)->acc_handle[0], (uint32_t*)(addr)) +#define PCI_WRITEW(osdev, addr, data) oss_mem_put16((osdev)->acc_handle[0], (uint16_t*)(addr), data) +#define PCI_WRITEL(osdev, addr, data) oss_mem_put32((osdev)->acc_handle[0], (uint32_t*)(addr), data) +#endif +#else /* x86/AMD64 */ +/* I/O Mapped devices */ +#define INB(o, p) inb(p) +#define INW(o, p) inw(p) +#define INL(o, p) inl(p) + +#define OUTB(o, v, p) outb(p,v) +#define OUTW(o, v, p) outw(p,v) +#define OUTL(o, v, p) outl(p,v) + +/* Memory Mapped devices */ +#define PCI_READW(osdev, addr) ddi_mem_get16((osdev)->acc_handle[0], (uint16_t*)(addr)) +#define PCI_READL(osdev, addr) ddi_mem_get32((osdev)->acc_handle[0], (uint32_t*)(addr)) +#define PCI_WRITEW(osdev, addr, data) ddi_mem_put16((osdev)->acc_handle[0], (uint16_t*)(addr), data) +#define PCI_WRITEL(osdev, addr, data) ddi_mem_put32((osdev)->acc_handle[0], (uint32_t*)(addr), data) +#endif + +#define PCI_READB(osdev, addr) ddi_mem_get8((osdev)->acc_handle[0], addr) +#define PCI_WRITEB(osdev, addr, data) ddi_mem_put8((osdev)->acc_handle[0], addr, data) + +typedef ddi_dma_handle_t oss_dma_handle_t; + +/* + KERNEL_MALLOC() allocates requested number of memory and + KERNEL_FREE is used to free it. + These macros are never called from interrupt, in addition the + nbytes will never be more than 4096 bytes. Generally the driver + will allocate memory in blocks of 4k. If the kernel has just a + page level memory allocation, 4K can be safely used as the size + (the nbytes parameter can be ignored). +*/ +#ifdef MEMDEBUG +extern void *oss_kmem_alloc (size_t size, int flags, char *file, int line); +extern void oss_kmem_free (void *addr); +#define KERNEL_MALLOC(nbytes) oss_kmem_alloc(nbytes, KM_SLEEP, __FILE__, __LINE__) +#define KERNEL_FREE(addr) oss_kmem_free(addr) +extern void *oss_contig_malloc (oss_device_t * osdev, int sz, + oss_uint64_t memlimit, + oss_native_word *phaddr, + oss_dma_handle_t *dma_handle, + char * file, int line); +extern void oss_contig_free (oss_device_t * osdev, void *p, int sz); +#define CONTIG_MALLOC(osdev, sz, memlimit, phaddr, handle) oss_contig_malloc(osdev, sz, memlimit, phaddr, &handle, __FILE__, __LINE__) +#define CONTIG_FREE(osdev, p, sz, handle) oss_contig_free(osdev, p, sz, handle) +#else +extern void *oss_kmem_alloc (size_t size, int flags); +extern void oss_kmem_free (void *addr); +#define KERNEL_MALLOC(nbytes) oss_kmem_alloc(nbytes, KM_SLEEP) +#define KERNEL_FREE(addr) oss_kmem_free(addr) +extern void *oss_contig_malloc (oss_device_t * osdev, int sz, + oss_uint64_t memlimit, + oss_native_word * phaddr, + oss_dma_handle_t *dma_handle); +extern void oss_contig_free (oss_device_t * osdev, void *p, int sz); +#define CONTIG_MALLOC(osdev, sz, memlimit, phaddr, handle) oss_contig_malloc(osdev, sz, memlimit, phaddr, &handle) +#define CONTIG_FREE(osdev, p, sz, handle) oss_contig_free(osdev, p, sz) +#endif + +/* + * Timer macros + * + * These macros are obsolete and should not be used in any new code. + * Use the timeout mechanism (see the timeout(9F) Solaris man page). + */ +#define DEFINE_TIMER(name, proc) static timeout_id_t name = 0 +#define REMOVE_TIMER(name, proc) {if (name != 0) untimeout(name);} +#define INIT_TIMER(name,proc) +typedef void (*timeout_func_t) (void *); +#define ACTIVATE_TIMER(name, proc, time) \ + name=timeout((timeout_func_t)proc, (void*)&name, time) + +#define OSS_DMA_SYNC_INBOUND DDI_DMA_SYNC_FORCPU +#define OSS_DMA_SYNC_OUTBOUND DDI_DMA_SYNC_FORDEV +#define OSS_DMA_SYNC(handle, offs, len, direc) (ddi_dma_sync(handle, offs, len, direc)==DDI_SUCCESS) + +#ifdef _KERNEL +struct os_dma_params +{ + int state; /* 0=unavail, 1=avail, 2=busy */ + oss_device_t *osdev; + ddi_dma_handle_t handle; + ddi_acc_handle_t dma_acc_handle; + ddi_dma_cookie_t cookie; + ddi_dma_win_t win; + ddi_dma_seg_t seg; + void *orig_buf; + + volatile int enabled, ignore; +}; +#define OS_DMA_PARMS \ + struct os_dma_params dma_parms; +#endif +#endif + +struct fileinfo +{ + int mode; /* Open mode */ + int acc_flags; +}; + +#define ISSET_FILE_FLAG(fileinfo, flag) (fileinfo->acc_flags & (flag) ? 1:0) + +#ifdef sparc +#define OSS_OS "Sparc" +#define OSS_OS_LONGNAME "Solaris (Sparc)" +#else +#define OSS_OS "Solaris" +#define OSS_OS_LONGNAME "Solaris (x86)" +#endif + +#undef DMA_TRY_PSEUDOINIT + +int get_dma_residue (int chn); +void disable_dma (int chn); +void enable_dma (int chn); + +typedef void (*softintr_func_t) (int); + +struct oss_softintr +{ + int id; + softintr_func_t func; + volatile int armed, running; +}; + +/* + * There is apparently no documented way to map DMA buffers allocated + * using ddi_dma_mem_alloc() to applications. For this reason we cannot + * support mmap() under Solaris. + */ +#undef ALLOW_BUFFER_MAPPING + +#if 0 +extern int detect_trace; +#define DDB(x) if (detect_trace) x +#else +#define DDB(x) {} +#endif + +/* + * PCI I/O and memory address mapping. + * + * Note that for compatibility with the other operating systems supported by + * OSS the region numbering is different. So 0 means BAR0 while under + * Solaris (ddi_regs_map_setup) BAR0 is 1. oss_map_pci_ioaddr() will + * add 1 to the region number. + */ +extern caddr_t oss_map_pci_ioaddr (oss_device_t * osdev, int nr, int io); +extern void oss_unmap_pci_ioaddr(oss_device_t * osdev, int nr); +#define MAP_PCI_IOADDR(osdev, nr, io) (oss_native_word)oss_map_pci_ioaddr(osdev, nr, io) +#define MAP_PCI_MEM(osdev, ix, phaddr, size) oss_map_pci_ioaddr(osdev, ix, phaddr) +#define UNMAP_PCI_MEM(osdev, ix, ph, virt, size) oss_unmap_pci_ioaddr(osdev, ix) +#define UNMAP_PCI_IOADDR(osdev, ix) oss_unmap_pci_ioaddr(osdev, ix) + +#define GET_PROCESS_PID(x) ddi_get_pid() + +/* + * Instead of returning UID check if the process has PRIV_SYS_DEVICES privilege. + * Report such users as UID=0 (root) and others as UID=1. + */ +#define GET_PROCESS_UID() ((drv_priv(ddi_get_cred())==0) ? 0 : 1) + +#if 1 +/* TODO: This works OK but may cause crashes with different kernel versions/builds */ +extern char *oss_get_procname (void); +#define GET_PROCESS_NAME(x) oss_get_procname() +#endif + +#define FP_SUPPORT + +#ifdef FP_SUPPORT +typedef short fp_env_t[512]; +typedef unsigned int fp_flags_t[5]; +extern int oss_fp_check (void); +extern void oss_fp_save (short *envbuf, fp_flags_t flags); +extern void oss_fp_restore (short *envbuf, fp_flags_t flags); +# define FP_SAVE(envbuf, flags) oss_fp_save(envbuf, flags) +# define FP_RESTORE(envbuf, flags) oss_fp_restore(envbuf, flags) +#endif + +#define abs(x) ((x) >= 0 ? (x) : -(x)) + +extern int oss_open (dev_t * dev_p, int open_flags, int otyp, + cred_t * cred_p); +extern int oss_close (dev_t dev, int flag, int otyp, cred_t * cred_p); +extern int oss_ioctl (dev_t dev, int cmd, intptr_t arg, int mode, + cred_t * cred_p, int *rval_p); +extern int oss_read (dev_t dev, struct uio *uiop, cred_t * credp); +extern int oss_write (dev_t dev, struct uio *uiop, cred_t * cred_p); +extern int oss_chpoll (dev_t dev, short events, int anyyet, short *reventsp, + struct pollhead **phpp); +#ifdef _KERNEL +extern int oss_devmap (dev_t dev, devmap_cookie_t handle, offset_t off, + size_t len, size_t * maplen, uint_t model); + +extern void *oss_memset (void *s, int c, size_t n); +#define memset oss_memset + +extern void *oss_memcpy (void *s1, const void *s2, size_t n); +#define memcpy oss_memcpy +#endif + +#define DISABLE_FMA +#if !defined(SOL9) && defined(DDI_FM_DEVICE) && !defined(DISABLE_FMA) +/* + * Fault management (FMA) support. + */ + +#define FMA_EREPORT(osdev, detail, name, type, value) \ +{ \ + uint64_t ena; \ + char buf[FM_MAX_CLASS]; \ + (void) snprintf (buf, FM_MAX_CLASS, "%s.%s", DDI_FM_DEVICE, detail); \ + ena = fm_ena_generate(0, FM_ENA_FMT1); \ + if (osdev->dip != NULL && osdev->fm_capabilities != 0) \ + ddi_fm_ereport_post(osdev->dip, buf, ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, name, type, value, NULL); \ +} + +#define FMA_IMPACT(osdev, impact) \ + if (osdev->fm_capabilities != 0) \ + ddi_fm_service_impact(osdev->dip, impact) +#endif diff --git a/kernel/OS/SunOS/udi.c b/kernel/OS/SunOS/udi.c new file mode 100644 index 0000000..ad4d4fa --- /dev/null +++ b/kernel/OS/SunOS/udi.c @@ -0,0 +1,1365 @@ +/* + * Purpose: USB device interface (udi.h) routines for Solaris + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ +#if !defined(SOL9) +#include "oss_config.h" +#include <udi.h> + +#define USBDRV_MAJOR_VER 2 +#define USBDRV_MINOR_VER 0 +#include <sys/usb/usba.h> +#include <sys/strsubr.h> + +#undef IO_DEBUG +int udi_usb_trace = 0; + +#define MAX_DEVICE_SLOTS 20 + +struct udi_usb_devc +{ + oss_device_t *osdev; + usb_client_dev_data_t *dev_data; + char *name; + + const struct usb_device_id *id; + const udi_usb_devinfo *udi_usb_dev; + char devpath[32]; + + int enabled; + + struct usb_interface *iface; + udi_usb_driver *drv; + void *client_devc; + void (*client_disconnect) (void *devc); +}; + +#define MAX_DEVC 32 + +int +udi_attach_usbdriver (oss_device_t * osdev, const udi_usb_devinfo * devlist, + udi_usb_driver * driver) +{ + int err, i, nalt, vendor, product, busaddr; + udi_usb_devc *usbdev; + dev_info_t *dip = osdev->dip; + char *name; + unsigned long osid; + //usb_alt_if_data_t *altif_data; + usb_client_dev_data_t *dev_data; + + if (dip==NULL) + { + cmn_err(CE_WARN, "dip==NULL\n"); + return 0; + } + + name = ddi_get_name (osdev->dip); + + if (strcmp (name, "oss_usb") == 0) + { + oss_register_device (osdev, "USB audio/MIDI device"); + return 1; + } + + /* + * Select the default configuration + */ + if ((err = usb_set_cfg (osdev->dip, USB_DEV_DEFAULT_CONFIG_INDEX, + USB_FLAGS_SLEEP, NULL, NULL)) == USB_SUCCESS) + { + cmn_err (CE_CONT, "usb_set_cfg returned %d\n", err); + } + + busaddr = ddi_prop_get_int (DDI_DEV_T_ANY, osdev->dip, + DDI_PROP_NOTPROM, "assigned-address", 1234); + if ((usbdev = KERNEL_MALLOC (sizeof (*usbdev))) == NULL) + { + cmn_err (CE_WARN, "udi_attach_usbdriver: Out of memory\n"); + return 0; + } + + memset (usbdev, 0, sizeof (*usbdev)); + osdev->usbdev = usbdev; + usbdev->osdev = osdev; + + if ((err = usb_client_attach (dip, USBDRV_VERSION, 0)) != USB_SUCCESS) + { + cmn_err (CE_WARN, "usb_client_attach failed, err=%d\n", err); + KERNEL_FREE (usbdev); + return 0; + } + + if ((err = + usb_get_dev_data (osdev->dip, &usbdev->dev_data, USB_PARSE_LVL_CFG, + 0)) != USB_SUCCESS) + { + cmn_err (CE_WARN, "usb_get_dev_data failed, err=%d\n", err); + KERNEL_FREE (usbdev); + return 0; + } + dev_data = usbdev->dev_data; + + osdev->iblock_cookie = dev_data->dev_iblock_cookie; + + sprintf (usbdev->devpath, "usb%x,%x@port%d", udi_usbdev_get_vendor (usbdev), + udi_usbdev_get_product (usbdev), busaddr); + oss_register_device (osdev, "USB audio/MIDI device"); + sprintf (osdev->nick, "usb%x_%x_%d", udi_usbdev_get_vendor (usbdev), + udi_usbdev_get_product (usbdev), busaddr); + +#if 0 + if (udi_usb_trace > 5) + usb_print_descr_tree (osdev->dip, dev_data); +#endif + + nalt = 1; + if (nalt >= dev_data->dev_curr_cfg->cfg_if[dev_data->dev_curr_if].if_n_alt) + nalt = 0; + + //altif_data = &dev_data->dev_curr_cfg-> + // cfg_if[dev_data->dev_curr_if].if_alt[nalt]; + + usbdev->name = usbdev->dev_data->dev_product; + vendor = udi_usbdev_get_vendor (usbdev); + product = udi_usbdev_get_product (usbdev); + + /* inum = usbdev->dev_data->dev_curr_if; */ + osid = (vendor << 16) | product; + osdev->osid = (void *) osid; + + sprintf (osdev->handle, "usb%x,%x.%d:%d", vendor, product, + dev_data->dev_curr_if, osdev->instance); + + for (i = 0; devlist[i].vendor != -1; i++) + if (devlist[i].vendor == vendor && devlist[i].product == product) + { + usbdev->name = devlist[i].name; + usbdev->udi_usb_dev = &devlist[i]; + oss_register_device (osdev, usbdev->name); + break; + } + + if ((usbdev->client_devc = driver->attach (usbdev, osdev)) == NULL) + { + cmn_err (CE_WARN, "Client attach failed, err=%d\n", err); + udi_unload_usbdriver (osdev); + return 0; + } + + usbdev->client_disconnect = driver->disconnect; + + return 1; +} + +static char * +usb_errstr (int err) +{ + + static struct errmsg_ + { + int err; + char *str; + } errs[] = + { + { + USB_FAILURE, "USB_FAILURE"}, + { + USB_NO_RESOURCES, "USB_NO_RESOURCES"}, + { + USB_NO_BANDWIDTH, "USB_NO_BANDWIDTH"}, + { + USB_NOT_SUPPORTED, "USB_NOT_SUPPORTED"}, + { + USB_PIPE_ERROR, "USB_PIPE_ERROR"}, + { + USB_INVALID_PIPE, "USB_INVALID_PIPE"}, + { + USB_NO_FRAME_NUMBER, "USB_NO_FRAME_NUMBER"}, + { + USB_INVALID_START_FRAME, "USB_INVALID_START_FRAME"}, + { + USB_HC_HARDWARE_ERROR, "USB_HC_HARDWARE_ERROR"}, + { + USB_INVALID_REQUEST, "USB_INVALID_REQUEST"}, + { + USB_INVALID_CONTEXT, "USB_INVALID_CONTEXT"}, + { + USB_INVALID_VERSION, "USB_INVALID_VERSION"}, + { + USB_INVALID_ARGS, "USB_INVALID_ARGS"}, + { + USB_INVALID_PERM, "USB_INVALID_PERM"}, + { + USB_BUSY, "USB_BUSY"}, + { + 0, NULL} + }; + + int i; + static char msg[20]; + + for (i = 0; errs[i].err != 0; i++) + if (errs[i].err == err) + { + return errs[i].str; + } + + sprintf (msg, "Err %d", err); + return msg; +} + +static char * +usb_cb_err (int err) +{ + + static struct errmsg_ + { + int err; + char *str; + } errs[] = + { + { + USB_CB_STALL_CLEARED, "USB_CB_STALL_CLEARED"}, + { + USB_CB_FUNCTIONAL_STALL, "USB_CB_FUNCTIONAL_STALL"}, + { + USB_CB_PROTOCOL_STALL, "USB_CB_PROTOCOL_STALL"}, + { + USB_CB_RESET_PIPE, "USB_CB_RESET_PIPE"}, + { + USB_CB_ASYNC_REQ_FAILED, "USB_CB_ASYNC_REQ_FAILED"}, + { + USB_CB_NO_RESOURCES, "USB_CB_NO_RESOURCES"}, + { + USB_CB_SUBMIT_FAILED, "USB_CB_SUBMIT_FAILED"}, + { + USB_CB_INTR_CONTEXT, "USB_CB_INTR_CONTEXT"}, + { + USB_FAILURE, "USB_FAILURE"}, + { + USB_NO_RESOURCES, "USB_NO_RESOURCES"}, + { + USB_NO_BANDWIDTH, "USB_NO_BANDWIDTH"}, + { + USB_NOT_SUPPORTED, "USB_NOT_SUPPORTED"}, + { + USB_PIPE_ERROR, "USB_PIPE_ERROR"}, + { + USB_INVALID_PIPE, "USB_INVALID_PIPE"}, + { + USB_NO_FRAME_NUMBER, "USB_NO_FRAME_NUMBER"}, + { + USB_INVALID_START_FRAME, "USB_INVALID_START_FRAME"}, + { + USB_HC_HARDWARE_ERROR, "USB_HC_HARDWARE_ERROR"}, + { + USB_INVALID_REQUEST, "USB_INVALID_REQUEST"}, + { + USB_INVALID_CONTEXT, "USB_INVALID_CONTEXT"}, + { + USB_INVALID_VERSION, "USB_INVALID_VERSION"}, + { + USB_INVALID_ARGS, "USB_INVALID_ARGS"}, + { + USB_INVALID_PERM, "USB_INVALID_PERM"}, + { + USB_BUSY, "USB_BUSY"}, + { + 0, NULL} + }; + + int i; + static char msg[20]; + + if (err == 0) + return "USB_CB_NO_INFO"; + + for (i = 0; errs[i].err != 0; i++) + { + if (errs[i].err == err) + { + return errs[i].str; + } + } + + sprintf (msg, "CB Err %d", err); + return msg; +} + +static char * +usb_cr_err (int err) +{ + + static struct errmsg_ + { + int err; + char *str; + } errs[] = + { + { + USB_CR_CRC, "USB_CR_CRC"}, + { + USB_CR_BITSTUFFING, "USB_CR_BITSTUFFING"}, + { + USB_CR_DATA_TOGGLE_MM, "USB_CR_DATA_TOGGLE_MM"}, + { + USB_CR_STALL, "USB_CR_STALL"}, + { + USB_CR_DEV_NOT_RESP, "USB_CR_DEV_NOT_RESP"}, + { + USB_CR_PID_CHECKFAILURE, "USB_CR_PID_CHECKFAILURE"}, + { + USB_CR_UNEXP_PID, "USB_CR_UNEXP_PID"}, + { + USB_CR_DATA_OVERRUN, "USB_CR_DATA_OVERRUN"}, + { + USB_CR_DATA_UNDERRUN, "USB_CR_DATA_UNDERRUN"}, + { + USB_CR_BUFFER_OVERRUN, "USB_CR_BUFFER_OVERRUN"}, + { + USB_CR_BUFFER_UNDERRUN, "USB_CR_BUFFER_UNDERRUN"}, + { + USB_CR_TIMEOUT, "USB_CR_TIMEOUT"}, + { + USB_CR_NOT_ACCESSED, "USB_CR_NOT_ACCESSED"}, + { + USB_CR_NO_RESOURCES, "USB_CR_NO_RESOURCES"}, + { + USB_CR_UNSPECIFIED_ERR, "USB_CR_UNSPECIFIED_ERR"}, + { + USB_CR_STOPPED_POLLING, "USB_CR_STOPPED_POLLING"}, + { + USB_CR_PIPE_CLOSING, "USB_CR_PIPE_CLOSING"}, + { + USB_CR_PIPE_RESET, "USB_CR_PIPE_RESET"}, + { + USB_CR_NOT_SUPPORTED, "USB_CR_NOT_SUPPORTED"}, + { + USB_CR_FLUSHED, "USB_CR_FLUSHED"}, + { + USB_CR_HC_HARDWARE_ERR, "USB_CR_HC_HARDWARE_ERR"}, + { + 0, NULL} + }; + + int i; + static char msg[20]; + + if (err == 0) + return "USB_CR_OK"; + + for (i = 0; errs[i].err != 0; i++) + { + if (errs[i].err == err) + { + return errs[i].str; + } + } + + sprintf (msg, "CR Err %d", err); + return msg; +} + +void +udi_unload_usbdriver (oss_device_t * osdev) +{ + udi_usb_devc *usbdev = osdev->usbdev; + + if (usbdev == NULL) + return; + + if (usbdev->client_disconnect != NULL) + usbdev->client_disconnect (usbdev->client_devc); + usb_client_detach (osdev->dip, usbdev->dev_data); + + KERNEL_FREE (usbdev); + osdev->usbdev = NULL; +} + +/* + * Device access routines + */ + +int +udi_usbdev_get_class (udi_usb_devc * usbdev) +{ + udi_usb_devc *devc = (udi_usb_devc *) usbdev; + + return devc->dev_data->dev_curr_cfg->cfg_if[devc->dev_data->dev_curr_if]. + if_alt[0].altif_descr.bInterfaceClass; +} + +int +udi_usbdev_get_subclass (udi_usb_devc * usbdev) +{ + udi_usb_devc *devc = (udi_usb_devc *) usbdev; + + return devc->dev_data->dev_curr_cfg->cfg_if[devc->dev_data->dev_curr_if]. + if_alt[0].altif_descr.bInterfaceSubClass; +} + +int +udi_usbdev_get_vendor (udi_usb_devc * usbdev) +{ + udi_usb_devc *devc = (udi_usb_devc *) usbdev; + + return devc->dev_data->dev_descr->idVendor; +} + +int +udi_usbdev_get_product (udi_usb_devc * usbdev) +{ + udi_usb_devc *devc = (udi_usb_devc *) usbdev; + + return devc->dev_data->dev_descr->idProduct; +} + +int +udi_usbdev_get_inum (udi_usb_devc * usbdev) +{ + udi_usb_devc *devc = (udi_usb_devc *) usbdev; + + return devc->dev_data->dev_curr_if; +} + +int +udi_usbdev_set_interface (udi_usb_devc * usbdev, int inum, int altset) +{ + //udi_usb_devc *devc = (udi_usb_devc *) usbdev; + + if (usb_set_alt_if (usbdev->osdev->dip, inum, altset, USB_FLAGS_SLEEP, + NULL, 0) != USB_SUCCESS) + { + return OSS_EIO; + } + + return 0; +} + +unsigned char * +udi_usbdev_get_endpoint (udi_usb_devc * usbdev, int altsetting, int n, + int *len) +{ + usb_alt_if_data_t *altif_data; + usb_client_dev_data_t *dev_data; + + *len = 0; + + if (usbdev->dev_data == NULL) + { + cmn_err (CE_WARN, "Missing USB devdata\n"); + return NULL; + } + dev_data = usbdev->dev_data; + + if (altsetting < 0 + || altsetting >= + dev_data->dev_curr_cfg->cfg_if[dev_data->dev_curr_if].if_n_alt) + { + return NULL; + } + + altif_data = &dev_data->dev_curr_cfg-> + cfg_if[dev_data->dev_curr_if].if_alt[altsetting]; + + if (altif_data == NULL) + return NULL; + + if (n < 0 || n >= altif_data->altif_n_ep) + return NULL; + + *len = altif_data->altif_ep[n].ep_descr.bLength; + return (unsigned char *) &altif_data->altif_ep[n].ep_descr; +} + +int +udi_usbdev_get_num_altsettings (udi_usb_devc * usbdev) +{ + //udi_usb_devc *devc = (udi_usb_devc *) usbdev; + usb_client_dev_data_t *dev_data; + + dev_data = usbdev->dev_data; + return dev_data->dev_curr_cfg->cfg_if[dev_data->dev_curr_if].if_n_alt; +} + +unsigned char * +udi_usbdev_get_altsetting (udi_usb_devc * usbdev, int nalt, int *size) +{ + int i, n; + usb_cvs_data_t *cvs; + usb_alt_if_data_t *altif_data; + usb_client_dev_data_t *dev_data; + static unsigned char buf[1024]; + + dev_data = usbdev->dev_data; + if ((unsigned long) dev_data <= 4096) + { + cmn_err (CE_WARN, "Internal error: dev_data==NULL)\n"); + return NULL; + } + + if (nalt < 0 + || nalt >= + dev_data->dev_curr_cfg->cfg_if[dev_data->dev_curr_if].if_n_alt) + { + return NULL; + } + + altif_data = &dev_data->dev_curr_cfg-> + cfg_if[dev_data->dev_curr_if].if_alt[nalt]; + + if (altif_data == NULL) + return NULL; + + n = 0; + + for (i = 0; i < altif_data->altif_n_cvs; i++) + { + cvs = &altif_data->altif_cvs[i]; + memcpy (buf + n, cvs->cvs_buf, cvs->cvs_buf_len); + n += cvs->cvs_buf_len; + } + + cvs = &altif_data->altif_cvs[0]; + if (cvs == NULL || cvs->cvs_buf == NULL) + return NULL; + + *size = n; + return buf; +} + +char * +udi_usbdev_get_name (udi_usb_devc * usbdev) +{ + udi_usb_devc *devc = (udi_usb_devc *) usbdev; + + return devc->name; +} + +char * +udi_usbdev_get_altsetting_labels (udi_usb_devc * usbdev, int if_num, + int *default_alt, unsigned int *mask) +{ + int i; + + *default_alt = 1; + *mask = 0xffffffff; + + if (usbdev->udi_usb_dev == NULL) /* No device definitions available */ + { + return NULL; + } + + for (i = 0; usbdev->udi_usb_dev->altsettings[i].altsetting_labels != NULL; + i++) + if (i == if_num) + { + *default_alt = usbdev->udi_usb_dev->altsettings[i].default_altsetting; + *mask = usbdev->udi_usb_dev->altsettings[i].altsetting_mask; + if (*mask == 0) + *mask = 0xffffffff; + return usbdev->udi_usb_dev->altsettings[i].altsetting_labels; + } + + return NULL; /* Not found */ +} + +char * +udi_usbdev_get_string (udi_usb_devc * usbdev, int ix) +{ + static char str[256]; + + if (usb_get_string_descr + (usbdev->osdev->dip, USB_LANG_ID, ix, str, + sizeof (str) - 1) != USB_SUCCESS) + return NULL; + return str; +} + +char * +udi_usbdev_get_devpath (udi_usb_devc * usbdev) +{ + udi_usb_devc *devc = (udi_usb_devc *) usbdev; + + return devc->devpath; +} + +/*ARGSUSED*/ +int +udi_usb_snd_control_msg (udi_usb_devc * usbdev, unsigned int endpoint, + unsigned char rq, + unsigned char rqtype, + unsigned short value, + unsigned short index, + void *buf, int len, int timeout) +{ + + int err; + + usb_ctrl_setup_t setup; + usb_cr_t completion_reason; + usb_cb_flags_t cb_flags; + mblk_t *data = NULL; + + if (usbdev == NULL) + { + cmn_err (CE_CONT, "udi_usb_snd_control_msg: usbdev==NULL\n"); + return OSS_EFAULT; + } + + data = allocb_wait (len + 1, BPRI_HI, STR_NOSIG, NULL); + + memcpy (data->b_wptr, buf, len); + data->b_wptr = data->b_rptr + len; + + setup.bmRequestType = rqtype | USB_DEV_REQ_HOST_TO_DEV; + setup.bRequest = rq; + setup.wValue = value; + setup.wIndex = index; + setup.wLength = len; + setup.attrs = 0; + + if ((err = usb_pipe_ctrl_xfer_wait (usbdev->dev_data->dev_default_ph, + &setup, + &data, + &completion_reason, + &cb_flags, 0)) != USB_SUCCESS) + { + char tmp[128], *s = tmp; + unsigned char *p = buf; + int i; + + sprintf (s, "Msg (rq=0x%x, val=0x%04x, ix=0x%04x, len=%d): ", rq, value, + index, len); + for (i = 0; i < len; i++) + { + s += strlen (s); + sprintf (s, "%02x ", p[i]); + } + cmn_err (CE_CONT, "%s\n", tmp); + + cmn_err (CE_NOTE, "usb_pipe_ctrl_xfer_wait write failed: %s (%s)\n", + usb_errstr (err), usb_cb_err (completion_reason)); + cmn_err (CE_CONT, "bRq %x, wIx %x, wVal %x, wLen %d\n", rq, index, + value, len); + freemsg (data); + return OSS_EIO; + } + + freemsg (data); + return len; +} + +/*ARGSUSED*/ +int +udi_usb_rcv_control_msg (udi_usb_devc * usbdev, unsigned int endpoint, + unsigned char rq, + unsigned char rqtype, + unsigned short value, + unsigned short index, + void *buf, int len, int timeout) +{ + int err, l; + + usb_ctrl_setup_t setup; + usb_cr_t completion_reason; + usb_cb_flags_t cb_flags; + mblk_t *data = NULL; + + if (usbdev == NULL) + { + cmn_err (CE_CONT, "udi_usb_rcv_control_msg: usbdev==NULL\n"); + return OSS_EFAULT; + } + + setup.bmRequestType = rqtype | USB_DEV_REQ_DEV_TO_HOST; + setup.bRequest = rq; + setup.wValue = value; + setup.wIndex = index; + setup.wLength = len; + setup.attrs = 0; + + if ((err = usb_pipe_ctrl_xfer_wait (usbdev->dev_data->dev_default_ph, + &setup, + &data, + &completion_reason, + &cb_flags, 0)) != USB_SUCCESS) + { + char tmp[128], *s = tmp; + + sprintf (s, "Msg (rq=0x%02x, val=0x%04x, ix=0x%04x, len=%d): ", rq, + value, index, len); + cmn_err (CE_CONT, "%s\n", tmp); + cmn_err (CE_NOTE, "usb_pipe_ctrl_xfer_wait read failed: %s (%s)\n", + usb_errstr (err), usb_cb_err (completion_reason)); + freemsg (data); + return OSS_EIO; + } + + /*LINTED*/ l = data->b_wptr - data->b_rptr; + + memcpy (buf, data->b_rptr, l); + freemsg (data); + + return l; +} + +/* Endpoint/pipe access */ + +struct _udi_endpoint_handle_t +{ + usb_pipe_handle_t pipe_handle; + unsigned char *ep_desc; + udi_usb_devc *usbdev; +}; + +udi_endpoint_handle_t * +udi_open_endpoint (udi_usb_devc * usbdev, void *ep_desc) +{ + udi_endpoint_handle_t *h; + int err; + usb_pipe_policy_t policy; + + if ((h = KERNEL_MALLOC (sizeof (*h))) == NULL) + return NULL; + + policy.pp_max_async_reqs = 2; + + if ((err = usb_pipe_open (usbdev->osdev->dip, ep_desc, &policy, + USB_FLAGS_SLEEP, &h->pipe_handle)) != USB_SUCCESS) + { + cmn_err (CE_WARN, "usb_pipe_open() failed %d (%s)\n", err, + usb_cb_err (err)); + KERNEL_FREE (h); + return NULL; + } + + h->ep_desc = ep_desc; + h->usbdev = usbdev; + + return h; +} + +void +udi_close_endpoint (udi_endpoint_handle_t * h) +{ + usb_pipe_close (h->usbdev->osdev->dip, h->pipe_handle, USB_FLAGS_SLEEP, + NULL, 0); + KERNEL_FREE (h); +} + +int +udi_endpoint_get_num (udi_endpoint_handle_t * eph) +{ + return eph->ep_desc[2] & 0xff; +} + +/* Request stuff */ + +struct udi_usb_request +{ + dev_info_t *dip; + usb_pipe_handle_t pipe_handle; + udi_usb_complete_func_t callback; + void *callback_arg; + int active; + int xfer_type; + mblk_t *data; + + /* + * Recording + */ + int actlen; + + usb_isoc_req_t *isoc_req; + usb_bulk_req_t *bulk_req; + usb_intr_req_t *intr_req; +}; + + /*ARGSUSED*/ + udi_usb_request_t + * udi_usb_alloc_request (udi_usb_devc * usbdev, udi_endpoint_handle_t * eph, + int nframes, int xfer_type) +{ + udi_usb_request_t *req; + + if ((req = KERNEL_MALLOC (sizeof (*req))) == NULL) + { + cmn_err (CE_WARN, "udi_usb_alloc_request: Out of memory\n"); + return NULL; + } + + req->xfer_type = xfer_type; + req->dip = usbdev->osdev->dip; + req->pipe_handle = eph->pipe_handle; + + switch (xfer_type) + { + case UDI_USBXFER_ISO_READ: + return req; + break; + + case UDI_USBXFER_ISO_WRITE: + return req; + break; + + case UDI_USBXFER_BULK_READ: + return req; + break; + + case UDI_USBXFER_INTR_READ: + return req; + break; + + case UDI_USBXFER_BULK_WRITE: + return req; + break; + + default: + cmn_err (CE_WARN, "Internal error - bad transfer type %d\n", xfer_type); + KERNEL_FREE (req); + return NULL; + } +} + +void +udi_usb_free_request (udi_usb_request_t * req) +{ + if (req == NULL) + return; + + udi_usb_cancel_request (req); + + if (req->active) + { + cmn_err (CE_WARN, "Warning: Freeing active request\n"); + } + + switch (req->xfer_type) + { + case UDI_USBXFER_ISO_WRITE: + req->isoc_req = NULL; + break; + + case UDI_USBXFER_ISO_READ: + req->isoc_req = NULL; + break; + + case UDI_USBXFER_BULK_WRITE: + req->bulk_req = NULL; + break; + + case UDI_USBXFER_BULK_READ: + req->bulk_req = NULL; + break; + + case UDI_USBXFER_INTR_READ: + req->intr_req = NULL; + break; + + default: + cmn_err (CE_WARN, "Internal error - bad transfer type %d\n", + req->xfer_type); + } + + KERNEL_FREE (req); +} + +/*ARGSUSED*/ +static void +isoc_play_callback (usb_pipe_handle_t ph, usb_isoc_req_t * isoc_req) +{ + udi_usb_request_t *req = + (udi_usb_request_t *) isoc_req->isoc_client_private; + + usb_free_isoc_req (isoc_req); + + req->isoc_req = NULL; + req->active = 0; + + req->callback (req, req->callback_arg); +} + +/*ARGSUSED*/ +static void +isoc_rec_callback (usb_pipe_handle_t ph, usb_isoc_req_t * isoc_req) +{ + int i; + udi_usb_request_t *req = + (udi_usb_request_t *) isoc_req->isoc_client_private; + + req->data = isoc_req->isoc_data; + + for (i = 0; i < isoc_req->isoc_pkts_count; i++) + { + int len; + + len = isoc_req->isoc_pkt_descr[i].isoc_pkt_actual_length; + + req->actlen = len; + + req->callback (req, req->callback_arg); + + req->data->b_rptr += len; + } + + req->active = 0; + usb_free_isoc_req (isoc_req); +} + +/*ARGSUSED*/ +static void +isoc_exc_callback (usb_pipe_handle_t ph, usb_isoc_req_t * isoc_req) +{ + udi_usb_request_t *req = + (udi_usb_request_t *) isoc_req->isoc_client_private; + usb_cr_t reason; + + reason = isoc_req->isoc_completion_reason; + + usb_free_isoc_req (isoc_req); + req->isoc_req = NULL; + req->active = 0; + + if (reason && reason != USB_CR_FLUSHED && reason != USB_CR_PIPE_CLOSING) + cmn_err (CE_CONT, "USB isoc transfer completion status %s\n", + usb_cr_err (reason)); +} + +/*ARGSUSED*/ +void +bulk_play_callback (usb_pipe_handle_t ph, usb_bulk_req_t * bulk_req) +{ + udi_usb_request_t *req = + (udi_usb_request_t *) bulk_req->bulk_client_private; + + usb_free_bulk_req (bulk_req); + + req->bulk_req = NULL; + req->active = 0; + + req->callback (req, req->callback_arg); +} + +/*ARGSUSED*/ +void +bulk_rec_callback (usb_pipe_handle_t ph, usb_bulk_req_t * bulk_req) +{ + udi_usb_request_t *req = + (udi_usb_request_t *) bulk_req->bulk_client_private; + + req->data = bulk_req->bulk_data; + req->actlen = bulk_req->bulk_len; + req->active = 0; + + req->callback (req, req->callback_arg); + //usb_free_bulk_req (bulk_req); +} + +/*ARGSUSED*/ +static void +bulk_exc_callback (usb_pipe_handle_t ph, usb_bulk_req_t * bulk_req) +{ + udi_usb_request_t *req = + (udi_usb_request_t *) bulk_req->bulk_client_private; + usb_cr_t reason; + + reason = bulk_req->bulk_completion_reason; + + usb_free_bulk_req (bulk_req); + req->bulk_req = NULL; + req->active = 0; + + if (reason && reason != USB_CR_FLUSHED && reason != USB_CR_PIPE_CLOSING) + cmn_err (CE_CONT, "USB bulk transfer completion status %s\n", + usb_cr_err (reason)); +} + +/*ARGSUSED*/ +void +intr_rec_callback (usb_pipe_handle_t ph, usb_intr_req_t * intr_req) +{ + udi_usb_request_t *req = + (udi_usb_request_t *) intr_req->intr_client_private; + + req->data = intr_req->intr_data; + req->actlen = intr_req->intr_len; + req->active = 0; + + req->callback (req, req->callback_arg); + //usb_free_intr_req (intr_req); +} + +/*ARGSUSED*/ +static void +intr_exc_callback (usb_pipe_handle_t ph, usb_intr_req_t * intr_req) +{ + udi_usb_request_t *req = + (udi_usb_request_t *) intr_req->intr_client_private; + usb_cr_t reason; + + reason = intr_req->intr_completion_reason; + + usb_free_intr_req (intr_req); + req->intr_req = NULL; + req->active = 0; + + if (reason && reason != USB_CR_FLUSHED && reason != USB_CR_PIPE_CLOSING) + cmn_err (CE_CONT, "USB intr transfer completion status %s\n", + usb_cr_err (reason)); +} + +/*ARGSUSED*/ +int +udi_usb_submit_request (udi_usb_request_t * req, + udi_usb_complete_func_t callback, void *callback_arg, + udi_endpoint_handle_t * eph, int xfer_type, + void *data, int len) +{ + int err; + + req->callback = callback; + req->callback_arg = callback_arg; + + if (req->active) + return 0; + + switch (xfer_type) + { + case UDI_USBXFER_ISO_WRITE: + { + usb_isoc_req_t *isoc_req; + + if (req->isoc_req != NULL) + isoc_req = req->isoc_req; + else + { + req->data = allocb (len, BPRI_HI); + if (req->data == NULL) + { + cmn_err (CE_WARN, "allocb_wait (isoc) failed\n"); + KERNEL_FREE (req); + return OSS_ENOMEM; + } + + if ((isoc_req = usb_alloc_isoc_req (req->dip, 1, 0, 0)) == NULL) + { + cmn_err (CE_WARN, "usb_alloc_isoc_req failed\n"); + freemsg (req->data); + KERNEL_FREE (req); + return OSS_ENOMEM; + } + req->isoc_req = isoc_req; + } + + if (isoc_req == NULL) + { + cmn_err (CE_WARN, "req->isoc==NULL\n"); + return OSS_EIO; + } + + memcpy (req->data->b_wptr, data, len); + req->data->b_wptr += len; + + isoc_req->isoc_data = req->data; + isoc_req->isoc_pkt_descr[0].isoc_pkt_length = len; + isoc_req->isoc_pkts_count = 1; + isoc_req->isoc_pkts_length = len; + isoc_req->isoc_attributes = + USB_ATTRS_ISOC_XFER_ASAP | USB_ATTRS_AUTOCLEARING; + isoc_req->isoc_cb = isoc_play_callback; + isoc_req->isoc_exc_cb = isoc_exc_callback; + isoc_req->isoc_client_private = (usb_opaque_t) req; + + if ((err = + usb_pipe_isoc_xfer (req->pipe_handle, isoc_req, + 0)) != USB_SUCCESS) + { + cmn_err (CE_WARN, "usb_pipe_isoc_xfer failed (%s)\n", + usb_errstr (err)); + return OSS_EIO; + } + req->active = 1; + } + break; + + case UDI_USBXFER_ISO_READ: + { + usb_isoc_req_t *isoc_req; + + if (req->isoc_req != NULL) + isoc_req = req->isoc_req; + else + { + if ((isoc_req = + usb_alloc_isoc_req (req->dip, 1, len, + USB_FLAGS_SLEEP)) == NULL) + { + cmn_err (CE_WARN, "usb_alloc_isoc_req failed\n"); + KERNEL_FREE (req); + return OSS_ENOMEM; + } + req->isoc_req = isoc_req; + } + + if (isoc_req == NULL) + { + cmn_err (CE_WARN, "req->isoc==NULL\n"); + return OSS_EIO; + } + + isoc_req->isoc_attributes = USB_ATTRS_ISOC_XFER_ASAP; + isoc_req->isoc_cb = isoc_rec_callback; + isoc_req->isoc_exc_cb = isoc_exc_callback; + isoc_req->isoc_client_private = (usb_opaque_t) req; + isoc_req->isoc_pkt_descr[0].isoc_pkt_length = len; + isoc_req->isoc_pkts_count = 1; + isoc_req->isoc_pkts_length = len; + req->active = 1; + + if ((err = + usb_pipe_isoc_xfer (req->pipe_handle, isoc_req, + USB_FLAGS_NOSLEEP)) != USB_SUCCESS) + { + cmn_err (CE_WARN, "usb_pipe_isoc_xfer failed (%s)\n", + usb_errstr (err)); + return OSS_EIO; + } + } + break; + + case UDI_USBXFER_BULK_WRITE: + { + usb_bulk_req_t *bulk_req; + + if (req->bulk_req != NULL) + bulk_req = req->bulk_req; + else + { + req->data = allocb (len, BPRI_HI); + if (req->data == NULL) + { + cmn_err (CE_WARN, "allocb_wait (bulk) failed\n"); + KERNEL_FREE (req); + return OSS_ENOMEM; + } + + if ((bulk_req = usb_alloc_bulk_req (req->dip, len, 0)) == NULL) + { + cmn_err (CE_WARN, "usb_alloc_bulk_req failed\n"); + freemsg (req->data); + KERNEL_FREE (req); + return OSS_ENOMEM; + } + req->bulk_req = bulk_req; + } + + if (bulk_req == NULL) + { + cmn_err (CE_WARN, "req->bulk==NULL\n"); + return OSS_EIO; + } + + memcpy (req->data->b_wptr, data, len); + req->data->b_wptr += len; + + bulk_req->bulk_data = req->data; + bulk_req->bulk_len = len; + bulk_req->bulk_timeout = 5; /* 5 seconds */ + bulk_req->bulk_attributes = USB_ATTRS_AUTOCLEARING; + bulk_req->bulk_cb = bulk_play_callback; + bulk_req->bulk_exc_cb = bulk_exc_callback; + bulk_req->bulk_client_private = (usb_opaque_t) req; + + if ((err = + usb_pipe_bulk_xfer (req->pipe_handle, bulk_req, + 0)) != USB_SUCCESS) + { + cmn_err (CE_WARN, "usb_pipe_bulk_xfer failed (%s)\n", + usb_errstr (err)); + return OSS_EIO; + } + req->active = 1; + } + break; + + case UDI_USBXFER_BULK_READ: + { + usb_bulk_req_t *bulk_req; + + if (req->bulk_req != NULL) + bulk_req = req->bulk_req; + else + { +#if 0 + req->data = allocb (len, BPRI_HI); + if (req->data == NULL) + { + cmn_err (CE_WARN, "allocb_wait (bulk) failed\n"); + KERNEL_FREE (req); + return OSS_ENOMEM; + } +#endif + + if ((bulk_req = + usb_alloc_bulk_req (req->dip, len, USB_FLAGS_SLEEP)) == NULL) + { + cmn_err (CE_WARN, "usb_alloc_bulk_req failed\n"); + freemsg (req->data); + KERNEL_FREE (req); + return OSS_ENOMEM; + } + req->bulk_req = bulk_req; + } + + if (bulk_req == NULL) + { + cmn_err (CE_WARN, "req->bulk==NULL\n"); + return OSS_EIO; + } + + bulk_req->bulk_attributes = USB_ATTRS_SHORT_XFER_OK; + bulk_req->bulk_cb = bulk_rec_callback; + bulk_req->bulk_exc_cb = bulk_exc_callback; + bulk_req->bulk_client_private = (usb_opaque_t) req; + // bulk_req->bulk_data = data; + bulk_req->bulk_len = len; + bulk_req->bulk_timeout = 0x7fffffff; /* As long as possible */ + req->active = 1; + + if ((err = + usb_pipe_bulk_xfer (req->pipe_handle, bulk_req, + USB_FLAGS_NOSLEEP)) != USB_SUCCESS) + { + cmn_err (CE_WARN, "usb_pipe_bulk_xfer failed (%s)\n", + usb_errstr (err)); + return OSS_EIO; + } + } + break; + + case UDI_USBXFER_INTR_READ: + { + usb_intr_req_t *intr_req; + + if (req->intr_req != NULL) + intr_req = req->intr_req; + else + { +#if 0 + req->data = allocb (len, BPRI_HI); + if (req->data == NULL) + { + cmn_err (CE_WARN, "allocb_wait (intr) failed\n"); + KERNEL_FREE (req); + return OSS_ENOMEM; + } +#endif + + if ((intr_req = + usb_alloc_intr_req (req->dip, len, USB_FLAGS_SLEEP)) == NULL) + { + cmn_err (CE_WARN, "usb_alloc_intr_req failed\n"); + freemsg (req->data); + KERNEL_FREE (req); + return OSS_ENOMEM; + } + req->intr_req = intr_req; + } + + if (intr_req == NULL) + { + cmn_err (CE_WARN, "req->intr==NULL\n"); + return OSS_EIO; + } + + intr_req->intr_attributes = + USB_ATTRS_SHORT_XFER_OK | USB_ATTRS_ONE_XFER; + intr_req->intr_cb = intr_rec_callback; + intr_req->intr_exc_cb = intr_exc_callback; + intr_req->intr_client_private = (usb_opaque_t) req; + intr_req->intr_data = NULL; + intr_req->intr_len = len; + intr_req->intr_timeout = 0x7fffffff; /* As long as possible */ + req->active = 1; + + if ((err = + usb_pipe_intr_xfer (req->pipe_handle, intr_req, + 0)) != USB_SUCCESS) + { + cmn_err (CE_WARN, "usb_pipe_intr_xfer failed (%s)\n", + usb_errstr (err)); + return OSS_EIO; + } + } + break; + + default: + cmn_err (CE_WARN, "Unimplemented transfer type %d\n", xfer_type); + return OSS_EIO; + } + + return 0; +} + +int +udi_usb_request_actlen (udi_usb_request_t * request) +{ + return request->actlen; +} + +unsigned char * +udi_usb_request_actdata (udi_usb_request_t * request) +{ + return request->data->b_rptr; +} + +void +udi_usb_cancel_request (udi_usb_request_t * req) +{ + if (req == NULL || !req->active) + return; + + req->active = 0; + usb_pipe_reset (req->dip, req->pipe_handle, USB_FLAGS_SLEEP, NULL, 0); + + switch (req->xfer_type) + { + case UDI_USBXFER_ISO_WRITE: + break; + + case UDI_USBXFER_ISO_READ: + usb_pipe_stop_isoc_polling (req->pipe_handle, USB_FLAGS_SLEEP); + //if (req->isoc_req!=NULL) + //usb_free_isoc_req(req->isoc_req); + req->isoc_req = NULL; + break; + + case UDI_USBXFER_BULK_WRITE: + break; + + case UDI_USBXFER_BULK_READ: + req->bulk_req = NULL; + break; + + case UDI_USBXFER_INTR_READ: + req->intr_req = NULL; + break; + + default: + cmn_err (CE_WARN, "Internal error - bad transfer type %d\n", + req->xfer_type); + } +} +#endif |