/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* dcopy.c
* dcopy misc module
*/
#include <sys/sysmacros.h>
#include <sys/dcopy_device.h>
/* Number of entries per channel to allocate */
typedef struct dcopy_list_s {
} dcopy_list_t;
/* device state for register/unregister */
struct dcopy_device_s {
/* DMA device drivers private pointer */
void *dc_device_private;
/* to track list of channels from this DMA device */
/*
* dc_removing_cnt track how many channels still have to be freed up
* before it's safe to allow the DMA device driver to detach.
*/
};
typedef struct dcopy_stats_s {
/* DMA channel state */
struct dcopy_channel_s {
/* DMA driver channel private pointer */
void *ch_channel_private;
/* shortcut to device callbacks */
/*
* number of outstanding allocs for this channel. used to track when
* it's safe to free up this channel so the DMA device driver can
* detach.
*/
/* state for if channel needs to be removed when ch_ref_cnt gets to 0 */
/*
* per channel list of commands actively blocking waiting for
* completion.
*/
/* pointer back to our device */
};
/*
* If grabbing both device_list mutex & globalchan_list mutex,
* Always grab globalchan_list mutex before device_list mutex
*/
typedef struct dcopy_state_s {
/* Module Driver Info */
"dcopy kernel module"
};
/* Module Linkage */
};
static int dcopy_init();
static void dcopy_fini();
/*
* _init()
*/
int
_init()
{
int e;
e = dcopy_init();
if (e != 0) {
return (e);
}
return (mod_install(&dcopy_modlinkage));
}
/*
* _info()
*/
int
{
}
/*
* _fini()
*/
int
_fini()
{
int e;
e = mod_remove(&dcopy_modlinkage);
if (e != 0) {
return (e);
}
dcopy_fini();
return (e);
}
/*
* dcopy_init()
*/
static int
{
int e;
/* Initialize the list we use to track device register/unregister */
sizeof (struct dcopy_device_s),
if (e != DCOPY_SUCCESS) {
goto dcopyinitfail_device;
}
/* Initialize the list we use to track all DMA channels */
sizeof (struct dcopy_channel_s),
if (e != DCOPY_SUCCESS) {
goto dcopyinitfail_global;
}
return (0);
return (-1);
}
/*
* dcopy_fini()
*/
static void
{
/*
* if mod_remove was successfull, we shouldn't have any
*/
}
/* *** EXTERNAL INTERFACE *** */
/*
* dcopy_query()
*/
void
{
}
/*
* dcopy_alloc()
*/
/*ARGSUSED*/
int
{
/*
* we don't use the dcopy_list_* code here because we need to due
* some non-standard stuff.
*/
/*
* if nothing is on the channel list, return DCOPY_NORESOURCES. This
* can happen if there aren't any DMA device registered.
*/
return (DCOPY_NORESOURCES);
}
/*
* increment the reference count, and pop the channel off the head and
* push it on the tail. This ensures we rotate through the channels.
* DMA channels are shared.
*/
channel->ch_ref_cnt++;
return (DCOPY_SUCCESS);
}
/*
* dcopy_free()
*/
void
{
/*
* we don't need to add the channel back to the list since we never
* removed it. decrement the reference count.
*/
(*channel)->ch_ref_cnt--;
/*
* if we need to remove this channel, and the reference count is down
* to 0, decrement the number of channels which still need to be
* removed on the device.
*/
if (device->dc_removing_cnt == 0) {
}
}
/*
* if there are no channels which still need to be removed, cleanup the
* device state and call back into the DMA device driver to tell them
* the device is free.
*/
if (cleanup) {
}
}
/*
* dcopy_query_channel()
*/
void
{
}
/*
* dcopy_cmd_alloc()
*/
int
{
int e;
cmd);
if (e == DCOPY_SUCCESS) {
/*
* we won't initialize the blocking state until we actually
* need to block.
*/
}
return (e);
}
/*
* dcopy_cmd_free()
*/
void
{
/* if we initialized the blocking state, clean it up too */
if (priv->pr_block_init) {
}
}
/*
* dcopy_cmd_post()
*/
int
{
int e;
}
if (e != DCOPY_SUCCESS) {
return (e);
}
return (DCOPY_SUCCESS);
}
/*
* dcopy_cmd_poll()
*/
int
{
int e;
/*
* if the caller is trying to block, they needed to post the
* command with DCOPY_CMD_INTR set.
*/
return (DCOPY_FAILURE);
}
if (e == DCOPY_PENDING) {
/*
* if the command is still active, and the blocking flag
* is set.
*/
if (flags & DCOPY_POLL_BLOCK) {
/*
* if we haven't initialized the state, do it now. A
* command can be re-used, so it's possible it's
* already been initialized.
*/
if (!priv->pr_block_init) {
NULL);
}
/* push it on the list for blocking commands */
/*
* it's possible we already cleared pr_wait before we
* grabbed the mutex.
*/
}
/*
* the command has completed, go back and poll so we
* get the status.
*/
goto repoll;
}
}
return (e);
}
/* *** END OF EXTERNAL INTERFACE *** */
/*
* dcopy_list_init()
*/
static int
{
return (DCOPY_SUCCESS);
}
/*
* dcopy_list_fini()
*/
static void
{
}
/*
* dcopy_list_push()
*/
static void
{
}
/*
* dcopy_list_pop()
*/
static void *
{
return (list_node);
}
return (list_node);
}
/* *** DEVICE INTERFACE *** */
/*
* dcopy_device_register()
*/
int
{
int e;
int i;
/* initialize the per device state */
device->dc_removing_cnt = 0;
/*
* we have a per device channel list so we can remove a device in the
* future.
*/
sizeof (struct dcopy_channel_s),
if (e != DCOPY_SUCCESS) {
goto registerfail_devchan;
}
/*
* allocate state for each channel, allocate the channel, and then add
* the devices dma channels to the devices channel list.
*/
for (i = 0; i < info->di_num_dma; i++) {
channel->ch_ref_cnt = 0;
if (e != DCOPY_SUCCESS) {
goto registerfail_alloc;
}
e = dcopy_stats_init(channel);
if (e != DCOPY_SUCCESS) {
goto registerfail_alloc;
}
sizeof (struct dcopy_cmd_priv_s),
if (e != DCOPY_SUCCESS) {
goto registerfail_alloc;
}
}
/* add the device to device list */
/*
* add the device's dma channels to the global channel list (where
* dcopy_alloc's come from)
*/
channel);
}
/* last call-back into kernel for dcopy KAPI enabled */
return (DCOPY_SUCCESS);
/* remove from the list */
}
return (DCOPY_FAILURE);
}
/*
* dcopy_device_unregister()
*/
/*ARGSUSED*/
int
{
/* first call-back into kernel for dcopy KAPI disable */
/*
* remove the devices dma channels from the global channel list (where
* dcopy_alloc's come from)
*/
/*
* if the channel has outstanding allocs, mark it as having
* to be removed and increment the number of channels which
* need to be removed in the device state too.
*/
if (channel->ch_ref_cnt != 0) {
}
}
/*
* if there are channels which still need to be removed, we will clean
* up the device state after they are freed up.
*/
if (device_busy) {
return (DCOPY_PENDING);
}
return (DCOPY_SUCCESS);
}
/*
* dcopy_device_cleanup()
*/
static void
{
/*
* remove all the channels in the device list, free them, and clean up
* the state.
*/
}
/* remove it from the list of devices */
/*
* notify the DMA device driver that the device is free to be
* detached.
*/
if (do_callback) {
}
}
/*
* dcopy_device_channel_notify()
*/
/*ARGSUSED*/
void
{
int e;
/*
* when we get a completion notification from the device, go through
* all of the commands blocking on this channel and see if they have
* completed. Remove the command and wake up the block thread if they
* have. Once we hit a command which is still pending, we are done
* polling since commands in a channel complete in order.
*/
if (e == DCOPY_PENDING) {
break;
}
}
}
}
/*
* dcopy_stats_init()
*/
static int
{
int instance;
char *name;
return (DCOPY_FAILURE);
}
return (DCOPY_SUCCESS);
}
/*
* dcopy_stats_fini()
*/
static void
{
}
/* *** END OF DEVICE INTERFACE *** */