ACPI 5.0 specification introduces mechanisms of enumerating the peripherals connected on the serial buses. It will look like: Device (BTH0) { Name (_HID, "IBMF001") // _HID: Hardware ID Name (_CID, "PNPF001") // _CID: Compatible ID Method (_CRS, 0, NotSerialized) // _CRS: Current Resource Settings { Name (UBUF, ResourceTemplate () { UartSerialBus (0x0001C200, DataBitsEight, StopBitsOne, 0xC0, LittleEndian, ParityTypeNone, FlowControlHardware, 0x0020, 0x0020, "\\_SB.PCI0.UA00", 0x00, ResourceConsumer, , ) } Return (UBUF) } } This patch follows the specification, implementing such UART enumeration machanism by exporting current peripheral HID/CIDs for the UART devices. A userspace udev rule may look like: SUBSYSTEM=tty, ATTR{peripheral_type}=="*:IBMF001:*", RUN+="/bin/userprog --devpath=%p" Thanks for the internal composers: Mika Westerberg <mika.westerberg@xxxxxxxxx> Huang Ying <ying.huang@xxxxxxxxx> Zhang Rui <rui.zhang@xxxxxxxxx> Thanks for the internal reviewers: Alan Cox <alan@xxxxxxxxxxxxxxx> Rafael J. Wysocki <rafael.j.wysocki@xxxxxxxxx> Heikki Krogerus <heikki.krogerus@xxxxxxxxx> Andriy Shevchenko <andriy.shevchenko@xxxxxxxxx> Fengguang Wu <fengguang.wu@xxxxxxxxx> Signed-off-by: Lv Zheng <lv.zheng@xxxxxxxxx> Signed-off-by: Zhang Rui <rui.zhang@xxxxxxxxx> Acked-by: Huang Ying <ying.huang@xxxxxxxxx> Reviewed-by: Mika Westerberg <mika.westerberg@xxxxxxxxxxxxxxx> --- drivers/acpi/Kconfig | 6 ++ drivers/acpi/Makefile | 1 + drivers/acpi/acpi_uart.c | 135 ++++++++++++++++++++++++++++++++++++++ drivers/acpi/internal.h | 2 + drivers/acpi/scan.c | 8 +-- drivers/tty/serial/serial_core.c | 15 +++++ include/linux/tty.h | 11 ++++ 7 files changed, 174 insertions(+), 4 deletions(-) create mode 100644 drivers/acpi/acpi_uart.c diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 92ed969..42233f6 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -186,6 +186,12 @@ config ACPI_I2C help ACPI I2C enumeration support. +config ACPI_UART + def_tristate TTY + depends on TTY + help + ACPI UART enumeration support. + config ACPI_PROCESSOR tristate "Processor" select THERMAL diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 474fcfe..4db5470 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o obj-$(CONFIG_ACPI_BGRT) += bgrt.o obj-$(CONFIG_ACPI_I2C) += acpi_i2c.o +obj-$(CONFIG_ACPI_UART) += acpi_uart.o # processor has its own "processor." module_param namespace processor-y := processor_driver.o processor_throttling.o diff --git a/drivers/acpi/acpi_uart.c b/drivers/acpi/acpi_uart.c new file mode 100644 index 0000000..dd5ac2e --- /dev/null +++ b/drivers/acpi/acpi_uart.c @@ -0,0 +1,135 @@ +/* + * acpi_uart.c - ACPI UART enumeration support + * + * Copyright (c) 2013, Intel Corporation + * Author: Lv Zheng <lv.zheng@xxxxxxxxx> + * + * This program 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; version 2 of the License. + */ + +#include <linux/init.h> +#include <linux/tty.h> +#include <linux/module.h> +#include <linux/acpi.h> + +#include "internal.h" + +struct acpi_uart_buf { + char *buf; + size_t size; + int *len; +}; + +struct acpi_uart_enum { + void *context; + acpi_status status; + + struct acpi_device *adev; +}; + +static acpi_status +acpi_uart_enum_pnpids(struct acpi_device *adev, + struct acpi_resource_uart_serialbus *sb, + void *context); + +static int acpi_uart_enum_resources(struct acpi_resource *ares, + void *context) +{ + struct acpi_uart_enum *ctx = context; + struct acpi_resource_uart_serialbus *sb; + + if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) + return 1; + sb = &ares->data.uart_serial_bus; + if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_UART) + return 1; + + ctx->status = acpi_uart_enum_pnpids(ctx->adev, sb, ctx->context); + return 1; +} + +static acpi_status acpi_uart_enum_devices(acpi_handle handle, u32 level, + void *context, + void **return_value) +{ + struct acpi_uart_enum *ctx = context; + struct acpi_device *adev; + struct list_head resource_list; + + if (acpi_bus_get_device(handle, &adev)) + return AE_OK; + if (acpi_bus_get_status(adev) || !adev->status.present) + return AE_OK; + + /* Enumerate resources. */ + ctx->adev = adev; + ctx->status = AE_OK; + INIT_LIST_HEAD(&resource_list); + acpi_dev_get_resources(adev, &resource_list, + acpi_uart_enum_resources, ctx); + acpi_dev_free_resource_list(&resource_list); + + return ctx->status; +} + +static int acpi_uart_walk_port(struct device *parent, + void *context) +{ + acpi_handle handle; + acpi_status status; + struct acpi_uart_enum ctx; + + handle = parent ? ACPI_HANDLE(parent) : NULL; + if (!handle) + return -ENODEV; + + ctx.context = context; + + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, + acpi_uart_enum_devices, NULL, + &ctx, NULL); + if (ACPI_FAILURE(status)) + return -EINVAL; + + return 0; +} + +static acpi_status +acpi_uart_enum_pnpids(struct acpi_device *adev, + struct acpi_resource_uart_serialbus *sb, + void *context) +{ + int len; + struct acpi_uart_buf *ctx = context; + + len = acpi_device_create_modalias(adev, ctx->buf, ctx->size); + *ctx->len = len; + + return len < 0 ? AE_CTRL_DEPTH : AE_OK; +} + +/** + * acpi_uart_get_peripheral_type - get peripheral HID/CIDs + * @dev: the tty class device + * @buf: the string buffer containing HID/CIDs + * @size: the size of the string buffer + * + * Obtain UART peripheral HID/CIDs. Return buffer size on success, + * -errno on failure. + */ +int acpi_uart_get_peripheral_type(struct device *dev, + char *buf, size_t size) +{ + int ret; + int len = 0; + struct acpi_uart_buf ctx = { buf, size, &len }; + + ret = acpi_uart_walk_port(dev->parent, &ctx); + if (ret < 0) + return ret; + + return len; +} +EXPORT_SYMBOL_GPL(acpi_uart_get_peripheral_type); diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 3c94a73..30d7440 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -61,6 +61,8 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, int type, unsigned long long sta); void acpi_device_add_finalize(struct acpi_device *device); void acpi_free_ids(struct acpi_device *device); +int acpi_device_create_modalias(struct acpi_device *acpi_dev, + char *modalias, int size); /* -------------------------------------------------------------------------- Power Resource diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 5e7e991..0d7d49f 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -68,8 +68,8 @@ int acpi_scan_add_handler(struct acpi_scan_handler *handler) * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get: * char *modalias: "acpi:IBM0001:ACPI0001" */ -static int create_modalias(struct acpi_device *acpi_dev, char *modalias, - int size) +int acpi_device_create_modalias(struct acpi_device *acpi_dev, + char *modalias, int size) { int len; int count; @@ -99,7 +99,7 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha int len; /* Device has no HID and no CID or string is >1024 */ - len = create_modalias(acpi_dev, buf, 1024); + len = acpi_device_create_modalias(acpi_dev, buf, 1024); if (len <= 0) return 0; buf[len++] = '\n'; @@ -567,7 +567,7 @@ static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env) if (add_uevent_var(env, "MODALIAS=")) return -ENOMEM; - len = create_modalias(acpi_dev, &env->buf[env->buflen - 1], + len = acpi_device_create_modalias(acpi_dev, &env->buf[env->buflen - 1], sizeof(env->buf) - env->buflen); if (len >= (sizeof(env->buf) - env->buflen)) return -ENOMEM; diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index a400002..17e104e 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2515,6 +2515,19 @@ static ssize_t uart_get_attr_iomem_reg_shift(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", tmp.iomem_reg_shift); } +static ssize_t uart_get_attr_peripheral_type(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int len; + + len = acpi_uart_get_peripheral_type(dev, buf, 1024); + if (len > 0) { + buf[len++] = '\n'; + return len; + } + return snprintf(buf, PAGE_SIZE, "uart\n"); +} + static DEVICE_ATTR(type, S_IRUSR | S_IRGRP, uart_get_attr_type, NULL); static DEVICE_ATTR(line, S_IRUSR | S_IRGRP, uart_get_attr_line, NULL); static DEVICE_ATTR(port, S_IRUSR | S_IRGRP, uart_get_attr_port, NULL); @@ -2528,6 +2541,7 @@ static DEVICE_ATTR(custom_divisor, S_IRUSR | S_IRGRP, uart_get_attr_custom_divis static DEVICE_ATTR(io_type, S_IRUSR | S_IRGRP, uart_get_attr_io_type, NULL); static DEVICE_ATTR(iomem_base, S_IRUSR | S_IRGRP, uart_get_attr_iomem_base, NULL); static DEVICE_ATTR(iomem_reg_shift, S_IRUSR | S_IRGRP, uart_get_attr_iomem_reg_shift, NULL); +static DEVICE_ATTR(peripheral_type, S_IRUSR | S_IRGRP, uart_get_attr_peripheral_type, NULL); static struct attribute *tty_dev_attrs[] = { &dev_attr_type.attr, @@ -2543,6 +2557,7 @@ static struct attribute *tty_dev_attrs[] = { &dev_attr_io_type.attr, &dev_attr_iomem_base.attr, &dev_attr_iomem_reg_shift.attr, + &dev_attr_peripheral_type.attr, NULL, }; diff --git a/include/linux/tty.h b/include/linux/tty.h index 367a9df..723fdd01 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -692,4 +692,15 @@ do { \ } while (0) +#ifdef CONFIG_ACPI_UART +int acpi_uart_get_peripheral_type(struct device *dev, + char *buf, size_t size); +#else +static inline int acpi_uart_get_peripheral_type(struct device *dev, + char *buf, size_t size) +{ + return -ENODEV; +} +#endif + #endif -- 1.7.10 -- To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html