⛏️ index : haiku.git

/*
 * Copyright 2004, Axel DΓΆrfler, axeld@pinc-software.de. All rights reserved.
 * Distributed under the terms of the MIT License.
 */

#include <new>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <lock.h>

#include "tty_driver.h"
#include "tty_private.h"


//#define TTY_TRACE
#ifdef TTY_TRACE
#	define TRACE(x) dprintf x
#else
#	define TRACE(x)
#endif

#define DRIVER_NAME "tty"

static const int kMaxCachedSemaphores = 8;

int32 api_version = B_CUR_DRIVER_API_VERSION;

char *gDeviceNames[kNumTTYs * 2 + 3];
	// reserve space for "pt/" and "tt/" entries, "ptmx", "tty", and the
	// terminating NULL

static mutex sTTYLocks[kNumTTYs];
static tty_settings sTTYSettings[kNumTTYs];

struct mutex gGlobalTTYLock;
struct mutex gTTYCookieLock;
struct recursive_lock gTTYRequestLock;


status_t
init_hardware(void)
{
	TRACE((DRIVER_NAME ": init_hardware()\n"));
	return B_OK;
}


status_t
init_driver(void)
{
	TRACE((DRIVER_NAME ": init_driver()\n"));

	memset(gDeviceNames, 0, sizeof(gDeviceNames));

	// create the request mutex
	recursive_lock_init(&gTTYRequestLock, "tty requests");

	// create the global mutex
	mutex_init(&gGlobalTTYLock, "tty global");

	// create the cookie mutex
	mutex_init(&gTTYCookieLock, "tty cookies");

	// create driver name array and initialize basic TTY structures

	char letter = 'p';
	int8 digit = 0;

	for (uint32 i = 0; i < kNumTTYs; i++) {
		// For compatibility, we have to create the same mess in /dev/pt and
		// /dev/tt as BeOS does: we publish devices p0, p1, ..., pf, r1, ...,
		// sf. It would be nice if we could drop compatibility and create
		// something better. In fact we already don't need the master devices
		// anymore, since "/dev/ptmx" does the job. The slaves entries could
		// be published on the fly when a master is opened (e.g via
		// vfs_create_special_node()).
		char buffer[64];

		snprintf(buffer, sizeof(buffer), "pt/%c%x", letter, digit);
		gDeviceNames[i] = strdup(buffer);

		snprintf(buffer, sizeof(buffer), "tt/%c%x", letter, digit);
		gDeviceNames[i + kNumTTYs] = strdup(buffer);

		if (++digit > 15)
			digit = 0, letter++;

		mutex_init(&sTTYLocks[i], "tty lock");
		reset_tty(&gMasterTTYs[i], i, &sTTYLocks[i], true);
		reset_tty(&gSlaveTTYs[i], i, &sTTYLocks[i], false);
		reset_tty_settings(&sTTYSettings[i]);

		if (!gDeviceNames[i] || !gDeviceNames[i + kNumTTYs]) {
			uninit_driver();
			return B_NO_MEMORY;
		}
	}

	gDeviceNames[2 * kNumTTYs] = (char *)"ptmx";
	gDeviceNames[2 * kNumTTYs + 1] = (char *)"tty";

	return B_OK;
}


void
uninit_driver(void)
{
	TRACE((DRIVER_NAME ": uninit_driver()\n"));

	for (int32 i = 0; i < (int32)kNumTTYs * 2; i++)
		free(gDeviceNames[i]);

	for (int32 i = 0; i < (int32)kNumTTYs; i++)
		mutex_destroy(&sTTYLocks[i]);

	recursive_lock_destroy(&gTTYRequestLock);
	mutex_destroy(&gTTYCookieLock);
	mutex_destroy(&gGlobalTTYLock);
}


const char **
publish_devices(void)
{
	TRACE((DRIVER_NAME ": publish_devices()\n"));
	return const_cast<const char **>(gDeviceNames);
}


device_hooks *
find_device(const char *name)
{
	TRACE((DRIVER_NAME ": find_device(\"%s\")\n", name));

	for (uint32 i = 0; gDeviceNames[i] != NULL; i++)
		if (!strcmp(name, gDeviceNames[i])) {
			return i < kNumTTYs || i == (2 * kNumTTYs)
				? &gMasterTTYHooks : &gSlaveTTYHooks;
		}

	return NULL;
}


int32
get_tty_index(const char* name)
{
	// device names follow this form: "pt/%c%x"
	int8 digit = name[4];
	if (digit >= 'a') {
		// hexadecimal digits
		digit -= 'a' - 10;
	} else
		digit -= '0';

	return (name[3] - 'p') * 16 + digit;
}


void
reset_tty(struct tty* tty, int32 index, mutex* lock, bool isMaster)
{
	tty->ref_count = 0;
	tty->open_count = 0;
	tty->opened_count = 0;
	tty->index = index;
	tty->lock = lock;
	tty->settings = &sTTYSettings[index];
	tty->select_pool = NULL;
	tty->is_master = isMaster;
	tty->pending_eof = 0;
}