Re: [PATCH RFC v2 1/3] tty: serial core: provide method to search uart by phandle

From: Sergei Zviagintsev
Date: Sun Jun 28 2015 - 17:34:56 EST


Hi,

Some comments below.

On Sun, Jun 28, 2015 at 09:46:24PM +0200, Marek Belisko wrote:
> From: "H. Nikolaus Schaller" <hns@xxxxxxxxxxxxx>
>
> 1. add registered uart_ports to a search list
> 2. provide a function to search an uart_port by phandle. This copies the
> mechanism how devm_usb_get_phy_by_phandle() works
>
> Signed-off-by: H. Nikolaus Schaller <hns@xxxxxxxxxxxxx>
> Signed-off-by: Marek Belisko <marek@xxxxxxxxxxxxx>
> ---
> Documentation/serial/slaves.txt | 36 ++++++++++++++
> drivers/tty/serial/serial_core.c | 103 +++++++++++++++++++++++++++++++++++++++
> include/linux/serial_core.h | 10 ++++
> 3 files changed, 149 insertions(+)
> create mode 100644 Documentation/serial/slaves.txt
>
> diff --git a/Documentation/serial/slaves.txt b/Documentation/serial/slaves.txt
> new file mode 100644
> index 0000000..6f8d44d
> --- /dev/null
> +++ b/Documentation/serial/slaves.txt
> @@ -0,0 +1,36 @@
> +UART slave device support
> +
> +A remote device connected to a RS232 interface is usually power controlled by the DTR line.
> +The DTR line is managed automatically by the UART driver for open() and close() syscalls
> +and on demand by tcsetattr().
> +
> +With embedded devices, the serial peripheral might be directly and always connected to the UART
> +and there might be no physical DTR line involved. Power control (on/off) has to be done by some
> +chip specific device driver (which we call "UART slave") through some mechanisms (I2C, GPIOs etc.)
> +not related to the serial interface. Some devices do not explicitly tell their power state except
> +by sending or not sending data to the UART. In such a case the device driver must be able to monitor
> +data activity. The role of the device driver is to encapsulate such power control in a single place.
> +
> +This patch series allows to support such drivers by providing:
> +* a mechanism that a slave driver can identify the UART instance it is connected to
> +* a mechanism that UART slave drivers can register to be notified
> +* notfications for DTR (and other modem control) state changes
> +* notifications that the UART has received some data from the UART
> +
> +A slave device simply adds a phandle reference to the UART it is connected to, e.g.
> +
> + gps {
> + compatible = "wi2wi,w2sg0004";
> + uart = <&uart1>;
> + };
> +
> +The slave driver calls devm_serial_get_uart_by_phandle() to identify the uart driver.
> +This API follows the concept of devm_usb_get_phy_by_phandle().
> +
> +A slave device driver registers itself with serial_register_slave() to receive notifications.
> +Notification handler callbacks can be registered by serial_register_mctrl_notification() and
> +serial_register_rx_notification(). If an UART has registered a NULL slave or a NULL handler,
> +no notifications are sent.
> +
> +RX notification handlers can define a ktermios during setup and the handler function can modify
> +or decide to throw away each character that is passed upwards.
> diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
> index eec067d..ad61441 100644
> --- a/drivers/tty/serial/serial_core.c
> +++ b/drivers/tty/serial/serial_core.c
> @@ -38,6 +38,33 @@
> #include <asm/irq.h>
> #include <asm/uaccess.h>
>
> +static LIST_HEAD(uart_list);
> +static DEFINE_SPINLOCK(uart_lock);
> +
> +/* same concept as __of_usb_find_phy */
> +static struct uart_port *__of_serial_find_uart(struct device_node *node)
> +{
> + struct uart_port *uart;
> +
> + if (!of_device_is_available(node))
> + return ERR_PTR(-ENODEV);
> +
> + list_for_each_entry(uart, &uart_list, head) {
> + if (node != uart->dev->of_node)
> + continue;
> +
> + return uart;

We can easily save three lines here :)

> + }
> +
> + return ERR_PTR(-EPROBE_DEFER);
> +}
> +
> +static void devm_serial_uart_release(struct device *dev, void *res)
> +{
> + struct uart_port *uart = *(struct uart_port **)res;
> + /* FIXME: serial_put_uart(uart); */
> +}

Looks unfinished...

