diff -Nru ddcci-driver-linux-0.2/ddcci/ddcci.c ddcci-driver-linux-0.3.1/ddcci/ddcci.c --- ddcci-driver-linux-0.2/ddcci/ddcci.c 2016-04-09 00:13:00.000000000 +0000 +++ ddcci-driver-linux-0.3.1/ddcci/ddcci.c 2016-12-21 11:22:09.000000000 +0000 @@ -12,7 +12,6 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include #include #include #include @@ -24,6 +23,7 @@ #include #include #include +#include #include @@ -31,8 +31,8 @@ #define DEVICE_NAME "ddcci" static unsigned int delay = 60; -unsigned short autoprobe_addrs[127] = {0xF0, 0xF2, 0xF4, 0xF6, 0xF8}; -int autoprobe_addr_count = 5; +static unsigned short autoprobe_addrs[127] = {0xF0, 0xF2, 0xF4, 0xF6, 0xF8}; +static int autoprobe_addr_count = 5; static dev_t ddcci_cdev_first; static dev_t ddcci_cdev_next; @@ -42,6 +42,7 @@ struct bus_type ddcci_bus_type; EXPORT_SYMBOL_GPL(ddcci_bus_type); +/* Internal per-i2c-client driver data */ struct ddcci_bus_drv_data { unsigned long quirks; struct i2c_client *i2c_dev; @@ -72,22 +73,22 @@ /* first byte: sender address */ xor ^= addr; - if ((ret = i2c_smbus_write_byte(client, addr)) < 0) { + ret = i2c_smbus_write_byte(client, addr); + if (ret < 0) return ret; - } /* second byte: protocol flag and message size */ xor ^= ((p_flag << 7) | len); - if ((ret = i2c_smbus_write_byte(client, (p_flag << 7)|len)) < 0) { + ret = i2c_smbus_write_byte(client, (p_flag << 7)|len); + if (ret < 0) return ret; - } /* send payload */ while (len--) { xor ^= (*buf); - if ((ret = i2c_smbus_write_byte(client, (*buf))) < 0) { + ret = i2c_smbus_write_byte(client, (*buf)); + if (ret < 0) return ret; - } buf++; } @@ -153,7 +154,7 @@ pr_debug("sending to %d:%02x:%02x: %*ph\n", client->adapter->nr, - client->addr << 1, addr, len, data); + client->addr << 1, addr, len, data); if (drv_data->quirks & DDCCI_QUIRK_WRITE_BYTEWISE) { ret = __ddcci_write_bytewise(client, addr, p_flag, data, len); } else { @@ -204,7 +205,7 @@ /* validate first byte */ if (unlikely(buf[0] != addr)) { - ret = (buf[0] == '\0') ? -EAGAIN: -EIO; + ret = (buf[0] == '\0') ? -EAGAIN : -EIO; goto out_err; } @@ -218,17 +219,14 @@ /* get and check payload length */ payload_len = buf[1] & 0x7F; - if (3+payload_len > packet_length) { + if (3+payload_len > packet_length) return -EBADMSG; - } - if (3+payload_len > len) { + if (3+payload_len > len) return -EMSGSIZE; - } /* calculate checksum */ - for (i = 0; i < 3+payload_len; i++) { + for (i = 0; i < 3+payload_len; i++) xor ^= buf[i]; - } /* verify checksum */ if (xor != 0) { @@ -250,8 +248,8 @@ * * You must hold the bus semaphore when calling this function. */ -static int ddcci_read(struct i2c_client *client, unsigned char addr, bool p_flag, - unsigned char *buf, unsigned char len) +static int ddcci_read(struct i2c_client *client, unsigned char addr, + bool p_flag, unsigned char *buf, unsigned char len) { struct ddcci_bus_drv_data *drv_data; unsigned char *recvbuf; @@ -279,13 +277,13 @@ if (ret > len) { /* if message was truncated, return -EMSGSIZE */ pr_debug("received from %d:%02x:%02x: [%u/%u] %*ph ...\n", - client->adapter->nr, client->addr << 1, addr, - ret, len, len, buf); + client->adapter->nr, client->addr << 1, + addr, ret, len, len, buf); ret = -EMSGSIZE; } else { pr_debug("received from %d:%02x:%02x: [%u/%u] %*ph\n", - client->adapter->nr, client->addr << 1, addr, - ret, len, ret, buf); + client->adapter->nr, client->addr << 1, + addr, ret, len, ret, buf); } } } @@ -297,16 +295,15 @@ } /* Request the capability string for a device and put it into buf */ -static int ddcci_get_caps(struct i2c_client* client, unsigned char addr, +static int ddcci_get_caps(struct i2c_client *client, unsigned char addr, unsigned char *buf, unsigned int len) { int result = 0, counter = 0, offset = 0; unsigned char cmd[3] = { DDCCI_COMMAND_CAPS, 0x00, 0x00}; unsigned char *chunkbuf = kzalloc(35, GFP_KERNEL); - if (!chunkbuf) { + if (!chunkbuf) return -ENOMEM; - } do { /* Send command */ @@ -360,7 +357,7 @@ * The identification command will fail on most DDC devices, as it is optional * to support, but even the "failed" response suffices to detect quirks. */ -static int ddcci_identify_device(struct i2c_client* client, unsigned char addr, +static int ddcci_identify_device(struct i2c_client *client, unsigned char addr, unsigned char *buf, unsigned char len) { int i, payload_len, ret = -ENODEV; @@ -388,19 +385,18 @@ msleep(delay); } } - if (ret < 0 && (quirks & DDCCI_QUIRK_WRITE_BYTEWISE)) { + if (ret < 0 && (quirks & DDCCI_QUIRK_WRITE_BYTEWISE)) ret = __ddcci_write_bytewise(client, addr, true, cmd, 2); - } - if (ret < 0) { + if (ret < 0) return -ENODEV; - } /* Wait */ msleep(delay); /* Receive response */ ret = i2c_master_recv(client, buffer, DDCCI_RECV_BUFFER_SIZE); - if (ret < 3) return -ENODEV; + if (ret < 3) + return -ENODEV; /* Skip first byte if quirk already active */ if (quirks & DDCCI_QUIRK_SKIP_FIRST_BYTE) { @@ -409,14 +405,12 @@ } /* If answer too short (= incomplete) break out */ - if (ret < 3) { + if (ret < 3) return -EIO; - } /* validate first byte */ - if (buffer[0] != addr) { + if (buffer[0] != addr) return -ENODEV; - } /* Check if first byte is doubled (QUIRK_SKIP_FIRST_BYTE) */ if (!(quirks & DDCCI_QUIRK_SKIP_FIRST_BYTE)) { @@ -426,9 +420,8 @@ "DDC/CI bus quirk detected: doubled first byte on read\n"); ret--; buffer++; - if (ret < 3) { + if (ret < 3) return -EIO; - } } } @@ -441,14 +434,12 @@ /* get and check payload length */ payload_len = buffer[1] & 0x7F; - if (3+payload_len > ret) { + if (3+payload_len > ret) return -EBADMSG; - } /* calculate checksum */ - for (i = 0; i < 3+payload_len; i++) { + for (i = 0; i < 3+payload_len; i++) xor ^= buffer[i]; - } /* verify checksum */ if (xor != 0) { @@ -482,7 +473,7 @@ }; /* Called when the character device is opened */ -static int ddcci_cdev_open(struct inode* inode, struct file* filp) +static int ddcci_cdev_open(struct inode *inode, struct file *filp) { struct ddcci_device *dev = container_of(inode->i_cdev, struct ddcci_device, cdev); @@ -490,9 +481,8 @@ fp_data = kzalloc(sizeof(struct ddcci_fp_data), GFP_KERNEL); - if (!fp_data) { + if (!fp_data) return -ENOMEM; - } fp_data->exclusive = filp->f_flags & O_EXCL; @@ -515,16 +505,15 @@ } /* Called when the character device is closed */ -static int ddcci_cdev_close(struct inode* inode, struct file* filp) +static int ddcci_cdev_close(struct inode *inode, struct file *filp) { struct ddcci_fp_data *fp_data = filp->private_data; struct ddcci_device *dev = fp_data->dev; - if (fp_data->exclusive) { + if (fp_data->exclusive) up_write(&dev->cdev_sem); - } else { + else up_read(&dev->cdev_sem); - } filp->private_data = NULL; kfree(fp_data); @@ -532,8 +521,8 @@ } /* Called when reading from the character device */ -static ssize_t ddcci_cdev_read(struct file* filp, char __user *buffer, - size_t length, loff_t* offset) +static ssize_t ddcci_cdev_read(struct file *filp, char __user *buffer, + size_t length, loff_t *offset) { struct ddcci_fp_data *fp_data = filp->private_data; struct ddcci_device *dev = fp_data->dev; @@ -541,9 +530,8 @@ const bool nonblocking = (filp->f_flags & O_NONBLOCK) != 0; int ret = 0; - if ((filp->f_mode & FMODE_READ) == 0) { + if ((filp->f_mode & FMODE_READ) == 0) return -EBADF; - } /* Lock mutex */ if (nonblocking) { @@ -581,13 +569,11 @@ const bool nonblocking = (filp->f_flags & O_NONBLOCK) != 0; int ret = 0; - if ((filp->f_mode & FMODE_WRITE) == 0) { + if ((filp->f_mode & FMODE_WRITE) == 0) return -EBADF; - } - if (count > 127) { + if (count > 127) return -EINVAL; - } /* Lock mutex */ if (nonblocking) { @@ -627,7 +613,7 @@ return -EINVAL; } -static struct file_operations ddcci_fops = { +static const struct file_operations ddcci_fops = { .owner = THIS_MODULE, .read = ddcci_cdev_read, .write = ddcci_cdev_write, @@ -680,9 +666,8 @@ if (likely(device != NULL)) { len = device->capabilities_len; - if (unlikely(len > PAGE_SIZE)) { + if (unlikely(len > PAGE_SIZE)) len = PAGE_SIZE; - } if (len == 0) { ret = len; } else { @@ -813,9 +798,9 @@ struct ddcci_device *device = ddcci_verify_device(dev); ssize_t ret = -ENOENT; - if (likely(device != NULL)) { + if (likely(device != NULL)) ret = scnprintf(buf, PAGE_SIZE, "%d\n", device->device_number); - } + return ret; } @@ -878,7 +863,6 @@ return -ENOMEM; } - dev_dbg(dev, "uevent\n"); return 0; } @@ -897,9 +881,8 @@ /* Teardown chardev */ if (dev->devt) { down(&core_lock); - if (device->cdev.dev == ddcci_cdev_next-1) { + if (device->cdev.dev == ddcci_cdev_next-1) ddcci_cdev_next--; - } cdev_del(&device->cdev); up(&core_lock); } @@ -969,8 +952,10 @@ */ struct ddcci_device *ddcci_verify_device(struct device *dev) { - if (unlikely(dev == NULL)) return NULL; - return (dev->type == &ddcci_device_type || dev->type == &ddcci_dependent_type) + if (unlikely(!dev)) + return NULL; + return (dev->type == &ddcci_device_type + || dev->type == &ddcci_dependent_type) ? to_ddcci_device(dev) : NULL; } @@ -1003,6 +988,8 @@ if (unlikely(WARN_ON(!ddcci_bus_type.p))) return -EAGAIN; + pr_debug("registering driver [%s]\n", driver->driver.name); + /* add the driver to the list of ddcci drivers in the driver core */ driver->driver.owner = owner; driver->driver.bus = &ddcci_bus_type; @@ -1045,9 +1032,9 @@ { int ret; - if (down_interruptible(&dev->bus_drv_data->sem)) { + if (down_interruptible(&dev->bus_drv_data->sem)) return -EAGAIN; - } + ret = ddcci_write(dev->bus_drv_data->i2c_dev, dev->inner_addr, p_flag, data, length); msleep(delay); up(&dev->bus_drv_data->sem); @@ -1067,9 +1054,9 @@ { int ret; - if (down_interruptible(&dev->bus_drv_data->sem)) { + if (down_interruptible(&dev->bus_drv_data->sem)) return -EAGAIN; - } + ret = ddcci_read(dev->bus_drv_data->i2c_dev, dev->inner_addr, p_flag, buffer, length); up(&dev->bus_drv_data->sem); return ret; @@ -1093,11 +1080,12 @@ { int ret; - if (down_interruptible(&dev->bus_drv_data->sem)) { + if (down_interruptible(&dev->bus_drv_data->sem)) return -EAGAIN; - } + ret = ddcci_write(dev->bus_drv_data->i2c_dev, dev->inner_addr, p_flag, buffer, length); - if (ret < 0) goto err; + if (ret < 0) + goto err; msleep(delay); ret = ddcci_read(dev->bus_drv_data->i2c_dev, dev->inner_addr, p_flag, buffer, maxlength); err: @@ -1153,13 +1141,11 @@ driver = to_ddcci_driver(dev->driver); id = ddcci_match_id(driver->id_table, device); - if (id == NULL) { + if (!id) return -ENODEV; - } - if (driver->probe) { + if (driver->probe) ret = driver->probe(device, id); - } return ret; } @@ -1174,13 +1160,15 @@ return -EINVAL; driver = to_ddcci_driver(dev->driver); - if (driver->remove) { + if (driver->remove) ret = driver->remove(device); - } return ret; } +/** + * DDCCI bus type structure + */ struct bus_type ddcci_bus_type = { .name = "ddcci", .match = ddcci_device_match, @@ -1213,31 +1201,68 @@ return end; } -/* Search the capability string for a tag and copy the value to dest */ -static int ddcci_cpy_capstr_item(char *dest, const char *src, - const char *tag, size_t maxlen) +/** + * ddcci_find_capstr_item - Search capability string for a tag + * @capabilities: Capability string to search + * @tag: Tag to find + * @length: Buffer for the length of the found tag value (optional) + * + * Return a pointer to the start of the tag value (directly after the '(') on + * success and write the length of the value (excluding the ')') into `length`. + * + * If the tag is not found or another error occurs, an ERR_PTR is returned + * and `length` stays untouched. + */ +const char *ddcci_find_capstr_item(const char *capabilities, const char *tag, + ptrdiff_t *length) { - char *ptr; + const char *src = capabilities, *ptr; ptrdiff_t len; int taglen = strlen(tag); + /* Check length of requested tag */ + if (unlikely(taglen <= 0 || taglen > 65535)) + return ERR_PTR(-EINVAL); + /* Find tag */ while (src && (strncmp(src+1, tag, taglen) != 0 || src[1+taglen] != '(')) src = ddcci_capstr_tok(src+1, -1); if (!src || src[0] == '\0') - return -ENOENT; + return ERR_PTR(-ENOENT); /* Locate end of value */ src += taglen+2; ptr = ddcci_capstr_tok(src, 0); if (unlikely(!ptr)) - return -EOVERFLOW; + return ERR_PTR(-EOVERFLOW); - /* Copy value */ + /* Check length of tag data */ len = ptr-src; if (unlikely(len < 0 || len > 65535)) - return -EMSGSIZE; - memcpy(dest, src, (lencapabilities; int ret = 0; - if (!capstr) return -EINVAL; + + if (!capstr) + return -EINVAL; /* capability string start with a paren */ - if (capstr[0] != '(') return -EINVAL; + if (capstr[0] != '(') + return -EINVAL; /* get prot(...) */ ret = ddcci_cpy_capstr_item(device->prot, capstr, "prot", 8); @@ -1322,12 +1350,12 @@ device->dev.type = &ddcci_device_type; ret = dev_set_name(&device->dev, "ddcci%d", client->adapter->nr); } else if (outer_addr == dependent) { - // Internal dependent device + /* Internal dependent device */ device->dev.type = &ddcci_dependent_type; device->flags = DDCCI_FLAG_DEPENDENT; ret = dev_set_name(&device->dev, "ddcci%di%02x", client->adapter->nr, addr); } else if (outer_addr == addr) { - // External dependent device + /* External dependent device */ device->dev.type = &ddcci_dependent_type; device->flags = DDCCI_FLAG_DEPENDENT | DDCCI_FLAG_EXTERNAL; ret = dev_set_name(&device->dev, "ddcci%de%02x", client->adapter->nr, addr); @@ -1339,19 +1367,17 @@ ret = dev_set_name(&device->dev, "ddcci%de%02x%02x", client->adapter->nr, outer_addr, addr); } - if (ret) { + if (ret) goto err_free; - } /* Read identification and check for quirks */ ret = ddcci_identify_device(client, addr, buffer, 29); - if (ret < 0) { + if (ret < 0) goto err_free; - } if (ret == 29 && buffer[0] == DDCCI_REPLY_ID) { memcpy(device->vendor, &buffer[7], 8); memcpy(device->module, &buffer[17], 8); - device->device_number = ntohl(*(__s32*)&buffer[18]); + device->device_number = be32_to_cpu(*(__force __be32 *)&buffer[18]); } /* Read capabilities */ @@ -1360,13 +1386,13 @@ device->capabilities = kzalloc(ret+1, GFP_KERNEL); if (!device->capabilities) { ret = -ENOMEM; - goto err_free; } device->capabilities_len = ret; memcpy(device->capabilities, buffer, ret); - if ((ret = ddcci_parse_capstring(device))) { + ret = ddcci_parse_capstring(device); + if (ret) { dev_err(&device->dev, "malformed capability string: %d", ret); ret = -EINVAL; goto err_free; @@ -1382,12 +1408,14 @@ /* Setup chardev */ down(&core_lock); ret = ddcci_setup_char_device(device); - if (ret) goto err_free; + if (ret) + goto err_free; /* Add device */ pr_debug("found device at %d:%02x:%02x\n", client->adapter->nr, outer_addr, addr); ret = device_add(&device->dev); - if (ret) goto err_free; + if (ret) + goto err_free; goto end; err_free: @@ -1409,8 +1437,10 @@ unsigned char cmd[2] = { DDCCI_COMMAND_ID, 0x00 }; /* Check for i2c_master_* functionality */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_debug("i2c adapter %d unsuitable: no i2c_master functionality\n", client->adapter->nr); return -ENODEV; + } /* send Identification Request command */ outer_addr = client->addr << 1; @@ -1430,19 +1460,21 @@ } } - if (ret < 0) { + if (ret < 0) return -ENODEV; - } /* wait for device */ msleep(delay); /* receive answer */ - if ((ret = i2c_master_recv(client, buf, 32)) < 3) { + ret = i2c_master_recv(client, buf, 32); + if (ret < 3) { + pr_debug("detection failed: no answer\n"); return -ENODEV; } /* check response starts with outer addr */ if (buf[0] != outer_addr) { + pr_debug("detection failed: invalid answer\n"); return -ENODEV; } @@ -1463,9 +1495,8 @@ /* Initialize driver data structure */ drv_data = devm_kzalloc(&client->dev, sizeof(struct ddcci_bus_drv_data), GFP_KERNEL); - if (!drv_data) { + if (!drv_data) return -ENOMEM; - } drv_data->i2c_dev = client; sema_init(&drv_data->sem, 1); @@ -1477,7 +1508,8 @@ main_addr = DDCCI_DEFAULT_DEVICE_ADDR; dev_dbg(&client->dev, "probing core device [%02x]\n", client->addr << 1); - if ((ret = ddcci_detect_device(client, main_addr, 0))) { + ret = ddcci_detect_device(client, main_addr, 0); + if (ret) { dev_info(&client->dev, "core device [%02x] probe failed: %d\n", client->addr << 1, ret); if (ret == -EIO) @@ -1488,7 +1520,7 @@ /* Detect internal dependent devices */ dev_dbg(&client->dev, "probing internal dependent devices\n"); for (i = 0; i < autoprobe_addr_count; ++i) { - addr = (unsigned short)autoprobe_addrs[i]; + addr = (unsigned short)autoprobe_addrs[i]; if ((addr & 1) == 0 && addr != main_addr) { tmp = ddcci_detect_device(client, addr, main_addr); if (tmp < 0 && tmp != -ENODEV) { @@ -1501,7 +1533,8 @@ /* External dependent device */ main_addr = client->addr << 1; dev_dbg(&client->dev, "probing external dependent device [%02x]\n", main_addr); - if ((ret = ddcci_detect_device(client, main_addr, -1))) { + ret = ddcci_detect_device(client, main_addr, -1); + if (ret) { dev_info(&client->dev, "external dependent device [%02x] probe failed: %d\n", main_addr, ret); if (ret == -EIO) @@ -1527,19 +1560,19 @@ * Find next device with matching outer address not flagged with * DDCCI_FLAG_REMOVED and flag it. */ -static int ddcci_remove_helper(struct device *dev, void* p) +static int ddcci_remove_helper(struct device *dev, void *p) { unsigned char outer_addr; struct ddcci_device *device; if (p) - outer_addr = *(unsigned char*)p; + outer_addr = *(unsigned char *)p; else outer_addr = 0; - device = ddcci_verify_device(dev); - if (!device || device->flags & DDCCI_FLAG_REMOVED) return 0; + if (!device || device->flags & DDCCI_FLAG_REMOVED) + return 0; if (!outer_addr || (device->outer_addr == outer_addr)) { device->flags |= DDCCI_FLAG_REMOVED; @@ -1561,7 +1594,8 @@ while (1) { dev = bus_find_device(&ddcci_bus_type, NULL, &outer_addr, ddcci_remove_helper); - if (!dev) break; + if (!dev) + break; device_unregister(dev); put_device(dev); } @@ -1569,14 +1603,19 @@ return 0; } -static struct i2c_device_id ddcci_idtable[] = { +/* + * I2C driver device identification table. + */ +static const struct i2c_device_id ddcci_idtable[] = { { "ddcci", 0 }, { "ddcci-dependent", 1 }, {} }; - MODULE_DEVICE_TABLE(i2c, ddcci_idtable); +/* + * I2C driver description structure + */ static struct i2c_driver ddcci_driver = { .driver = { .name = "ddcci", @@ -1593,9 +1632,15 @@ ), }; +/* + * Module initialization function. Called when the module is inserted or + * (if builtin) at boot time. + */ static int __init ddcci_module_init(void) { int ret; + + pr_debug("initializing ddcci driver\n"); /* Allocate a device number region for the character devices */ ret = alloc_chrdev_region(&ddcci_cdev_first, 0, 128, DEVICE_NAME); if (ret < 0) { @@ -1619,6 +1664,8 @@ goto err_drvreg; } + pr_debug("ddcci driver initialized\n"); + return 0; err_drvreg: @@ -1629,12 +1676,17 @@ return ret; } +/* + * Module clean-up function. Called when the module is removed. + */ static void __exit ddcci_module_exit(void) { struct device *dev; + while (1) { dev = bus_find_device(&ddcci_bus_type, NULL, NULL, ddcci_remove_helper); - if (!dev) break; + if (!dev) + break; device_unregister(dev); put_device(dev); } @@ -1657,5 +1709,5 @@ /* Module description */ MODULE_AUTHOR("Christoph Grenz"); MODULE_DESCRIPTION("DDC/CI bus driver"); -MODULE_VERSION("0.2"); +MODULE_VERSION("0.3.1"); MODULE_LICENSE("GPL"); diff -Nru ddcci-driver-linux-0.2/ddcci/Makefile ddcci-driver-linux-0.3.1/ddcci/Makefile --- ddcci-driver-linux-0.2/ddcci/Makefile 2016-04-09 00:13:00.000000000 +0000 +++ ddcci-driver-linux-0.3.1/ddcci/Makefile 2016-12-21 11:22:09.000000000 +0000 @@ -1,22 +1,22 @@ #!/usr/bin/make -f # (c) 2015 Christoph Grenz -# This file is part of ddcci-bus-linux. +# This file is part of ddcci-driver-linux. # -# ddcci-bus-linux is free software: you can redistribute it and/or modify +# ddcci-driver-linux is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # -# ddcci-bus-linux is distributed in the hope that it will be useful, +# ddcci-driver-linux is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with ddcci-bus-linux. If not, see . +# along with ddcci-driver-linux. If not, see . MODULE_NAME := ddcci -MODULE_VERSION := 0.2 +MODULE_VERSION := 0.3.1 KVER := $(shell uname -r) KERNEL_MODLIB := /lib/modules/$(KVER) diff -Nru ddcci-driver-linux-0.2/ddcci-backlight/ddcci-backlight.c ddcci-driver-linux-0.3.1/ddcci-backlight/ddcci-backlight.c --- ddcci-driver-linux-0.2/ddcci-backlight/ddcci-backlight.c 2016-04-09 00:13:00.000000000 +0000 +++ ddcci-driver-linux-0.3.1/ddcci-backlight/ddcci-backlight.c 2016-12-21 11:22:09.000000000 +0000 @@ -26,6 +26,7 @@ #define DDCCI_MONITOR_LUMINANCE 0x10 #define DDCCI_MONITOR_BACKLIGHT 0x13 +#define DDCCI_MONITOR_BL_WHITE 0x6B struct ddcci_monitor_drv_data { struct ddcci_device *device; @@ -34,7 +35,7 @@ unsigned char used_vcp; }; -static int ddcci_monitor_writectrl(struct ddcci_device* device, +static int ddcci_monitor_writectrl(struct ddcci_device *device, unsigned char ctrl, unsigned short value) { unsigned char buf[4]; @@ -50,7 +51,7 @@ return ret; } -static int ddcci_monitor_readctrl(struct ddcci_device* device, +static int ddcci_monitor_readctrl(struct ddcci_device *device, unsigned char ctrl, unsigned short *value, unsigned short *maximum) { @@ -61,22 +62,23 @@ buf[1] = ctrl; ret = ddcci_device_writeread(device, true, buf, 2, sizeof(buf)); - if (ret < 0) return ret; + if (ret < 0) + return ret; - if (ret == 0) return -ENOTSUPP; + if (ret == 0) + return -ENOTSUPP; - if (ret == 8 && buf[0] == DDCCI_REPLY_READ && buf[2] == ctrl) - { - if (value) { + if (ret == 8 && buf[0] == DDCCI_REPLY_READ && buf[2] == ctrl) { + if (value) *value = buf[6] * 256 + buf[7]; - } - if (maximum) { + if (maximum) *maximum = buf[4] * 256 + buf[5]; - } - if (buf[1] == 1) return -ENOTSUPP; - if (buf[1] != 0) return -EIO; + if (buf[1] == 1) + return -ENOTSUPP; + if (buf[1] != 0) + return -EIO; return 0; } @@ -101,9 +103,10 @@ bl->props.state & BL_CORE_FBBLANK) brightness = 0; - ret = ddcci_monitor_writectrl(drv_data->device, DDCCI_MONITOR_LUMINANCE, + ret = ddcci_monitor_writectrl(drv_data->device, drv_data->used_vcp, brightness); - if (ret > 0) return 0; + if (ret > 0) + ret = 0; return ret; } @@ -113,7 +116,7 @@ int ret; struct ddcci_monitor_drv_data *drv_data = bl_get_data(bl); - ret = ddcci_monitor_readctrl(drv_data->device, DDCCI_MONITOR_LUMINANCE, + ret = ddcci_monitor_readctrl(drv_data->device, drv_data->used_vcp, &value, &maxval); if (ret < 0) return ret; @@ -132,6 +135,78 @@ .check_fb = ddcci_backlight_check_fb, }; +static const char *ddcci_monitor_vcp_name(unsigned char vcp) +{ + switch (vcp) { + case DDCCI_MONITOR_BL_WHITE: + return "backlight"; + case DDCCI_MONITOR_LUMINANCE: + return "luminance"; + default: + return "???"; + } +} + +static const char *ddcci_monitor_next_vcp_item(const char *ptr) +{ + int depth = 0; + + /* Sanity check */ + if (unlikely(ptr == NULL || ptr[0] == '\0')) + return NULL; + + /* Find next white space outside of parentheses */ + while ((ptr = strpbrk(ptr, " ()"))) { + if (!ptr || depth == INT_MAX) { + return NULL; + } else if (*ptr == '(') { + depth++; + } else if (depth > 0) { + if (*ptr == ')') + depth--; + } else { + break; + } + ++ptr; + } + + /* Skip over whitespace */ + ptr = skip_spaces(ptr); + + /* Check if we're now at the end of the list */ + if (unlikely(*ptr == '\0' || *ptr == ')')) + return NULL; + + return ptr; +} + +static bool ddcci_monitor_find_vcp(unsigned char vcp, const char *s) +{ + const char *ptr = s; + char vcp_hex[3]; + + /* Sanity check */ + if (unlikely(s == NULL || s[0] == '\0')) + return false; + + /* Create hex representation of VCP */ + if (unlikely(snprintf(vcp_hex, 3, "%02hhX", vcp) != 2)) { + pr_err("snprintf failed to convert to hex. This should not happen.\n"); + return false; + } + + /* Search for it */ + do { + if (strncasecmp(vcp_hex, ptr, 2) == 0) { + if (ptr[2] == ' ' || ptr[2] == '(' || ptr[2] == ')') { + return true; + } + } + } while ((ptr = ddcci_monitor_next_vcp_item(ptr))); + + return false; +} + static int ddcci_monitor_probe(struct ddcci_device *dev, const struct ddcci_device_id *id) { @@ -139,24 +214,74 @@ struct backlight_properties props; struct backlight_device *bl = NULL; int ret = 0; + bool support_luminance, support_bl_white; unsigned short brightness = 0, max_brightness = 0; + const char *vcps; + + dev_dbg(&dev->dev, "probing monitor backlight device\n"); + + /* Get VCP list */ + vcps = ddcci_find_capstr_item(dev->capabilities, "vcp", NULL); + if (IS_ERR(vcps)) { + dev_info(&dev->dev, + "monitor doesn't provide a list of supported controls.\n"); + support_bl_white = support_luminance = true; + } else { + /* Check VCP list for supported VCPs */ + support_bl_white = ddcci_monitor_find_vcp(DDCCI_MONITOR_BL_WHITE, vcps); + support_luminance = ddcci_monitor_find_vcp(DDCCI_MONITOR_LUMINANCE, vcps); + /* Fallback to trying if no support is found */ + if (!support_bl_white && !support_luminance) { + dev_info(&dev->dev, + "monitor doesn't announce support for backlight or luminance controls.\n"); + support_bl_white = support_luminance = true; + } + } /* Initialize driver data structure */ drv_data = devm_kzalloc(&dev->dev, sizeof(struct ddcci_monitor_drv_data), GFP_KERNEL); - if (!drv_data) return -ENOMEM; + if (!drv_data) + return -ENOMEM; drv_data->device = dev; - /* Try getting luminance */ - ret = ddcci_monitor_readctrl(drv_data->device, DDCCI_MONITOR_LUMINANCE, - &brightness, &max_brightness); - if (ret < 0) { - if (ret == -ENOTSUPP) - dev_info(&dev->dev, - "monitor does not support reading luminance\n"); - goto err_free; + if (support_bl_white) { + /* Try getting backlight level */ + dev_dbg(&dev->dev, + "trying to access \"backlight level white\" control\n"); + ret = ddcci_monitor_readctrl(drv_data->device, DDCCI_MONITOR_BL_WHITE, + &brightness, &max_brightness); + if (ret < 0) { + if (ret == -ENOTSUPP) + dev_info(&dev->dev, + "monitor does not support reading backlight level\n"); + else + goto err_free; + } else { + drv_data->used_vcp = DDCCI_MONITOR_BL_WHITE; + } } - drv_data->used_vcp = DDCCI_MONITOR_LUMINANCE; + + if (support_luminance && !drv_data->used_vcp) { + /* Try getting luminance */ + dev_dbg(&dev->dev, + "trying to access \"luminance\" control\n"); + ret = ddcci_monitor_readctrl(drv_data->device, DDCCI_MONITOR_LUMINANCE, + &brightness, &max_brightness); + if (ret < 0) { + if (ret == -ENOTSUPP) + dev_info(&dev->dev, + "monitor does not support reading luminance\n"); + else + goto err_free; + } else { + drv_data->used_vcp = DDCCI_MONITOR_LUMINANCE; + } + drv_data->used_vcp = DDCCI_MONITOR_LUMINANCE; + } + + if (!drv_data->used_vcp) + goto err_free; /* Create brightness device */ memset(&props, 0, sizeof(props)); @@ -164,14 +289,15 @@ props.max_brightness = max_brightness; props.brightness = brightness; bl = devm_backlight_device_register(&dev->dev, dev_name(&dev->dev), - &dev->dev, drv_data, &ddcci_backlight_ops, - &props); + &dev->dev, drv_data, + &ddcci_backlight_ops, &props); drv_data->bl_dev = bl; if (IS_ERR(bl)) { dev_err(&dev->dev, "failed to register backlight\n"); return PTR_ERR(bl); } - dev_info(&dev->dev, "registered luminance as backlight device %s\n", + dev_info(&dev->dev, "registered %s as backlight device %s\n", + ddcci_monitor_vcp_name(drv_data->used_vcp), dev_name(&dev->dev)); goto end; @@ -192,9 +318,9 @@ {} }; -static struct ddcci_driver ddcci_monitor_driver = { +static struct ddcci_driver ddcci_backlight_driver = { .driver = { - .name = "ddcci-monitor", + .name = "ddcci-backlight", .owner = THIS_MODULE, }, @@ -203,11 +329,11 @@ .remove = ddcci_monitor_remove, }; -module_ddcci_driver(ddcci_monitor_driver); +module_ddcci_driver(ddcci_backlight_driver); MODULE_AUTHOR("Christoph Grenz"); MODULE_DESCRIPTION("DDC/CI generic monitor backlight driver"); -MODULE_VERSION("0.2"); +MODULE_VERSION("0.3.1"); MODULE_LICENSE("GPL"); MODULE_ALIAS("ddcci:monitor-*-*-*-*"); diff -Nru ddcci-driver-linux-0.2/ddcci-backlight/Makefile ddcci-driver-linux-0.3.1/ddcci-backlight/Makefile --- ddcci-driver-linux-0.2/ddcci-backlight/Makefile 2016-04-09 00:13:00.000000000 +0000 +++ ddcci-driver-linux-0.3.1/ddcci-backlight/Makefile 2016-12-21 11:22:09.000000000 +0000 @@ -1,22 +1,22 @@ #!/usr/bin/make -f # (c) 2015 Christoph Grenz -# This file is part of ddcci-bus-linux. +# This file is part of ddcci-driver-linux. # -# ddcci-bus-linux is free software: you can redistribute it and/or modify +# ddcci-driver-linux is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # -# ddcci-bus-linux is distributed in the hope that it will be useful, +# ddcci-driver-linux is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with ddcci-bus-linux. If not, see . +# along with ddcci-driver-linux. If not, see . MODULE_NAME := ddcci-backlight -MODULE_VERSION := 0.2 +MODULE_VERSION := 0.3.1 KVER := $(shell uname -r) KERNEL_MODLIB := /lib/modules/$(KVER) diff -Nru ddcci-driver-linux-0.2/debian/changelog ddcci-driver-linux-0.3.1/debian/changelog --- ddcci-driver-linux-0.2/debian/changelog 2016-08-17 21:29:36.000000000 +0000 +++ ddcci-driver-linux-0.3.1/debian/changelog 2016-12-22 22:09:04.000000000 +0000 @@ -1,3 +1,15 @@ +ddcci-driver-linux (0.3.1-1) unstable; urgency=medium + + * New upstream release, fixing the version numbers. + + -- Stephen Kitt Thu, 22 Dec 2016 23:09:04 +0100 + +ddcci-driver-linux (0.3-1) unstable; urgency=medium + + * New upstream release, merging ddcci-backlight-dependency.patch. + + -- Stephen Kitt Tue, 20 Dec 2016 23:21:15 +0100 + ddcci-driver-linux (0.2-2) unstable; urgency=medium * Enforce the dependency between ddcci-backlight and ddcci at build-time diff -Nru ddcci-driver-linux-0.2/debian/patches/ddcci-backlight-dependency.patch ddcci-driver-linux-0.3.1/debian/patches/ddcci-backlight-dependency.patch --- ddcci-driver-linux-0.2/debian/patches/ddcci-backlight-dependency.patch 2016-08-17 21:26:13.000000000 +0000 +++ ddcci-driver-linux-0.3.1/debian/patches/ddcci-backlight-dependency.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -From a5cd99012f1379e5a3d525ddb988f7110259369a Mon Sep 17 00:00:00 2001 -From: Stephen Kitt -Date: Wed, 17 Aug 2016 18:33:51 +0000 -Subject: [PATCH] Declare ddcci-backlight's dependency on ddcci - -Make needs to know about the dependency in order to handle -parallelized builds correctly. Fixes #1. - -Signed-off-by: Stephen Kitt - ---- - Makefile | 2 ++ - 1 file changed, 2 insertions(+), 0 deletions(-) - -diff --git a/Makefile b/Makefile -index 6139b9d..301f2be 100644 ---- a/Makefile -+++ b/Makefile -@@ -17,6 +17,8 @@ - - MODULES := ddcci ddcci-backlight - -+ddcci-backlight: ddcci -+ - reverse = $(if $(1),$(call reverse,$(wordlist 2,$(words $(1)),$(1)))) $(firstword $(1)) - REVMODS := $(call reverse,$(MODULES)) - --- -libgit2 0.24.0 diff -Nru ddcci-driver-linux-0.2/debian/patches/series ddcci-driver-linux-0.3.1/debian/patches/series --- ddcci-driver-linux-0.2/debian/patches/series 2016-08-17 18:17:08.000000000 +0000 +++ ddcci-driver-linux-0.3.1/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -ddcci-backlight-dependency.patch diff -Nru ddcci-driver-linux-0.2/dkms.conf ddcci-driver-linux-0.3.1/dkms.conf --- ddcci-driver-linux-0.2/dkms.conf 2016-04-09 00:13:00.000000000 +0000 +++ ddcci-driver-linux-0.3.1/dkms.conf 2016-12-21 11:22:09.000000000 +0000 @@ -1,4 +1,4 @@ -PACKAGE_VERSION="0.2" +PACKAGE_VERSION="0.3.1" PACKAGE_NAME="ddcci" CLEAN="make clean" BUILT_MODULE_NAME[0]="ddcci" diff -Nru ddcci-driver-linux-0.2/include/linux/ddcci.h ddcci-driver-linux-0.3.1/include/linux/ddcci.h --- ddcci-driver-linux-0.2/include/linux/ddcci.h 2016-04-09 00:13:00.000000000 +0000 +++ ddcci-driver-linux-0.3.1/include/linux/ddcci.h 2016-12-21 11:22:09.000000000 +0000 @@ -68,7 +68,7 @@ char module[9]; kernel_ulong_t driver_data; /* Data private to the driver */ }; -#define DDCCI_ANY_ID "\xFF\xFF\xFF\xFFx\xFF\xFF\xFF\xFF" +#define DDCCI_ANY_ID "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" /** * struct ddcci_device - represent an DDC/CI device @@ -158,4 +158,7 @@ unsigned long ddcci_quirks(struct ddcci_device *dev); -#endif \ No newline at end of file +const char *ddcci_find_capstr_item(const char *capabilities, const char *tag, + ptrdiff_t *length); + +#endif diff -Nru ddcci-driver-linux-0.2/Makefile ddcci-driver-linux-0.3.1/Makefile --- ddcci-driver-linux-0.2/Makefile 2016-04-09 00:13:00.000000000 +0000 +++ ddcci-driver-linux-0.3.1/Makefile 2016-12-21 11:22:09.000000000 +0000 @@ -1,22 +1,24 @@ #!/usr/bin/make -f # (c) 2015 Christoph Grenz -# This file is part of ddcci-bus-linux. +# This file is part of ddcci-driver-linux. # -# ddcci-bus-linux is free software: you can redistribute it and/or modify +# ddcci-driver-linux is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # -# ddcci-bus-linux is distributed in the hope that it will be useful, +# ddcci-driver-linux is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with ddcci-bus-linux. If not, see . +# along with ddcci-driver-linux. If not, see . MODULES := ddcci ddcci-backlight +ddcci-backlight: ddcci + reverse = $(if $(1),$(call reverse,$(wordlist 2,$(words $(1)),$(1)))) $(firstword $(1)) REVMODS := $(call reverse,$(MODULES)) diff -Nru ddcci-driver-linux-0.2/Makefile.dkms ddcci-driver-linux-0.3.1/Makefile.dkms --- ddcci-driver-linux-0.2/Makefile.dkms 2016-04-09 00:13:00.000000000 +0000 +++ ddcci-driver-linux-0.3.1/Makefile.dkms 2016-12-21 11:22:09.000000000 +0000 @@ -1,22 +1,22 @@ #!/usr/bin/make -f # (c) 2015 Christoph Grenz -# This file is part of ddcci-bus-linux. +# This file is part of ddcci-driver-linux. # -# ddcci-bus-linux is free software: you can redistribute it and/or modify +# ddcci-driver-linux is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # -# ddcci-bus-linux is distributed in the hope that it will be useful, +# ddcci-driver-linux is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with ddcci-bus-linux. If not, see . +# along with ddcci-driver-linux. If not, see . PACKAGE_NAME := ddcci -PACKAGE_VERSION := 0.2 +PACKAGE_VERSION := 0.3.1 install: dkms add . @@ -28,7 +28,7 @@ rm -r /usr/src/$(PACKAGE_NAME)-$(PACKAGE_VERSION) || true load: - @test -n "$$(dkms status ddcci/0.2)" || { echo 'Please run `make -f Makefile.dkms install` first.'; false; } + @test -n "$$(dkms status ddcci/0.3.1)" || { echo 'Please run `make -f Makefile.dkms install` first.'; false; } modprobe ddcci unload: diff -Nru ddcci-driver-linux-0.2/README.md ddcci-driver-linux-0.3.1/README.md --- ddcci-driver-linux-0.2/README.md 2016-04-09 00:13:00.000000000 +0000 +++ ddcci-driver-linux-0.3.1/README.md 2016-12-21 11:22:09.000000000 +0000 @@ -98,7 +98,7 @@ ## ddcci-backlight (monitor backlight driver) ## -For each monitor that supports accessing the Luminance property, a backlight device of type "raw" named like the corresponding ddcci device is created. You can find them +For each monitor that supports accessing the Backlight Level White or the Luminance property, a backlight device of type "raw" named like the corresponding ddcci device is created. You can find them in `/sys/class/backlight/`. ## Limitations ## @@ -110,12 +110,22 @@ You can force detection of external dependent devices by writing "ddcci-dependent [address]" into /sys/bus/i2c/i2c-?/new_device. -Currently only the Luminance setting is used by ddcci-backlight. There seems to be a setting called "Backlight Control" -(0x13) but I don't own a monitor supporting this VCP, so I couldn't test it. - There is no direct synchronization if you manually change the luminance with the buttons on your monitor, as this can only be realized through polling and some monitors close their OSD every time a DDC/CI command is received. +Monitor hotplugging is not detected. You need to detach/reattach the I²C driver or reload the module. + ## Installation ## Generally, you only need to clone the repository and run make to build both kernel modules, then run make load. To permanently install the drivers, use `make install` (or `make -f Makefile.dkms install` if you prefer DKMS). + +## Debugging ## + +Both drivers use the [dynamic debugging feature](https://www.kernel.org/doc/Documentation/dynamic-debug-howto.txt) of the Linux kernel. + +To get detailed debugging messages, set the `dyndbg` module parameter. For details on the syntax and for dynamic activation/deactivation of the debugging messages, see the documentation linked above. + +If you want to enable debugging permanently across reboots, create a file `/etc/modprobe.d/ddcci.conf` containing lines like the following before loading the modules: + + options ddcci dyndbg + options ddcci-backlight dyndbg