
IMPORTANT: this change has a docs/UPDATING entry! This rename is unfortunately necessary because NetBSD has decided to create its own service(8) utility, and we will want to import theirs as well. The two can obviously not coexist. Also move ours from /bin to /sbin, as it is a superuser-only utility. Change-Id: Ic6e46ffb3a84b4747d2fdcb0d74e62dbea065039
188 lines
4.7 KiB
C
188 lines
4.7 KiB
C
/*
|
|
* Handle reading the EDID information, validating it, and parsing it into
|
|
* a struct edid_info. EDID reads are done using the Block Device Protocol
|
|
* as it's already supported by the cat24c256 driver and there is no need
|
|
* to add yet another message format/type.
|
|
*/
|
|
|
|
#include <minix/fb.h>
|
|
#include <minix/chardriver.h>
|
|
#include <minix/drivers.h>
|
|
#include <minix/ds.h>
|
|
#include <minix/rs.h>
|
|
#include <minix/log.h>
|
|
#include <minix/sysutil.h>
|
|
#include <minix/type.h>
|
|
#include <minix/vm.h>
|
|
#include <sys/ioc_fb.h>
|
|
#include <assert.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mman.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <dev/videomode/videomode.h>
|
|
#include <dev/videomode/edidvar.h>
|
|
#include <dev/videomode/edidreg.h>
|
|
|
|
#include "fb_edid.h"
|
|
#include "fb.h"
|
|
|
|
static int do_read(endpoint_t endpt, uint8_t *buf, size_t bufsize);
|
|
|
|
/* logging - use with log_warn(), log_info(), log_debug(), log_trace() */
|
|
static struct log log = {
|
|
.name = "edid",
|
|
.log_level = LEVEL_INFO,
|
|
.log_func = default_log
|
|
};
|
|
|
|
/*
|
|
* Labels corresponding to drivers which provide EDID.
|
|
*/
|
|
static char edid_providers[FB_DEV_NR][RS_MAX_LABEL_LEN+1];
|
|
|
|
/*
|
|
* Populate edid_providers from command line arguments. The minix-service
|
|
* command should get EDID providers like this: "-args edid.0=tda19988.1.3470"
|
|
* where 0 is the minor number of the frame buffer, tda19988 is the device
|
|
* driver, 1 is the i2c bus and 3470 is the slave address (the TDA19988 has 2
|
|
* slave addresses 0x34 and 0x70).
|
|
*/
|
|
int
|
|
fb_edid_args_parse(void)
|
|
{
|
|
int i;
|
|
int r;
|
|
char key[32];
|
|
|
|
for (i = 0; i < FB_DEV_NR; i++) {
|
|
|
|
memset(key, '\0', 32);
|
|
snprintf(key, 32, "edid.%d", i);
|
|
|
|
memset(edid_providers[i], '\0', RS_MAX_LABEL_LEN);
|
|
r = env_get_param(key, edid_providers[i], RS_MAX_LABEL_LEN);
|
|
if (r == OK) {
|
|
log_debug(&log, "Found key:%s value:%s\n", key, edid_providers[i]);
|
|
} else {
|
|
/* not an error, user is allowed to omit EDID
|
|
* providers in order to skip EDID reading and use
|
|
* the default settings.
|
|
*/
|
|
log_debug(&log, "Couldn't find key:%s\n", key);
|
|
}
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*
|
|
* Send a read request to the block driver at endpoint endpt.
|
|
*/
|
|
static int
|
|
do_read(endpoint_t driver_endpt, uint8_t *buf, size_t bufsize)
|
|
{
|
|
int r;
|
|
message m;
|
|
cp_grant_id_t grant_nr;
|
|
|
|
/* Open Device - required for drivers using libblockdriver */
|
|
memset(&m, '\0', sizeof(message));
|
|
m.m_type = BDEV_OPEN;
|
|
m.m_lbdev_lblockdriver_msg.access = BDEV_R_BIT;
|
|
m.m_lbdev_lblockdriver_msg.id = 0;
|
|
m.m_lbdev_lblockdriver_msg.minor = 0;
|
|
|
|
r = ipc_sendrec(driver_endpt, &m);
|
|
if (r != OK) {
|
|
log_debug(&log, "ipc_sendrec(BDEV_OPEN) failed (r=%d)\n", r);
|
|
return r;
|
|
}
|
|
|
|
grant_nr = cpf_grant_direct(driver_endpt, (vir_bytes) buf,
|
|
bufsize, CPF_READ | CPF_WRITE);
|
|
|
|
/* Perform the read */
|
|
memset(&m, '\0', sizeof(message));
|
|
m.m_type = BDEV_READ;
|
|
m.m_lbdev_lblockdriver_msg.minor = 0;
|
|
m.m_lbdev_lblockdriver_msg.count = bufsize;
|
|
m.m_lbdev_lblockdriver_msg.grant = grant_nr;
|
|
m.m_lbdev_lblockdriver_msg.flags = BDEV_NOPAGE; /* the EEPROMs used for EDID are pageless */
|
|
m.m_lbdev_lblockdriver_msg.id = 0;
|
|
m.m_lbdev_lblockdriver_msg.pos = 0;
|
|
|
|
r = ipc_sendrec(driver_endpt, &m);
|
|
cpf_revoke(grant_nr);
|
|
if (r != OK) {
|
|
log_debug(&log, "ipc_sendrec(BDEV_READ) failed (r=%d)\n", r);
|
|
/* Clean-up: try to close the device */
|
|
memset(&m, '\0', sizeof(message));
|
|
m.m_type = BDEV_CLOSE;
|
|
m.m_lbdev_lblockdriver_msg.minor = 0;
|
|
m.m_lbdev_lblockdriver_msg.id = 0;
|
|
ipc_sendrec(driver_endpt, &m);
|
|
return r;
|
|
}
|
|
|
|
/* Close the device */
|
|
memset(&m, '\0', sizeof(message));
|
|
m.m_type = BDEV_CLOSE;
|
|
m.m_lbdev_lblockdriver_msg.minor = 0;
|
|
m.m_lbdev_lblockdriver_msg.id = 0;
|
|
r = ipc_sendrec(driver_endpt, &m);
|
|
if (r != OK) {
|
|
log_debug(&log, "ipc_sendrec(BDEV_CLOSE) failed (r=%d)\n", r);
|
|
return r;
|
|
}
|
|
|
|
return bufsize;
|
|
}
|
|
|
|
int
|
|
fb_edid_read(int minor, struct edid_info *info)
|
|
{
|
|
|
|
int r;
|
|
uint8_t buffer[128];
|
|
endpoint_t endpt;
|
|
|
|
if (info == NULL || minor < 0 || minor >= FB_DEV_NR ||
|
|
edid_providers[minor][0] == '\0') {
|
|
return EINVAL;
|
|
}
|
|
|
|
log_debug(&log, "Contacting %s to get EDID.\n", edid_providers[minor]);
|
|
|
|
/* Look up the endpoint that corresponds to the label */
|
|
endpt = 0;
|
|
r = ds_retrieve_label_endpt(edid_providers[minor], &endpt);
|
|
if (r != 0 || endpt == 0) {
|
|
log_warn(&log, "Couldn't find endpoint for label '%s'\n", edid_providers[minor]);
|
|
return r;
|
|
}
|
|
|
|
/* Perform the request and put the resulting EDID into the buffer. */
|
|
memset(buffer, 0x00, 128);
|
|
r = do_read(endpt, buffer, 128);
|
|
if (r < 0) {
|
|
log_debug(&log, "Failed to read EDID\n");
|
|
return r;
|
|
}
|
|
|
|
/* parse and validate EDID */
|
|
r = edid_parse(buffer, info);
|
|
if (r != 0) {
|
|
log_warn(&log, "Invalid EDID data in buffer.\n");
|
|
return r;
|
|
}
|
|
|
|
log_debug(&log, "EDID Retrieved and Parsed OK\n");
|
|
|
|
return OK;
|
|
}
|
|
|