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 -- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html