[PATCH v3] libgencec: Add userspace library for the generic CEC kernel interface
Hans Verkuil
hverkuil at xs4all.nl
Tue May 12 07:01:26 PDT 2015
Hi Kamil,
A quick review:
On 05/06/15 14:37, Kamil Debski wrote:
> new file mode 100644
> index 0000000..bb817b7
> --- /dev/null
> +++ b/README
> @@ -0,0 +1,22 @@
> +libGenCEC - library for the generic HDMI CEC kernel interface
> +--------------------------------------------------------------------------
> +
> +The libGenCEC library is a simple library that was written to facilitate
> +proper configuration and use of HDMI CEC devices that use the generic HDMI
> +CEC kernel interface.
> +
> +The library provides a range of functions that wrap around the ioctls of the
> +kernel API. It contains a test application that can be used to communicate
> +through the CEC bus with other compatible devices.
> +
> +The test application also serves as a code example on how to use the library.
> +
> +The library calls are documented in the gencec.h file.
> +
> +Example application use
> +--------------------------------------------------------------------------
> +The following command will initiate the devic, set the name and enable
devic -> device
> +keypress forwarding. Tested on a Samsung TV model LE32C650.
> +
> +./cectest -e -l playback -P -O Test123 -T -A -M on
> +
> diff --git a/examples/cectest.c b/examples/cectest.c
I think this should be in a utils directory and be called cec-ctl.
It's not just a test utility, like v4l2-ctl it can be used to control
the cec devices.
> new file mode 100644
> index 0000000..659f841
> --- /dev/null
> +++ b/examples/cectest.c
> @@ -0,0 +1,631 @@
> +/*
> + * Copyright 2015 Samsung Electronics Co. Ltd
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#include <gencec.h>
> +#include <getopt.h>
> +#include <stdint.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +/* A single entry in the list of tasks to be done */
> +struct todo {
> + char cmd;
> + char *param;
> + struct todo *next;
> +};
> +
> +/* Print a help message with the list and the description of all commands */
> +void print_usage(char *name)
> +{
> + printf("\nUsage:\n");
> + printf("\t%s\n", name);
> +
> + printf("General options:\n");
> + printf("\t--help/-h - display this message\n");
I think this ' - ' is confusing as a separator. Take a look at v4l2-ctl --help and
how that aligns things. I think that is more readable (admittedly, I am biased!).
> + printf("\t--device/-D <device name> - name of the CEC device (default is /dev/cec0)\n");
> +
> + printf("CEC device related commands:\n");
> + printf("\t--enable/-e - enable the CEC adapter\n");
> + printf("\t--disable/-d - disable the CEC adapter\n");
> + printf("\t--add-logical/-l <addr_type> - add logical address of given type\n");
> + printf("\t\tTypes: tv, record, tuner, playback, audio, switch, videoproc\n");
> + printf("\t--clear-logical/-c - clear logical addresses\n");
> + printf("\t--get-logical/-G - get logical address(es)\n");
> + printf("\t--get-physical/-g - get the physical address\n");
> + printf("\t--set-physical/-s <addr> - set the physical address\n");
> + printf("\t\t<addr> should be in the following format N.N.N.N where N is between 0 and 15\n");
> + printf("\t\te.g. --set-physical 1.0.0.11\n");
> + printf("\t--info/-i - print information about the CEC device\n");
> + printf("\t--send/-S <addr>:<contents> - send a message to a specified address\n");
> + printf("\t\t<addr> should be an integer ranging from 0 to 15 (where 15 is a broadcast address\n");
> + printf("\t\t<contents> should be an array of hexadecimal bytes\n");
> + printf("\t\te.g. --send 11:010b0cf6 which will send a message consisting of 010b0cf6 to\n");
> + printf("\t\tthe device with logical address 11\n");
> + printf("\t\t<contents> should be a hexadecimal number that represents the bytes to be sent\n");
> + printf("\t--receive/-R - receive a single CEC message\n");
> + printf("\t--passthrough/-p <state> - enable/disable passthrough mode\n");
> +
> + printf("Useful CEC standard commands:\n");
> + printf("\t--osd/-O <OSD name> - set the OSD name for device\n");
> + printf("\t--give-power-status/-P <status> - give device power status\n");
> + printf("\t--text-view-on/-T - Sent by a source device to the TV whenever it enters the active state \n");
> + printf("\t--active-source/-A - indicate that it has started to transmit a stream\n");
> + printf("\t--menu-state/-M <state> - used to indicate that the device is showing a menu (enable keycode forwarding)\n");
> + printf("\t\t<state> = \"on\" or \"off\"\n");
> + printf("\t\n");
> + printf("\tCommands are executed in the order given in argument list.\n");
> +}
> +
> +/* Parse the arguments list and prepare a list with task that are to be done */
> +struct todo *parse_args(int argc, char **argv)
> +{
> + struct todo *list = 0;
> + struct todo *tmp;
> +
> + int c;
> + int option_index = 0;
> +
> + static struct option long_options[] =
> + {
> + {"device", required_argument, 0, 'D'},
> + {"help", no_argument, 0, 'h'},
> +
> + {"enable", no_argument, 0, 'e'},
> + {"disable", no_argument, 0, 'd'},
> + {"add-logical", required_argument, 0, 'l'},
> + {"clear-logical", no_argument, 0, 'c'},
> + {"get-logical", no_argument, 0, 'G'},
> + {"get-physical", no_argument, 0, 'g'},
> + {"set-physical", required_argument, 0, 's'},
> + {"info", no_argument, 0, 'i'},
> + {"passthrough", required_argument, 0, 'p'},
> +
> + {"send", required_argument, 0, 'S'},
> + {"receive", no_argument, 0, 'R'},
> +
> + {"osd", required_argument, 0, 'O'},
> + {"give-power-status", required_argument, 0, 'P'},
> + {"text-view-on", no_argument, 0, 'T'},
> + {"active-source", no_argument, 0, 'A'},
> + {"menu-state", required_argument, 0, 'M'},
> +
> + {0, 0, 0, 0}
> + };
> +
> + while (1) {
> + c = getopt_long(argc, argv, "D:edl:cGgs:S:RO:PTAM:ip:", long_options, &option_index);
> + if (c == -1)
> + break;
> + switch (c) {
> + case 'D':
> + /* add as first element in todo */
> + tmp = malloc(sizeof(struct todo));
> + if (!tmp)
> + exit(1);
> + tmp->cmd = c;
> + tmp->param = strdup(optarg);
> + tmp->next = list;
> + list = tmp;
> + break;
> + /* cmds with no arg */
> + case 'A': case 'c': case 'd': case 'e':
> + case 'g': case 'G': case 'i': case 'P':
> + case 'R': case 'S': case 'T':
> + /* cmds with arg */
> + case 'l': case 'M': case 'O': case 's':
> + case 'p':
> + /* add as last element */
> + if (!list) {
> + list = tmp = malloc(sizeof(struct todo));
> + if (!tmp)
> + exit(1);
> + } else {
> + tmp = list;
> +
> + while (tmp->next)
> + tmp = tmp->next;
> + tmp->next = malloc(sizeof(struct todo));
> + if (!tmp)
> + exit(1);
> + tmp = tmp->next;
> + }
> + tmp->cmd = c;
> + if (optarg)
> + tmp->param = strdup(optarg);
> + else
> + tmp->param = 0;
> + tmp->next = 0;
> + break;
> + case 'h':
> + default:
> + while (list) {
> + tmp = list->next;
> + free(list->param);
> + free(list);
> + list = tmp;
> + }
> + return 0;
> + }
> + }
> +
> + return list;
> +}
> +
> +/* Parse a physical address which format is dd.dd.dd.dd
> + * where dd is a integer ranging from 0 to 15 */
> +int parse_paddr(char *s)
> +{
> + char c;
> + int r = 0;
> + int t = 0;
> + int dots = 0;
> + if (!s)
> + return -1;
> + while ((c = *(s++))) {
> + if (c == '.') {
> + r <<= 4;
> + r |= t;
> + t = 0;
> + dots++;
> + } else if (c >= '0' && c <= '9') {
> + t = t * 10 + c - '0';
> + } else {
> + return -1;
> + }
> + }
> + if (dots != 3)
> + return -1;
> + r <<= 4;
> + r |= t;
> + return r;
> +}
> +
> +/* Get the first logical address assigned to the used CEC device */
> +int get_addr(struct cec_device *cec)
> +{
> + uint8_t addrs[CEC_MAX_NUM_LOG_ADDR];
> + int num_addrs;
> + int ret;
> +
> + ret = cec_get_logical_addrs(cec, addrs, &num_addrs);
> + if (ret != CEC_OK)
> + return -1;
> +
> + if (num_addrs)
> + return addrs[0];
> + else
> + return -1;
> +}
> +
> +/* Convert a single character containing a single hexadecimal digit
> + * to an int */
> +int hexdigit(char c)
> +{
> + if (c >= '0' && c <= '9')
> + return c - '0';
> + if (c >= 'a' && c <= 'f')
> + return 10 + c - 'a';
> + if (c >= 'A' && c <= 'F')
> + return 10 + c - 'A';
> + return -1;
> +}
> +
> +/* Parse string in the format of dd:xxxx
> + * where dd - is an integer denoting the logical address of the recipient device
> + * and xxxx is an array of bytes written in hexadecimal notation.
> + * e.g. 11:1234 means send a message consisting of the following two bytes 0x12
> + * and 0x34 to the device with logical address 11 */
> +int parse_message(struct cec_buffer *msg, char *s)
> +{
> + int i;
> + int tmp;
> +
> + if (*s > '9' || *s < '0')
> + return 1;
> + msg->dst = *s - '0';
> + s++;
> + if (*s != ':') {
> + if (*s> '9' || *s < '0')
> + return 1;
> + msg->dst *= 10;
> + msg->dst += *s - '0';
> + s++;
> + }
> +
> + if (*s != ':')
> + return 1;
> + s++;
> +
> + i = 0;
> + while (*s) {
> + if (i > CEC_MAX_LENGTH * 2)
> + return 1;
> + tmp = hexdigit(*s);
> + if (tmp == -1)
> + return 1;
> + if (i % 2 == 0)
> + msg->payload[i / 2] = tmp << 4;
> + else
> + msg->payload[i / 2] |= tmp;
> + s++;
> + i++;
> + }
> +
> + msg->len = i / 2;
> + return 0;
> +}
> +
> +int main(int argc, char **argv)
> +{
> + struct todo *list;
> + struct cec_device cec;
> + uint8_t addr;
> + int ret;
> +
> + printf("libgencec test application (c) Samsung 2015\n");
> +
> + list = parse_args(argc, argv);
> +
> + if (!list) {
> + print_usage(argv[0]);
> + return 1;
> + }
> +
> + if (list->cmd == 'D') {
> + ret = cec_open(&cec, list->param);
> + list = list->next;
> + } else {
> + ret = cec_open(&cec, "/dev/cec0");
> + }
> + if (ret != CEC_OK) {
> + printf("Failed to open CEC device\n");
> + return 1;
> + }
> +
> + printf("Succesfully opened CEC device\n");
> +
> + /* Main processing loop */
> + while (list) {
> + switch (list->cmd) {
> + case 'd':
> + case 'e':
> + ret = cec_enable(&cec, list->cmd == 'e');
> + if (ret != CEC_OK) {
> + printf("Failed to %s CEC device\n",
> + list->cmd == 'e' ? "enable":"disable");
> + return 1;
> + }
> + printf("Successfully %s CEC device\n",
> + list->cmd == 'e' ? "enabled":"disabled");
> + break;
> + case 'l': {
> + enum cec_device_type type;
> + if (!strcasecmp(list->param, "tv"))
> + type = CEC_DEVICE_TYPE_TV;
> + else if (!strcasecmp(list->param, "record"))
> + type = CEC_DEVICE_TYPE_RECORD;
> + else if (!strcasecmp(list->param, "tuner"))
> + type = CEC_DEVICE_TYPE_TUNER;
> + else if (!strcasecmp(list->param, "playback"))
> + type = CEC_DEVICE_TYPE_PLAYBACK;
> + else if (!strcasecmp(list->param, "audio"))
> + type = CEC_DEVICE_TYPE_AUDIOSYSTEM;
> + else if (!strcasecmp(list->param, "switch"))
> + type = CEC_DEVICE_TYPE_SWITCH;
> + else if (!strcasecmp(list->param, "videoproc"))
> + type = CEC_DEVICE_TYPE_VIDEOPROC;
> + else {
> + printf("Unrecognised logical address type\n");
> + return 1;
> + }
> +
> + ret = cec_add_logical_addr(&cec, type, &addr);
> + if (ret != CEC_OK) {
> + printf("Failed to add a logical address\n");
> + return 1;
> + }
> + printf("Successfully added logical address of type '%s', id=%d\n", list->param, addr);
> + break;
> + }
> + case 'c':
> + ret = cec_clear_logical_addrs(&cec);
> + if (ret != CEC_OK) {
> + printf("Failed to clear logical addresses\n");
> + return 1;
> + }
> + printf("Successfully cleared logical addresses\n");
> + break;
> + case 'G': {
> + uint8_t addrs[CEC_MAX_NUM_LOG_ADDR];
> + int num_addrs;
> + int i;
> +
> + ret = cec_get_logical_addrs(&cec, addrs, &num_addrs);
> + if (ret != CEC_OK) {
> + printf("Failed to get logical addresses\n");
> + return 1;
> + }
> + if (num_addrs) {
> + for (i = 0; i < num_addrs; i++)
> + printf("Assigned logical address %d\n",
> + addrs[i]);
> + } else {
> + printf("No logical addresses assigned\n");
> + }
> +
> + break;
> + }
> + case 'g': {
> + uint16_t paddr;
> + ret = cec_get_physical_addr(&cec, &paddr);
> + if (ret != CEC_OK) {
> + printf("Failed to get physical address\n");
> + return 1;
> + }
> + printf("Got physical addr: %d.%d.%d.%d\n",
> + paddr >> 12 & 0xf, paddr >> 8 & 0xf,
> + paddr >> 4 & 0xf, paddr & 0xf);
> + break;
> + }
> + case 's': {
> + uint16_t paddr;
> + paddr = parse_paddr(list->param);
> + if (paddr == -1) {
> + printf("Failed to parse physical address (%s)\n", list->param);
> + return -1;
> + }
> + ret = cec_set_physical_addr(&cec, paddr);
> + if (ret != CEC_OK) {
> + printf("Failed to set physical address\n");
> + return 1;
> + }
> + printf("Set physical addr: %d.%d.%d.%d\n",
> + paddr >> 12 & 0xf, paddr >> 8 & 0xf,
> + paddr >> 4 & 0xf, paddr & 0xf);
> + break;
> + }
> + case 'i': {
> + struct cec_info info;
> +
> + ret = cec_get_info(&cec, &info);
> + if (ret != CEC_OK) {
> + printf("Failed to get CEC info\n");
> + return 1;
> + }
> +
> + printf( "Got info: \n"
> + "version = %d\n"
> + "vendor_id = 0x%08x\n"
> + "ports_num = %d\n\n",
> + info.version,
> + info.vendor_id,
> + info.ports_num);
> +
> + break;
> + }
> + case 'p': {
> + int passthrough;
> +
> + addr = get_addr(&cec);
> + if (strcasecmp(list->param, "on") == 0) {
> + passthrough = 1;
> + } else if (strcasecmp(list->param, "off") == 0) {
> + passthrough = 0;
> + } else {
> + printf("Unknown state \"%s\"\n", list->param);
> + return 1;
> + }
> +
> + printf("Successfully switched passthrought mode %s\n", list->param);
> + break;
> + }
> + case 'O': {
> + struct cec_buffer msg;
> + int addr;
> +
> + if (strlen(list->param) > CEC_MAX_LENGTH) {
> + printf("OSD name too long\n");
> + return -1;
> + }
> +
> + addr = get_addr(&cec);
> + if (addr == -1) {
> + printf("Failed to get logical address of the CEC device\n");
> + return 1;
> + }
> +
> + msg.src = addr;
> + msg.dst = 0x0; /* The TV */
> + msg.len = 1 + strlen(list->param);
> + msg.payload[0] = 0x47;
> + memcpy(msg.payload + 1, list->param, strlen(list->param));
> +
> + ret = cec_send_message(&cec, &msg);
> + if (ret != CEC_OK) {
> + printf("Failed to send message\n");
> + return 1;
> + }
> + printf("Successfully sent message - set OSD name to\"%s\"\n", list->param);
> + break;
> + }
> + case 'P': {
> + struct cec_buffer msg;
> + int addr;
> +
> + addr = get_addr(&cec);
> + if (addr == -1) {
> + printf("Failed to get logical address of the CEC device\n");
> + return 1;
> + }
> +
> + msg.src = addr;
> + msg.dst = 0x0; /* The TV */
> + msg.len = 1;
> + msg.payload[0] = 0x8f;
> +
> + ret = cec_send_message(&cec, &msg);
> + if (ret != CEC_OK) {
> + printf("Failed to send message\n");
> + return 1;
> + }
> + printf("Successfully sent message - Give Power Status\n");
> + break;
> + }
> + case 'T': {
> + struct cec_buffer msg;
> + int addr;
> +
> + addr = get_addr(&cec);
> + if (addr == -1) {
> + printf("Failed to get logical address of the CEC device\n");
> + return 1;
> + }
> +
> + msg.src = addr;
> + msg.dst = 0x0; /* The TV */
> + msg.len = 1;
> + msg.payload[0] = 0x0d;
> +
> + ret = cec_send_message(&cec, &msg);
> + if (ret != CEC_OK) {
> + printf("Failed to send message\n");
> + return 1;
> + }
> + printf("Successfully sent message - Text View On\n");
> + break;
> + }
> + case 'A': {
> + struct cec_buffer msg;
> + uint16_t paddr;
> + int addr;
> +
> + addr = get_addr(&cec);
> + if (addr == -1) {
> + printf("Failed to get logical address of the CEC device\n");
> + return 1;
> + }
> + ret = cec_get_physical_addr(&cec, &paddr);
> + if (ret != CEC_OK) {
> + printf("Failed to get physical address\n");
> + return 1;
> + }
> +
> + msg.src = addr;
> + msg.dst = 0xf; /* The TV */
> + msg.len = 3;
> + msg.payload[0] = 0x82;
> + msg.payload[1] = paddr >> 8;
> + msg.payload[2] = paddr & 0xff;
> +
> + ret = cec_send_message(&cec, &msg);
> + if (ret != CEC_OK) {
> + printf("Failed to send message\n");
> + return 1;
> + }
> + printf("Successfully sent message - Active Source\n");
> + break;
> + }
> + case 'M': {
> + struct cec_buffer msg;
> + int addr;
> +
> + addr = get_addr(&cec);
> + if (addr == -1) {
> + printf("Failed to get logical address of the CEC device\n");
> + return 1;
> + }
> +
> + msg.src = addr;
> + msg.dst = 0x0; /* The TV */
> + msg.len = 2;
> + msg.payload[0] = 0x8e;
> + if (strcasecmp(list->param, "on") == 0) {
> + msg.payload[1] = 0;
> + } else if (strcasecmp(list->param, "off") == 0) {
> + msg.payload[1] = 1;
> + } else {
> + printf("Unknown state \"%s\"\n", list->param);
> + return 1;
> + }
> +
> + ret = cec_send_message(&cec, &msg);
> + if (ret != CEC_OK) {
> + printf("Failed to send message\n");
> + return 1;
> + }
> + printf("Successfully sent message - Menu Status\n");
> + break;
> + }
> + case 'S': {
> + struct cec_buffer msg;
> + int addr;
> + int ret;
> + int i;
> +
> + ret = parse_message(&msg, list->param);
> + if (ret) {
> + printf("Failed to parse message to send\n");
> + return 1;
> + }
> + printf("Sending message=0x");
> + for (i = 0; i < msg.len; i++)
> + printf("%02x", msg.payload[i]);
> + printf(" (length=%d) to addr=%d\n", msg.len, msg.dst);
> + addr = get_addr(&cec);
> + if (addr == -1) {
> + printf("Failed to get logical address of the CEC device\n");
> + return 1;
> + }
> + msg.src = addr;
> +
> + ret = cec_send_message(&cec, &msg);
> + if (ret != CEC_OK) {
> + printf("Failed to send message\n");
> + return 1;
> + }
> + printf("Successfully sent custom message\n");
> + break;
> + }
> + case 'R': {
> + struct cec_buffer msg;
> + int i;
> +
> + ret = cec_receive_message(&cec, &msg);
> + if (ret == CEC_TIMEOUT) {
> + printf("CEC receive message timed out\n");
> + return 2;
> + } else if (ret != CEC_OK) {
> + printf("Failed to receive a message\n");
> + return 1;
> + }
> +
> + printf("Received message of length %d\n", msg.len);
> + for (i = 0; i < msg.len; i++)
> + printf("%02x", msg.payload[i]);
> + printf("\n");
> + break;
> + }
> + default:
> + printf("Command '%c' not yet implemented.\n", list->cmd);
> + break;
> + }
> +
> + list = list->next;
> + }
> +
> + return 0;
> +}
> diff --git a/include/gencec.h b/include/gencec.h
> new file mode 100644
> index 0000000..b727ddc
> --- /dev/null
> +++ b/include/gencec.h
> @@ -0,0 +1,255 @@
> +/*
> + * Copyright 2015 Samsung Electronics Co. Ltd
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#ifndef __GENCEC_H__
> +
> +#include <stdint.h>
> +#include <stdbool.h>
> +#include <time.h>
> +
> +/* Maximum length of the CEC message */
> +#define CEC_MAX_LENGTH 15 /* 16 including the automatically added
> + * address byte. */
> +#define MAX_NUM_OF_HDMI_PORTS 16
> +#define CEC_MAX_NUM_LOG_ADDR 4
> +#define DEFAULT_TIMEOUT 1000
> +
> +/* cec_version: list of CEC versions */
> +enum cec_version {
> + CEC_VER_UNKNOWN,
> + CEC_VER_1_0,
> + CEC_VER_1_1,
> + CEC_VER_1_2,
> + CEC_VER_1_3,
> + CEC_VER_1_3A,
> + CEC_VER_1_3B,
> + CEC_VER_1_3C,
> + CEC_VER_1_4,
> + CEC_VER_1_4B,
> + CEC_VER_2_0,
> +};
Isn't this a duplicate of what's in uapi/linux/cec.h?
> +
> +enum cec_device_type {
> + /* Used internally for error handling */
> + CEC_DEVICE_TYPE_EMPTY,
> + CEC_DEVICE_TYPE_TV,
> + CEC_DEVICE_TYPE_RECORD,
> + CEC_DEVICE_TYPE_TUNER,
> + CEC_DEVICE_TYPE_PLAYBACK,
> + CEC_DEVICE_TYPE_AUDIOSYSTEM,
> + CEC_DEVICE_TYPE_SWITCH,
> + CEC_DEVICE_TYPE_VIDEOPROC,
> +};
Same here.
> +
> +/**
> + * struct cec_device - a structure used to keep context of the current used CEC
> + * device
> + * @caps: used to keep a pointer to the kernel caps structure for the
> + * device
> + * @handle: this is used to keep the file handle to the CEC device
> + * @initialised: flag set if the structure was properly initialised
> + * @log_addr: an array containing the assigned logical addresses
> + * @log_addr_type_int: an array containing the logical addresses' types as
> + * needed by the kernel
> + * @dev_type: device type, as neede by the library
> + * @dev_type_int: primary device type, as needed by the kernel driver
> + * @num_log_addr: number of ssigned logical addresses
> + */
> +struct cec_device {
> + void *caps;
> + int handle;
> + int initialised;
> + uint32_t log_addr[CEC_MAX_NUM_LOG_ADDR];
> + uint32_t log_addr_type_int[CEC_MAX_NUM_LOG_ADDR];
> + enum cec_device_type dev_type[CEC_MAX_NUM_LOG_ADDR];
> + uint32_t dev_type_int[CEC_MAX_NUM_LOG_ADDR];
> + int num_log_addr;
> +};
> +
> +/* cec_error: list of CEC framework errors */
> +enum cec_error {
> + CEC_OK /* Success */,
> + CEC_TIMEOUT /* Timeout occured */,
> + CEC_NO_ADDR_LEFT /* No more logical addresses left */,
> + CEC_ERROR,
> +};
> +
> +/**
> + * struct hdmi_port_info - Information about a HDMI port
> + * @port_number: the port number
> + */
> +struct hdmi_port_info {
> + uint8_t port_number;
> +};
> +
> +/**
> + * struct cec_info - a structure used to get information about the CEC device
> + * @version: supported CEC version
> + * @vendor_id: the vendor ID
> + * @ports_num: number of HDMI ports available in the system
> + * @ports_info: an array containing information about HDMI ports
> + * */
> +struct cec_info {
> + enum cec_version version;
> + uint32_t vendor_id;
> + unsigned int ports_num;
> + struct hdmi_port_info ports_info[MAX_NUM_OF_HDMI_PORTS];
> +};
> +
> +/**
> + * struct cec_msg - a structure used to store message that were received or are
> + * to be sent
> + * @dst: The address of the destination device
> + * @src: The address of the source device
> + * @len: The length of the payload of the message
> + * @payload: The payload of the message
> + * @ts: The timestamp for received messages
> + */
> +struct cec_buffer {
> + uint8_t dst;
> + uint8_t src;
> + uint8_t len;
> + uint8_t payload[CEC_MAX_LENGTH];
> + struct timespec ts;
> +};
> +
> +/**
> + * cec_open() - Open a CEC device
> + * @dev: pointer to a structure that will hold the state of the device
> + * @path: path to the CEC device
> + * Returns: on success CEC_OK, on error returns an negative error code
> + */
> +int cec_open(struct cec_device *dev, char *path);
Can you add a newline after each function? It makes is a bit more readable
> +/**
> + * cec_close() - Close a CEC device
> + * @dev: pointer to a structure that holds the state of the device
> + * Returns: on success CEC_OK, on error returns an negative error code
> + */
> +int cec_close(struct cec_device *dev);
> +/**
> + * cec_is_enabled() - Check whether the CEC device is enabled
> + * @dev: pointer to a structure that holds the state of the device
> + * Returns: true if all is ok and the CEC device is enabled, false otherwise
> + */
> +bool cec_is_enabled(struct cec_device *dev);
> +/**
> + * cec_enable() - Enable a CEC device
> + * @dev: pointer to a structure that will hold the state of the device
> + * @enable: true to enable the CEC device, false to disable the CEC device
> + * Returns: on success CEC_OK, on error returns an negative error code
> + */
> +int cec_enable(struct cec_device *dev, bool enable);
> +/**
> + * cec_passthrough() - Enable a CEC device
> + * @dev: pointer to a structure that will hold the state of the device
> + * @enable: true to enable the passthrough mode, false to disable
> + * Returns: on success CEC_OK, on error returns an negative error code
> + */
> +int cec_passthrough(struct cec_device *dev, bool enable);
> +/**
> + * cec_info() - Returns information about the CEC device
> + * @dev: pointer to a structure that holds the state of the device
> + * @info: pointer to a info structure that will hold the information about
> + * the CEC device
> + * Returns: on success CEC_OK, on error returns an negative error code
> + */
> +int cec_get_info(struct cec_device *dev, struct cec_info *info);
> +/**
> + * cec_is_connected() - Return information about whether a device is connected
> + * to the port
> + * @dev: pointer to a structure that holds the state of the device
> + * Returns: when a device is connected to the port returns CEC_CONNECTED,
> + * CEC_DISCONNECTED when there is no device connected, on error
> + * returns an negative error code
> + */
> +int cec_is_connected(struct cec_device *dev);
> +/**
> + * cec_send_message() - Send a message over the CEC bus
> + * @dev: pointer to a structure that holds the state of the device
> + * @msg: the message do be sent over the CEC bus
> + * Returns: CEC_OK on success
> + * CEC_REPLY on successful send and reply receive
> + * CEC_REPLY_TIMEOUT when waiting for reply timed out
> + * CEC_TIMEOUT when a timeout occurred while sending the message
> + * negative error code on other error
> + */
> +int cec_send_message(struct cec_device *dev, struct cec_buffer *msg);
> +/**
> + * cec_receive_message() - Receive a message over the CEC bus
> + * @dev: pointer to a structure that holds the state of the device
> + * @msg: a structure used to store the message received over the CEC bus
> + * Returns: CEC_OK on success
> + * CEC_TIMEOUT when a timeout occurred while waiting for message
> + * negative error code on error
> + * Remarks: when waiting for a reply, the reply is stored in the msg struct
> + */
> +int cec_receive_message(struct cec_device *dev, struct cec_buffer *msg);
> +/**
> + * cec_get_logical_addrs() - Add a new logical address to the CEC device
> + * @dev: pointer to a structure that holds the state of the device
> + * @addr: pointer to an array to hold the list of assigned logical
> + * addresses, the size should be CEC_MAX_NUM_LOG_ADDR
> + * @num_addr: pointer to an int that will hold the number of assigned
> + * logical addresses
> + * Returns: CEC_OK on success
> + * negative error code on error
> + */
> +int cec_get_logical_addrs(struct cec_device *dev, uint8_t *addr, int *num_addr);
> +/**
> + * cec_add_logical_addr() - Add a new logical address to the CEC device
> + * @dev: pointer to a structure that holds the state of the device
> + * @type: the type of the device that is to be added, please note that
> + * this indicated the type and not the address that will be
> + * assigned
> + * @addr: a pointer to a location where to store the assigned logical
> + * address
> + * Returns: CEC_OK on success
> + * CEC_TIMEOUT when a timeout occurred while waiting for message
> + * CEC_NO_ADDR_LEFT when all addresses related to the chosen device
> + * type are already taken
> + * negative error code on error
> + */
> +int cec_add_logical_addr(struct cec_device *dev, enum cec_device_type type,
> + uint8_t *addr);
> +/**
> + * cec_clear_logical_addrs() - Clear the logical addresses that were assigned to
> + * the device
> + * @dev: pointer to a structure that holds the state of the device
> + * Returns: CEC_OK on success
> + * CEC_TIMEOUT when a timeout occurred while waiting for message
> + * negative error code on error
> + */
> +int cec_clear_logical_addrs(struct cec_device *dev);
> +/**
> + * cec_get_physical_addr() - Get the physical addr of the CEC device
> + * @dev: pointer to a structure that holds the state of the device
> + * @phys_addr: pointer to a uint16_t which will hold the physical address
> + * Returns: CEC_OK on success
> + * CEC_TIMEOUT when a timeout occurred while waiting for message
> + * negative error code on error
> + */
> +int cec_get_physical_addr(struct cec_device *dev, uint16_t *phys_addr);
> +/**
> + * cec_set_physical_addr() - Get the physical addr of the CEC device
> + * @dev: pointer to a structure that holds the state of the device
> + * @phys_addr: a uint16_t which holding the physical address
> + * Returns: CEC_OK on success
> + * CEC_TIMEOUT when a timeout occurred while waiting for message
> + * negative error code on error
> + */
> +int cec_set_physical_addr(struct cec_device *dev, uint16_t phys_addr);
> +
> +#endif /* __GENCEC_H__ */
Why is it called 'gencec'? The 'cec' part is obvious, but where does 'gen'
stand for?
> diff --git a/src/gencec.c b/src/gencec.c
> new file mode 100644
> index 0000000..2224115
> --- /dev/null
> +++ b/src/gencec.c
> @@ -0,0 +1,445 @@
> +/*
> + * Copyright 2015 Samsung Electronics Co. Ltd
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <stdio.h>
> +#include <stdbool.h>
> +#include <stdint.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#include <unistd.h>
> +#include <sys/ioctl.h>
> +#include <linux/cec.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include "gencec.h"
> +
> +bool cec_is_enabled(struct cec_device *dev)
> +{
> + struct cec_caps *caps = (struct cec_caps *)dev->caps;
> + int ret;
> +
> + if (!dev)
> + return CEC_ERROR;
> + if (!dev->initialised)
> + return CEC_ERROR;
> +
> + if (caps->capabilities && CEC_CAP_STATE) {
In all the capabilities checks you use && instead of &. It's a bitmask, so
you should use &.
> + uint32_t arg;
> +
> + ret = ioctl(dev->handle, CEC_G_ADAP_STATE, &arg);
> + if (ret)
> + return CEC_ERROR;
Weird mix between bool and CEC_ERROR.
In general I am not keen on introducing your own 'OK/ERROR' defines when
you have bools.
> + if (arg == 0)
> + return false;
> + }
> +
> + return true;
> +}
> +
> +int cec_enable(struct cec_device *dev, bool enable)
> +{
> + struct cec_caps *caps = (struct cec_caps *)dev->caps;
> + int ret;
> +
> + if (!dev)
> + return CEC_ERROR;
> + if (!dev->initialised)
> + return CEC_ERROR;
> +
> + if (caps->capabilities && CEC_CAP_STATE) {
> + uint32_t arg;
> +
> + arg = enable ? CEC_STATE_ENABLED : CEC_STATE_DISABLED;
> + ret = ioctl(dev->handle, CEC_S_ADAP_STATE, &arg);
> + if (ret)
> + return CEC_ERROR;
> + }
> +
> + return CEC_OK;
> +}
> +
> +int cec_passthrough(struct cec_device *dev, bool enable)
> +{
> + struct cec_caps *caps = (struct cec_caps *)dev->caps;
> + int ret;
> +
> + if (!dev)
> + return CEC_ERROR;
> + if (!dev->initialised)
> + return CEC_ERROR;
> +
> + if (caps->capabilities && CEC_CAP_PASSTHROUGH) {
> + uint32_t arg;
> +
> + arg = enable ? CEC_PASSTHROUGH_ENABLED : CEC_PASSTHROUGH_DISABLED;
> + ret = ioctl(dev->handle, CEC_S_PASSTHROUGH, &arg);
> + if (ret)
> + return CEC_ERROR;
> + }
> +
> + return CEC_OK;
> +}
> +
> +static int check_state(struct cec_device *dev)
> +{
> + if (!dev)
> + return CEC_ERROR;
> + if (!dev->initialised)
> + return CEC_ERROR;
> + if (!cec_is_enabled(dev))
> + return CEC_ERROR;
> + return CEC_OK;
> +}
> +
> +int cec_open(struct cec_device *dev, char *path)
> +{
> + int ret;
> +
> + if (!dev || !path)
> + return CEC_ERROR;
> +
> + memset(dev, 0, sizeof(*dev));
> +
> + dev->handle = open(path, O_RDWR);
> + if (dev->handle == -1)
> + return CEC_ERROR;
> +
> + dev->caps = malloc(sizeof(struct cec_caps));
> + if (!dev->caps) {
> + close(dev->handle);
> + return CEC_ERROR;
> + }
> +
> + ret = ioctl(dev->handle, CEC_G_CAPS, dev->caps);
> + if (ret) {
> + free(dev->caps);
> + close(dev->handle);
> + return CEC_ERROR;
> + }
> +
> + dev->initialised = 1;
> +
> + return CEC_OK;
> +}
> +
> +int cec_close(struct cec_device *dev)
> +{
> + if (!dev)
> + return CEC_ERROR;
> + if (close(dev->handle) == -1)
> + return CEC_ERROR;
> + dev->initialised = 0;
> + return CEC_OK;
> +}
> +
> +int cec_get_info(struct cec_device *dev, struct cec_info *info)
> +{
> + struct cec_caps *caps;
> + int i;
> +
> + if (check_state(dev) != CEC_OK || !info)
> + return CEC_ERROR;
> +
> + caps = (struct cec_caps *)(dev->caps);
> +
> + info->vendor_id = caps->vendor_id;
> + switch (caps->version) {
> + case CEC_VERSION_1_4:
> + info->version = CEC_VER_1_4;
> + break;
> + case CEC_VERSION_2_0:
> + info->version = CEC_VER_2_0;
> + break;
> + default:
> + info->version = CEC_VER_UNKNOWN;
> + break;
> + }
> + info->ports_num = 1; /* ? */
> +
> + for (i = 0; i < MAX_NUM_OF_HDMI_PORTS; i++) {
> + info->ports_info[i].port_number = i;
I'm not sure what the ports idea is about...
> + }
> +
> + return CEC_OK;
> +}
> +
> +int cec_is_connected(struct cec_device *dev)
> +{
> + if (!dev)
> + return CEC_ERROR;
> + /* TODO */
> + return CEC_OK;
> +}
> +
> +int cec_send_message(struct cec_device *dev, struct cec_buffer *msg)
> +{
> + struct cec_msg msg_int;
> + int i, ret;
> +
> + if (check_state(dev) != CEC_OK || !msg)
> + return CEC_ERROR;
> +
> + if (msg->len > CEC_MAX_LENGTH)
> + return CEC_ERROR;
> +
> + msg_int.len = msg->len + 1;
> + msg_int.msg[0] = msg->src << 4 & 0xf0;
> + msg_int.msg[0] |= msg->dst & 0x0f;
> + for (i = 0; i < msg->len; i++)
> + msg_int.msg[i + 1] = msg->payload[i];
> + msg_int.reply = 0;
> + msg_int.timeout = DEFAULT_TIMEOUT;
> +
> + ret = ioctl(dev->handle, CEC_TRANSMIT, &msg_int);
> + if (ret) {
> + if (errno == ETIMEDOUT)
> + return CEC_TIMEOUT;
> + return CEC_ERROR;
> + }
> +
> +
> + return CEC_OK;
> +}
Why not return the errno value instead of inventing new error codes?
> +
> +int cec_receive_message(struct cec_device *dev, struct cec_buffer *msg)
> +{
> + struct cec_msg msg_int;
> + int i, ret;
> +
> + if (check_state(dev) != CEC_OK || !msg)
> + return CEC_ERROR;
> +
> + msg_int.timeout = DEFAULT_TIMEOUT;
> + ret = ioctl(dev->handle, CEC_RECEIVE, &msg_int);
> + if (ret) {
> + if (errno == ETIMEDOUT)
> + return CEC_TIMEOUT;
> + return CEC_ERROR;
> + }
> + if (msg_int.len == 0 || msg_int.len > CEC_MAX_LENGTH + 1)
> + return CEC_ERROR;
> +
> + msg->src = msg_int.msg[0] >> 4 & 0xf;
> + msg->dst = msg_int.msg[0] & 0xf;
> + msg->ts.tv_sec = msg_int.ts / 1000000000;
> + msg->ts.tv_nsec = msg_int.ts % 1000000000;
> + msg->len = msg_int.len - 1;
> + for (i = 0; i < msg->len; i++)
> + msg->payload[i] = msg_int.msg[i + 1];
> +
> + return CEC_OK;
> +}
> +
> +static int dev_type_to_int_dev_type(enum cec_device_type type)
> +{
> + switch (type) {
> + case CEC_DEVICE_TYPE_TV:
> + return CEC_PRIM_DEVTYPE_TV;
> + case CEC_DEVICE_TYPE_RECORD:
> + return CEC_PRIM_DEVTYPE_RECORD;
> + case CEC_DEVICE_TYPE_TUNER:
> + return CEC_PRIM_DEVTYPE_TUNER;
> + case CEC_DEVICE_TYPE_PLAYBACK:
> + return CEC_PRIM_DEVTYPE_PLAYBACK;
> + case CEC_DEVICE_TYPE_AUDIOSYSTEM:
> + return CEC_PRIM_DEVTYPE_AUDIOSYSTEM;
> + case CEC_DEVICE_TYPE_SWITCH:
> + return CEC_PRIM_DEVTYPE_SWITCH;
> + case CEC_DEVICE_TYPE_VIDEOPROC:
> + return CEC_PRIM_DEVTYPE_VIDEOPROC;
> + }
> + return -1;
> +}
> +
> +static int dev_type_to_int_addr_type(enum cec_device_type type)
> +{
> + switch (type) {
> + case CEC_DEVICE_TYPE_TV:
> + return CEC_LOG_ADDR_TYPE_TV;
> + case CEC_DEVICE_TYPE_RECORD:
> + return CEC_LOG_ADDR_TYPE_RECORD;
> + case CEC_DEVICE_TYPE_TUNER:
> + return CEC_LOG_ADDR_TYPE_TUNER;
> + case CEC_DEVICE_TYPE_PLAYBACK:
> + return CEC_LOG_ADDR_TYPE_PLAYBACK;
> + case CEC_DEVICE_TYPE_AUDIOSYSTEM:
> + return CEC_LOG_ADDR_TYPE_AUDIOSYSTEM;
> + case CEC_DEVICE_TYPE_SWITCH:
> + return CEC_LOG_ADDR_TYPE_UNREGISTERED;
> + case CEC_DEVICE_TYPE_VIDEOPROC:
> + return CEC_LOG_ADDR_TYPE_SPECIFIC;
> + case CEC_DEVICE_TYPE_EMPTY:
> + default:
> + return -1;
> + }
> +}
> +
> +#if (CEC_MAX_LOG_ADDRS < CEC_MAX_NUM_LOG_ADDR)
> +#error The CEC_MAX_NUM_LOG_ADDR (lib define) is more than CEC_MAX_LOG_ADDRS \
> + (kernel framework defined)
> +#endif
> +
> +static int _cec_get_logical_addrs(struct cec_device *dev)
> +{
> + struct cec_log_addrs log_addr;
> + uint32_t dev_type;
> + uint32_t addr_type;
> + int ret;
> + int i;
> +
> + if (check_state(dev) != CEC_OK)
> + return CEC_ERROR;
> +
> + memset(&log_addr, 0, sizeof(log_addr));
> + ret = ioctl(dev->handle, CEC_G_ADAP_LOG_ADDRS, &log_addr);
> + if (ret)
> + return CEC_ERROR;
> +
> + for (i = 0; i < log_addr.num_log_addrs; i++) {
> + dev->dev_type_int[i] = log_addr.primary_device_type[i];
> + dev->log_addr_type_int[i] = log_addr.log_addr_type[i];
> + dev->log_addr[i] = log_addr.log_addr[i];
> + }
> +
> + dev->num_log_addr = log_addr.num_log_addrs;
> +
> + return CEC_OK;
> +}
> +
> +int cec_get_logical_addrs(struct cec_device *dev, uint8_t *addr, int *num_addr)
> +{
> + int i;
> +
> + if (!addr || !num_addr)
> + return CEC_ERROR;
> +
> + if (_cec_get_logical_addrs(dev) != CEC_OK)
> + return CEC_ERROR;
> +
> + *num_addr = dev->num_log_addr;
> + for (i = 0; i < *num_addr; i++)
> + addr[i] = dev->log_addr[i];
> +
> + return CEC_OK;
> +}
> +
> +int cec_add_logical_addr(struct cec_device *dev, enum cec_device_type type,
> + uint8_t *addr)
> +{
> + struct cec_log_addrs log_addr;
> + uint32_t dev_type;
> + uint32_t addr_type;
> + int ret;
> + int i;
> +
> + if (check_state(dev) != CEC_OK)
> + return CEC_ERROR;
> +
> + /* Refresh copy of logical addrs */
> + if (_cec_get_logical_addrs(dev) != CEC_OK)
> + return CEC_ERROR;
> +
> + if (dev->num_log_addr >= CEC_MAX_NUM_LOG_ADDR)
> + return CEC_NO_ADDR_LEFT;
> +
> + memset(&log_addr, 0, sizeof(log_addr));
> +
> + if (type != CEC_DEVICE_TYPE_EMPTY) {
> + dev->dev_type[dev->num_log_addr] = type;
> + dev->dev_type_int[dev->num_log_addr] = dev_type_to_int_dev_type(type);
> + dev->log_addr_type_int[dev->num_log_addr] = dev_type_to_int_addr_type(type);
> + if (dev->dev_type_int[dev->num_log_addr] == -1 ||
> + dev->log_addr_type_int[dev->num_log_addr] == -1)
> + return CEC_ERROR;
> + dev->num_log_addr++;
> + if (dev->num_log_addr >= CEC_MAX_NUM_LOG_ADDR) {
> + dev->num_log_addr--;
> + return CEC_NO_ADDR_LEFT;
> + }
> + }
> +
> + log_addr.cec_version = CEC_VERSION_1_4;
> + log_addr.num_log_addrs = dev->num_log_addr;
> + for (i = 0; i < dev->num_log_addr; i++) {
> + log_addr.primary_device_type[i] = dev->dev_type_int[i];
> + log_addr.log_addr_type[i] = dev->log_addr_type_int[i];
> + }
> + ret = ioctl(dev->handle, CEC_S_ADAP_LOG_ADDRS, &log_addr);
> + if (ret) {
> + /* Should it call set log addr again without the last added address? */
> + if (--dev->num_log_addr > 0)
> + cec_add_logical_addr(dev, CEC_DEVICE_TYPE_EMPTY, 0);
> + return CEC_ERROR;
> + }
> +
> + dev->log_addr[i - 1] = log_addr.log_addr[i - 1];
> + if (addr)
> + *addr = log_addr.log_addr[i - 1];
> +
> + return CEC_OK;
> +}
> +
> +int cec_clear_logical_addrs(struct cec_device *dev)
> +{
> + struct cec_log_addrs log_addr;
> + uint32_t dev_type;
> + uint32_t addr_type;
> + int ret;
> + int i;
> +
> + if (check_state(dev) != CEC_OK)
> + return CEC_ERROR;
> +
> + memset(&log_addr, 0, sizeof(log_addr));
> + log_addr.num_log_addrs = 0;
> + log_addr.cec_version = CEC_VERSION_1_4;
> +
> + ret = ioctl(dev->handle, CEC_S_ADAP_LOG_ADDRS, &log_addr);
> + if (ret)
> + return CEC_ERROR;
> +
> + return CEC_OK;
> +}
> +
> +int cec_get_physical_addr(struct cec_device *dev, uint16_t *phys_addr)
> +{
> + int ret;
> +
> + if (check_state(dev) != CEC_OK || !phys_addr)
> + return CEC_ERROR;
> + ret = ioctl(dev->handle, CEC_G_ADAP_PHYS_ADDR, phys_addr);
> + if (ret)
> + return CEC_ERROR;
> +
> + return CEC_OK;
> +}
> +
> +int cec_set_physical_addr(struct cec_device *dev, uint16_t phys_addr)
> +{
> + int ret;
> +
> + if (check_state(dev) != CEC_OK)
> + return CEC_ERROR;
> + ret = ioctl(dev->handle, CEC_S_ADAP_PHYS_ADDR, &phys_addr);
> + if (ret)
> + return CEC_ERROR;
> +
> + return CEC_OK;
> +}
> +
>
Regards,
Hans
More information about the dri-devel
mailing list