summaryrefslogtreecommitdiff
path: root/src/ipa/rkisp1
AgeCommit message (Expand)Author
2024-09-23ipa: rkisp1: Add polynomial LSC loaderStefan Klug
2024-09-23ipa: rkisp1: Add sensor info to contextStefan Klug
2024-09-23ipa: rkisp1: Move loader functions into helper classStefan Klug
2024-09-23ipa: rkisp1: Use interpolator in lscStefan Klug
2024-09-23ipa: rkisp1: Use generic Interpolator classStefan Klug
2024-09-03libcamera: ipa: Drop unneded includes from ipa_interface.hLaurent Pinchart
2024-09-02libcamera: rkisp1: Formatting improvementsMilan Zamazal
2024-09-02libcamera: rkisp1: Remove unused includesMilan Zamazal
2024-08-27ipa: rkisp1: blc: Add support for BLS in compandPaul Elder
2024-08-27ipa: rkisp1: Add compand feature flag to ipa contextPaul Elder
2024-08-27ipa: rkisp1: params: Add companding blocksLaurent Pinchart
2024-08-27ipa: rkisp1: Use the new ISP parameters abstractionLaurent Pinchart
2024-08-27ipa: rkisp1: Add ISP parameters abstraction classLaurent Pinchart
2024-08-27ipa: rkisp1: Pass parameters buffer size to pipeline handlerJacopo Mondi
2024-08-27ipa: rkisp1: Pass parameters buffer format to IPA moduleLaurent Pinchart
2024-08-07libcamera: Drop libcamera_generated_ipa_headers from sourcesLaurent Pinchart
2024-07-24ipa: rkisp1: ccm: Ensure metadata contains valid ccmStefan Klug
2024-07-24ipa: rkisp1: ccm: Fix ccm metadata outputStefan Klug
2024-07-24ipa: rkisp1: awb: Unconditionally fill metadataStefan Klug
2024-07-24ipa rkisp1: Remove temperatureK from FrameContextStefan Klug
2024-07-24ipa: rkisp1: awb: Clamp gains to machine limitsStefan Klug
2024-07-06ipa: rkisp1: Install all tuning filesRobert Mader
2024-07-04ipa: rkisp1: blc: Drop [[maybe_unused]] attributeLaurent Pinchart
2024-07-03ipa: rkisp1: Move ov4689 and ov5640 black levels into sensor helpersStefan Klug
2024-07-03ipa: rkisp1: data: Update tuning files for imx219 and imx258Stefan Klug
2024-07-03ipa: rkisp1: blc: Report sensor black levels in metadataStefan Klug
2024-07-03ipa: rkisp1: blc: Query black levels from camera sensor helperStefan Klug
2024-07-03ipa: rkisp1: Move camHelper into IPAContextStefan Klug
2024-06-24ipa: rkisp1: agc: Rename frame context update variable to updateMeteringLaurent Pinchart
2024-06-17ipa: rkisp1: agc: Move AeEnable control to the AGC algorithmLaurent Pinchart
2024-06-17ipa: rkisp1: agc: Correctly clamp maximum shutter speedLaurent Pinchart
2024-06-17ipa: rkisp1: agc: Rename maxShutterSpeed to maxFrameDurationLaurent Pinchart
2024-06-17ipa: rkisp1: agc: Use mode from frame context to calculate new EVLaurent Pinchart
2024-06-17ipa: rkisp1: agc: Simplify predivider calculationLaurent Pinchart
2024-06-17ipa: rkisp1: agc: Don't update histogram parameters unnecessarilyLaurent Pinchart
2024-06-17ipa: rkisp1: agc: Make size argument to computeHistogramPredivider constLaurent Pinchart
2024-06-17ipa: rkisp1: Document all AGC parameters in IPAFrameContextLaurent Pinchart
2024-06-17ipa: rkisp1: Document all AGC parameters in IPAActiveStateLaurent Pinchart
2024-06-17ipa: rkisp1: agc: Fix initialization without metering modesLaurent Pinchart
2024-06-17ipa: rkisp1: algorithms: Add crosstalk algorithmPaul Elder
2024-06-14ipa: rkisp1: agc: Plumb mode-selection and frame duration controlsPaul Elder
2024-06-14ipa: rkisp1: agc: Read histogram weights from tuning filePaul Elder
2024-06-13ipa: rkisp1: cproc: Drop incorrect [[maybe_unused]]Stefan Klug
2024-06-13ipa: rkisp1: cproc: Merge identical functionsStefan Klug
2024-06-13ipa: rkisp1: cproc: Use anonymous namespace to limit symbol visibilityStefan Klug
2024-06-12ipa: rkisp1: goc: Avoid use of auto for short typeLaurent Pinchart
2024-06-12ipa: rkisp1: goc: Mark default gamma table as static constexprLaurent Pinchart
2024-06-12ipa: rkisp1: goc: Use copydocLaurent Pinchart
2024-06-12ipa: rkisp1: goc: Drop incorrect [[maybe_unused]]Laurent Pinchart
2024-06-12ipa: rkisp1: goc: Fix typographical issues in documentation.Laurent Pinchart
/a> 337 338 339 340 341 342 343 344 345
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
 * Copyright (C) 2019, Google Inc.
 *
 * ipc_unixsocket.cpp - IPC mechanism based on Unix sockets
 */