> +
> /*
> * This is used to lock changes in serial line configuration.
> */
> @@ -64,6 +91,78 @@ static int uart_dcd_enabled(struct uart_port *uport)
> return !!(uport->status & UPSTAT_DCD_ENABLE);
> }
>
> +/**
> + * devm_serial_get_uart_by_phandle - find the uart by phandle
> + * @dev - device that requests this uart
> + * @phandle - name of the property holding the uart phandle value
> + * @index - the index of the uart
> + *
> + * Returns the uart_port associated with the given phandle value,
> + * after getting a refcount to it, -ENODEV if there is no such uart or
> + * -EPROBE_DEFER if there is a phandle to the uart, but the device is
> + * not yet loaded. While at that, it also associates the device with
> + * the uart using devres. On driver detach, release function is invoked
> + * on the devres data, then, devres data is freed.

Add -ENOMEM and -EINVAL, remove -EPROBE_DEFER?

> + *
> + * For use by tty host and peripheral drivers.
> + */
> +
> +/* same concept as devm_usb_get_phy_by_phandle() */
> +
> +struct uart_port *devm_serial_get_uart_by_phandle(struct device *dev,
> + const char *phandle, u8 index)
> +{
> + struct uart_port *uart = ERR_PTR(-ENOMEM), **ptr;
> + unsigned long flags;
> + struct device_node *node;
> +
> + if (!dev->of_node) {
> + dev_err(dev, "device does not have a device node entry\n");
> + return ERR_PTR(-EINVAL);
> + }
> +
> + node = of_parse_phandle(dev->of_node, phandle, index);
> + if (!node) {
> + dev_err(dev, "failed to get %s phandle in %s node\n", phandle,
> + dev->of_node->full_name);
> + return ERR_PTR(-ENODEV);

Indentation issue.

> + }
> +
> + ptr = devres_alloc(devm_serial_uart_release, sizeof(*ptr), GFP_KERNEL);
> + if (!ptr) {
> + dev_err(dev, "failed to allocate memory for devres\n");
> + goto err0;
> + }
> +
> + spin_lock_irqsave(&uart_lock, flags);
> +
> + uart = __of_serial_find_uart(node);
> + if (IS_ERR(uart)) {
> + devres_free(ptr);

I would rather create another goto label, say `out_devres_free' and
checked uart for error there to stay similar across the function, but
that's under question...

> + goto err1;
> + }
> +
> + if (!try_module_get(uart->dev->driver->owner)) {
> + uart = ERR_PTR(-ENODEV);
> + devres_free(ptr);
> + goto err1;
> + }
> +
> + *ptr = uart;
> + devres_add(dev, ptr);

What is the point of assigning value to *ptr?

> +
> + get_device(uart->dev);
> +
> +err1:

Naming of labels is against CodingStyle.

> + spin_unlock_irqrestore(&uart_lock, flags);
> +
> +err0:
> + of_node_put(node);
> +
> + return uart;
> +}
> +EXPORT_SYMBOL_GPL(devm_serial_get_uart_by_phandle);
> +
> /*
> * This routine is used by the interrupt handler to schedule processing in
> * the software interrupt portion of the driver.
> @@ -2727,6 +2826,8 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
> */
> uport->flags &= ~UPF_DEAD;
>
> + list_add_tail(&uport->head, &uart_list);
> +
> out:
> mutex_unlock(&port->mutex);
> mutex_unlock(&port_mutex);
> @@ -2758,6 +2859,8 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
>
> mutex_lock(&port_mutex);
>
> + list_del(&uport->head);
> +
> /*
> * Mark the port "dead" - this prevents any opens from
> * succeeding while we shut down the port.
> diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
> index 297d4fa..ba23718 100644
> --- a/include/linux/serial_core.h
> +++ b/include/linux/serial_core.h
> @@ -247,6 +247,7 @@ struct uart_port {
> const struct attribute_group **tty_groups; /* all attributes (serial core use only) */
> struct serial_rs485 rs485;
> void *private_data; /* generic platform data pointer */
> + struct list_head head; /* list of uarts e.g. to look up by phandle */
> };
>
> static inline int serial_port_in(struct uart_port *up, int offset)
> @@ -475,4 +476,13 @@ static inline int uart_handle_break(struct uart_port *port)
> (cflag) & CRTSCTS || \
> !((cflag) & CLOCAL))
>
> +/*
> + * Helper functions for UART slave drivers
> + */
> +
> +/* find UART by phandle (e.g. with 'uart = <&uart2>;' then call as
> + * devm_serial_get_uart_by_phandle(dev, "uart", 0);
> + */
> +extern struct uart_port *devm_serial_get_uart_by_phandle(struct device *dev,
> + const char *phandle, u8 index);
> #endif /* LINUX_SERIAL_CORE_H */
> --
> 1.9.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/