/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* 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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <limits.h>
#include <synch.h>
#include <libintl.h>
#include <errno.h>
#include <libdevinfo.h>
#include <sys/sysmacros.h>
#include <stropts.h>
#include <fcntl.h>
#include <sys/openpromio.h>
#include <sys/ttymuxuser.h>
#include "ttymux_rcm_impl.h"
#include "rcm_module.h"
static int muxfd;
/* module interface routines */
static int tty_register(rcm_handle_t *);
static int tty_unregister(rcm_handle_t *);
char **, nvlist_t *, rcm_info_t **);
rcm_info_t **);
rcm_info_t **);
rcm_info_t **);
rcm_info_t **);
static int get_devpath(char *, char **, dev_t *);
/*
* Module-Private data
*/
NULL,
};
/*PRINTFLIKE1*/
static void
{
int sz;
if (sz < 0)
_("TTYMUX: vsnprintf parse error\n"));
if (b != NULL) {
if (sz > 0)
free(b);
}
} else {
}
}
/*
* CACHE MANAGEMENT
* Resources managed by this module are stored in a list of rsrc_t
* structures.
*/
/*
* cache_lookup()
*
* Get a cache node for a resource. Call with cache lock held.
*/
static rsrc_t *
{
while (rsrc != &cache_tail) {
return (rsrc);
}
}
return (NULL);
}
/*
* Get a cache node for a minor node. Call with cache lock held.
*/
static rsrc_t *
{
while (rsrc != &cache_tail) {
return (rsrc);
}
return (NULL);
}
/*
* free_node()
*
* Free a node. Make sure it isn't in the list!
*/
static void
{
if (node) {
}
}
}
/*
* cache_insert()
*
* Call with the cache_lock held.
*/
static void
{
/* insert at the head for best performance */
}
/*
* cache_create()
*
* Call with the cache_lock held.
*/
static rsrc_t *
{
} else {
}
} else {
_msg(0, ("TTYMUX: malloc failure for resource %s.\n",
resource));
}
return (rsrc);
}
/*
* cache_get()
*
* Call with the cache_lock held.
*/
static rsrc_t *
{
}
return (rsrc);
}
/*
* cache_remove()
*
* Call with the cache_lock held.
*/
static void
{
}
/*
* Open a file identified by fname with the given open flags.
* If the request is to open a file with exclusive access and the open
* fails then backoff exponentially and then retry the open.
* Do not wait for longer than about a second (since this may be an rcm
* framework thread).
*/
static int
{
}
return (fd);
}
/*
* No-op for creating an association between a pair of resources.
*/
/*ARGSUSED*/
static int
{
return (0);
}
/*
* No-op for destroying an association between a pair of resources.
*/
/*ARGSUSED*/
static int
{
return (0);
}
/*
* Record an actual or desired association between two resources
* identified by their rsrc_t structures.
*/
static link_t *
{
return (NULL);
"the same driver\n"));
return (NULL);
}
/*
* Search for all resources that this resource user is depending
* upon.
*/
/*
* Does the using resource already depends on the used
* resource
*/
return (link);
}
return (NULL);
}
return (link);
}
/*
* Send an I_STR stream ioctl to a device
*/
static int
int rval;
if (bytes)
return (rval);
}
/*
* Streams link the driver identified by fd underneath a mux
* identified by ctrl_fd.
*/
static int
{
int linkid;
/*
* pop any modules off the lower stream.
*/
;
_("TTYMUX: I_PLINK error %d.\n"), errno);
return (linkid);
}
/*
* Streams unlink the STREAM identified by linkid from a mux
* identified by ctrl_fd.
*/
static int
{
return (errno);
else
return (0);
}
/*
* Connect a pair of resources by establishing the dependency association.
* Only works for devices that support the TTYMUX ioctls.
*/
static int
{
int lfd;
int rv;
/*
* One of the resources in the association is not
* present (wait for the online notification before
* attempting to establish the dependency association.
*/
return (EAGAIN);
}
"the same driver\n"));
return (EINVAL);
}
/*
* Explicitly check for attempts to plumb the system console -
* required becuase not all serial devices support the
* O_EXCL open flag.
*/
" system console under another device not allowed!\n"));
return (EPERM);
}
/*
*/
/*
* Open each resource participating in the association.
*/
if (lfd == -1) {
" busy - " " cannot connect to %s\n"),
} else {
_("TTYMUX: open error %d for device %s\n"),
}
return (errno);
}
/*
* Note: Issuing the I_PLINK and TTYMUX_ASSOC request on the 'using'
* resource is more generic:
* muxfd = open(link->user->id, oflags);
* However using the ctl (MUXCTLLINK) node means that any current opens
* on the 'using' resource are uneffected.
*/
/*
* Figure out if the 'used' resource is already associated with
* some resource - if so tell the caller to try again later.
* More generally if any user or kernel thread has the resource
* open then the association should not be made.
* The ttymux driver makes this check (but it should be done here).
*/
as.ttymux_linkid = 0;
return (EAGAIN);
}
}
/*
* Now link and associate the used resource under the using resource.
*/
_("TTYMUX: error %d whilst enabling the "
"receiver on device %d:%d\n"),
}
}
}
_("TTYMUX: Link error %d for device %s\n"),
goto out;
}
goto out;
}
return (0);
out:
_("TTYMUX: Error [%d] connecting %d:%d to %d:%d\n"),
if (as.ttymux_linkid > 0) {
/*
* There was an error so unwind the I_PLINK step
*/
_("TTYMUX: Unlink error %d (%s).\n"),
}
return (rv);
}
/*
* Disconnect a pair of resources by destroying the dependency association.
* Only works for devices that support the TTYMUX ioctls.
*/
static int
{
int rv;
return (0);
/*
* Do not disassociate console resources - simply
* unlink them so that they remain persistent.
*/
sizeof (as), 0) == -1) {
_("TTYMUX: Dissassociate error %d for %s\n"),
_("TTYMUX: Error %d unlinking %d:%d\n"),
} else {
rv = 0;
}
return (rv);
}
/* PESISTENCY */
/*
* Given a special device file system path return the /devices path
*/
static int
{
return (errno);
int lsz;
return (errno);
if (lsz <= 0)
return (ENODEV);
return (ENODEV);
}
return (0);
}
/*
* See routine locate_node
*/
static int
{
char *devfspath;
return (DI_WALK_CONTINUE);
return (DI_WALK_TERMINATE);
return (DI_WALK_TERMINATE);
}
return (DI_WALK_TERMINATE);
return (DI_WALK_TERMINATE);
}
/*
* Find a devinfo node that matches the device argument (dev).
* This is an expensive search of the whole device tree!
*/
static rsrc_t *
{
return (rsrc);
return (cache_lookup_bydevt(dev));
}
/*
* Search for any existing dependency relationships managed by this
* RCM module.
*/
static int
{
int cnt, n;
if (cnt <= 0)
return (0);
return (EAGAIN);
if (n == -1) {
return (0);
}
return (errno);
(void) mutex_lock(&cache_lock);
/*
* The TTYMUX_LIST ioctl can return links relating
* to potential devices. Such devices are identified
* in the path field.
*/
continue;
continue;
} else {
}
continue;
}
continue;
}
}
}
(void) mutex_unlock(&cache_lock);
return (0);
}
/*
* A resource has become available. Re-establish any associations involving
* the resource.
*/
static int
{
/*
* Now that the resource is present obtain its device number.
* For this to work the node must be present in the /devices
* tree (see devfsadm(1M) or drvconfig(1M)).
* We do not use libdevinfo because the node must be present
* under /devices for the connect step below to work
* (the node needs to be opened).
*/
_msg(4,
/*
* What does RCM do with failed online notifications.
*/
return (RCM_FAILURE);
}
}
}
}
}
return (RCM_SUCCESS);
}
/*
* A resource is going away. Tear down any associations involving
* the resource.
*/
static int
{
}
}
}
return (RCM_SUCCESS);
}
/*
* Find any resources that are using a given resource (identified by
* the rsrc argument). The search begins after the resource identified
* by the next argument. If next is NULL start at the first resource
* in this RCM modules resource list. If the redundancy argument is
* greater than zero then a resource which uses rsrc will only be
* returned if it is associated with >= redundancy dependents.
*
* Thus, provided that the caller keeps the list locked he can iterate
* through all the resources in the cache that depend upon rsrc.
*/
static rsrc_t *
{
int cnt = 0;
while (src != &cache_tail) {
cnt++;
}
return (src);
}
}
return (NULL);
}
/*
* Common handler for RCM notifications.
*/
/*ARGSUSED*/
static int
{
(void) mutex_lock(&cache_lock);
/* shouldn't happen because rsrc has been registered */
(void) mutex_unlock(&cache_lock);
return (RCM_SUCCESS);
}
(void) mutex_unlock(&cache_lock);
return (RCM_SUCCESS);
}
switch (op) {
case TTYMUX_SUSPEND:
rv = RCM_FAILURE;
}
break;
case TTYMUX_REMOVE:
rv = RCM_SUCCESS;
break;
case TTYMUX_OFFLINE:
if (rv == RCM_FAILURE) {
} else {
}
}
if (rv == RCM_FAILURE) {
NULL) {
}
}
rv = RCM_FAILURE;
} else {
}
} else {
if (rv == RCM_FAILURE) {
NULL) {
}
}
}
if (rv == RCM_FAILURE) {
} else {
}
break;
case TTYMUX_RESUME:
rv = RCM_FAILURE;
}
break;
case TTYMUX_ONLINE:
if (rv == RCM_FAILURE) {
}
}
break;
default:
rv = RCM_FAILURE;
}
}
(void) mutex_unlock(&cache_lock);
return (rv);
}
static boolean_t
{
char *devfspath;
int nminors = 0;
return (B_FALSE);
}
if (node == DI_NODE_NIL) {
return (B_FALSE);
}
/*
* If the device is not a prom node do not continue.
*/
return (B_FALSE);
}
return (B_FALSE);
}
/*
* Loop through all the minor nodes the driver (drv) looking
* for the ctl node (this is the device on which
* to issue ioctls).
*/
dim = DI_MINOR_NIL;
"/devices%s:%s", devfspath,
}
if (++nminors == 2)
break;
"/devices%s:%s", devfspath,
}
if (++nminors == 2)
break;
}
}
sizeof (cn_dev), 0) != 0) {
} else {
}
}
return (B_TRUE);
} else {
}
return (B_FALSE);
}
/*
* Update registrations, and return the ops structure.
*/
struct rcm_mod_ops *
{
/*
* Find the multiplexer ctl and con nodes
*/
return (&tty_ops);
}
/*
* Save state and release resources.
*/
int
{
(void) mutex_lock(&cache_lock);
while (rsrc != &cache_tail) {
}
}
(void) mutex_unlock(&cache_lock);
(void) mutex_destroy(&cache_lock);
return (RCM_SUCCESS);
}
/*
* Return a string describing this module.
*/
const char *
{
return ("Serial mux device module 1.1");
}
/*
* RCM Notification Handlers
*/
static int
{
int rv;
if (register_rsrcs == B_FALSE)
return (RCM_SUCCESS);
return (RCM_SUCCESS);
}
/*
* Search for any new dependencies since the last notification or
* since module was initialisated.
*/
(void) probe_dependencies();
/*
* Search the whole cache looking for any unregistered used resources
* and register them. Note that the 'using resource' (a ttymux device
* node) is not subject to DR operations so there is no need to
* register them with the RCM framework.
*/
(void) mutex_lock(&cache_lock);
if (rv == RCM_SUCCESS)
}
continue;
0, NULL);
if (rv != RCM_SUCCESS)
_("TTYMUX: err %d registering %s\n"),
else
}
}
(void) mutex_unlock(&cache_lock);
return (RCM_SUCCESS);
}
/*
* Unregister all registrations.
*/
static int
{
(void) mutex_lock(&cache_lock);
/*
* Search every resource in the cache and if it has been registered
* then unregister it from the RCM framework.
*/
continue;
else
}
(void) mutex_unlock(&cache_lock);
return (RCM_SUCCESS);
}
/*
* Report resource usage information.
*/
/*ARGSUSED*/
static int
{
char *ru;
(void) mutex_lock(&cache_lock);
(void) mutex_unlock(&cache_lock);
return (RCM_FAILURE);
}
break;
}
}
}
(void) mutex_unlock(&cache_lock);
return (RCM_FAILURE);
}
(void) mutex_unlock(&cache_lock);
return (RCM_SUCCESS);
}
/*ARGSUSED*/
static int
{
}
/*ARGSUSED*/
static int
{
}
/*ARGSUSED*/
static int
{
}
/*ARGSUSED*/
static int
{
}
/*ARGSUSED*/
static int
{
}