#include "libcamera/internal/ipc_unixsocket.h"

#include <poll.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#include "libcamera/internal/log.h"

/**
 * \file ipc_unixsocket.h
 * \brief IPC mechanism based on Unix sockets
 */

namespace libcamera {

LOG_DEFINE_CATEGORY(IPCUnixSocket)

/**
 * \struct IPCUnixSocket::Payload
 * \brief Container for an IPC payload
 *
 * Holds an array of bytes and an array of file descriptors that can be
 * transported across a IPC boundary.
 */

/**
 * \var IPCUnixSocket::Payload::data
 * \brief Array of bytes to cross IPC boundary
 */

/**
 * \var IPCUnixSocket::Payload::fds
 * \brief Array of file descriptors to cross IPC boundary
 */

/**
 * \class IPCUnixSocket
 * \brief IPC mechanism based on Unix sockets
 *
 * The Unix socket IPC allows bidirectional communication between two processes
 * through unnamed Unix sockets. It implements datagram-based communication,
 * transporting entire payloads with guaranteed ordering.
 *
 * The IPC design is asynchronous, a message is queued to a receiver which gets
 * notified that a message is ready to be consumed by the \ref readyRead
 * signal. The sender of the message gets no notification when a message is
 * delivered nor processed. If such interactions are needed a protocol specific
 * to the users use-case should be implemented on top of the IPC objects.
 *
 * Establishment of an IPC channel is asymmetrical. The side that initiates
 * communication first instantiates a local side socket and creates the channel
 * with create(). The method returns a file descriptor for the remote side of
 * the channel, which is passed to the remote process through an out-of-band
 * communication method. The remote side then instantiates a socket, and binds
 * it to the other side by passing the file descriptor to bind(). At that point
 * the channel is operation and communication is bidirectional and symmmetrical.
 *
 * \context This class is \threadbound.
 */

IPCUnixSocket::IPCUnixSocket()
	: fd_(-1), headerReceived_(false), notifier_(nullptr)
{
}

IPCUnixSocket::~IPCUnixSocket()
{
	close();
}

/**
 * \brief Create an new IPC channel
 *
 * This method creates a new IPC channel. The socket instance is bound to the
 * local side of the channel, and the method returns a file descriptor bound to
 * the remote side. The caller is responsible for passing the file descriptor to
 * the remote process, where it can be used with IPCUnixSocket::bind() to bind
 * the remote side socket.
 *
 * \return A file descriptor on success, negative error code on failure
 */
int IPCUnixSocket::create()
{
	int sockets[2];
	int ret;

	ret = socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, sockets);
	if (ret) {
		ret = -errno;
		LOG(IPCUnixSocket, Error)
			<< "Failed to create socket pair: " << strerror(-ret);
		return ret;
	}

	ret = bind(sockets[0]);
	if (ret)
		return ret;

	return sockets[1];
}

/**
 * \brief Bind to an existing IPC channel
 * \param[in] fd File descriptor
 *
 * This method binds the socket instance to an existing IPC channel identified
 * by the file descriptor \a fd. The file descriptor is obtained from the
 * IPCUnixSocket::create() method.
 *
 * \return 0 on success or a negative error code otherwise
 */
int IPCUnixSocket::bind(int fd)
{
	if (isBound())
		return -EINVAL;

	fd_ = fd;
	notifier_ = new EventNotifier(fd_, EventNotifier::Read);
	notifier_->activated.connect(this, &IPCUnixSocket::dataNotifier);

	return 0;
}

/**
 * \brief Close the IPC channel
 *
 * No communication is possible after close() has been called.
 */
void IPCUnixSocket::close()
{
	if (!isBound())
		return;

	delete notifier_;
	notifier_ = nullptr;

	::close(fd_);

	fd_ = -1;
	headerReceived_ = false;
}

/**
 * \brief Check if the IPC channel is bound
 * \return True if the IPC channel is bound, false otherwise
 */
bool IPCUnixSocket::isBound() const
{
	return fd_ != -1;
}

/**
 * \brief Send a message payload
 * \param[in] payload Message payload to send
 *
 * This method queues the message payload for transmission to the other end of
 * the IPC channel. It returns immediately, before the message is delivered to
 * the remote side.
 *
 * \return 0 on success or a negative error code otherwise
 */
int IPCUnixSocket::send(const Payload &payload)
{
	int ret;

	if (!isBound())
		return -ENOTCONN;

	Header hdr = {};
	hdr.data = payload.data.size();
	hdr.fds = payload.fds.size();

	if (!hdr.data && !hdr.fds)
		return -EINVAL;

	ret = ::send(fd_, &hdr, sizeof(hdr), 0);
	if (ret < 0) {
		ret = -errno;
		LOG(IPCUnixSocket, Error)
			<< "Failed to send: " << strerror(-ret);
		return ret;
	}

	return sendData(payload.data.data(), hdr.data, payload.fds.data(), hdr.fds);
}

