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