/**
 * \brief Receive a message payload
 * \param[out] payload Payload where to write the received message
 *
 * This method receives the message payload from the IPC channel and writes it
 * to the \a payload. If no message payload is available, it returns
 * immediately with -EAGAIN. The \ref readyRead signal shall be used to receive
 * notification of message availability.
 *
 * \todo Add state machine to make sure we don't block forever and that
 * a header is always followed by a payload.
 *
 * \return 0 on success or a negative error code otherwise
 * \retval -EAGAIN No message payload is available
 * \retval -ENOTCONN The socket is not connected (neither create() nor bind()
 * has been called)
 */
int IPCUnixSocket::receive(Payload *payload)
{
	if (!isBound())
		return -ENOTCONN;

	if (!headerReceived_)
		return -EAGAIN;

	payload->data.resize(header_.data);
	payload->fds.resize(header_.fds);

	int ret = recvData(payload->data.data(), header_.data,
			   payload->fds.data(), header_.fds);
	if (ret < 0)
		return ret;

	headerReceived_ = false;
	notifier_->setEnabled(true);

	return 0;
}

/**
 * \var IPCUnixSocket::readyRead
 * \brief A Signal emitted when a message is ready to be read
 */

int IPCUnixSocket::sendData(const void *buffer, size_t length,
			    const int32_t *fds, unsigned int num)
{
	struct iovec iov[1];
	iov[0].iov_base = const_cast<void *>(buffer);
	iov[0].iov_len = length;

	char buf[CMSG_SPACE(num * sizeof(uint32_t))];
	memset(buf, 0, sizeof(buf));

	struct cmsghdr *cmsg = (struct cmsghdr *)buf;
	cmsg->cmsg_len = CMSG_LEN(num * sizeof(uint32_t));
	cmsg->cmsg_level = SOL_SOCKET;
	cmsg->cmsg_type = SCM_RIGHTS;

	struct msghdr msg;
	msg.msg_name = nullptr;
	msg.msg_namelen = 0;
	msg.msg_iov = iov;
	msg.msg_iovlen = 1;
	msg.msg_control = cmsg;
	msg.msg_controllen = cmsg->cmsg_len;
	msg.msg_flags = 0;
	memcpy(CMSG_DATA(cmsg), fds, num * sizeof(uint32_t));

	if (sendmsg(fd_, &msg, 0) < 0) {
		int ret = -errno;
		LOG(IPCUnixSocket, Error)
			<< "Failed to sendmsg: " << strerror(-ret);
		return ret;
	}

	return 0;
}

int IPCUnixSocket::recvData(void *buffer, size_t length,
			    int32_t *fds, unsigned int num)
{
	struct iovec iov[1];
	iov[0].iov_base = buffer;
	iov[0].iov_len = length;

	char buf[CMSG_SPACE(num * sizeof(uint32_t))];
	memset(buf, 0, sizeof(buf));

	struct cmsghdr *cmsg = (struct cmsghdr *)buf;
	cmsg->cmsg_len = CMSG_LEN(num * sizeof(uint32_t));
	cmsg->cmsg_level = SOL_SOCKET;
	cmsg->cmsg_type = SCM_RIGHTS;

	struct msghdr msg;
	msg.msg_name = nullptr;
	msg.msg_namelen = 0;
	msg.msg_iov = iov;
	msg.msg_iovlen = 1;
	msg.msg_control = cmsg;
	msg.msg_controllen = cmsg->cmsg_len;
	msg.msg_flags = 0;

	if (recvmsg(fd_, &msg, 0) < 0) {
		int ret = -errno;
		if (ret != -EAGAIN)
			LOG(IPCUnixSocket, Error)
				<< "Failed to recvmsg: " << strerror(-ret);
		return ret;
	}

	memcpy(fds, CMSG_DATA(cmsg), num * sizeof(uint32_t));

	return 0;
}

void IPCUnixSocket::dataNotifier(EventNotifier *notifier)
{
	int ret;

	if (!headerReceived_) {
		/* Receive the header. */
		ret = ::recv(fd_, &header_, sizeof(header_), 0);
		if (ret < 0) {
			ret = -errno;
			LOG(IPCUnixSocket, Error)
				<< "Failed to receive header: " << strerror(-ret);
			return;
		}

		headerReceived_ = true;
	}

	/*
	 * If the payload has arrived, disable the notifier and emit the
	 * readyRead signal. The notifier will be reenabled by the receive()
	 * method.
	 */
	struct pollfd fds = { fd_, POLLIN, 0 };
	ret = poll(&fds, 1, 0);
	if (ret < 0)
		return;

	if (!(fds.revents & POLLIN))
		return;

	notifier_->setEnabled(false);
	readyRead.emit(this);
}

} /* namespace libcamera */