[greybus-dev][PATCH 2/3] staging: greybus: Add Greybus netlink driver
Greg KH
gregkh at linuxfoundation.org
Tue Apr 18 12:01:38 UTC 2017
On Sun, Mar 26, 2017 at 06:58:24PM +0200, Alexandre Bailon wrote:
> Currently, the only hd controller supported by Greybus is the es2
> controller which only support is mainly a bridge between USB and UniPro.
> In order to use Greybus on devices that do not support UniPro,
> add a the Greybus netlink hd controller.
>
> By using Generic Netlink, userspace can act as a bridge between Greybus
> and any kind of bus supported by the platform (e.g. Bluetooth).
> In addition, this add an easy way to implement some component such as
> SVC which is required by Greybus though it may not be available on
> every platforms.
>
> Signed-off-by: Alexandre Bailon <abailon at baylibre.com>
> ---
> drivers/staging/greybus/Kconfig | 9 ++
> drivers/staging/greybus/Makefile | 2 +
> drivers/staging/greybus/gb_netlink.h | 37 ++++++
> drivers/staging/greybus/netlink.c | 221 +++++++++++++++++++++++++++++++++++
> 4 files changed, 269 insertions(+)
> create mode 100644 drivers/staging/greybus/gb_netlink.h
> create mode 100644 drivers/staging/greybus/netlink.c
>
> diff --git a/drivers/staging/greybus/Kconfig b/drivers/staging/greybus/Kconfig
> index 50de2d7..f9f3526 100644
> --- a/drivers/staging/greybus/Kconfig
> +++ b/drivers/staging/greybus/Kconfig
> @@ -27,6 +27,15 @@ config GREYBUS_ES2
> To compile this code as a module, chose M here: the module
> will be called gb-es2.ko
>
> +config GREYBUS_NETLINK
> + tristate "Greybus netlink host controller"
> + ---help---
> + Select this option if you want to implement a Greybus
> + "host controller" in userspace.
> +
> + To compile this code as a module, chose M here: the module
> + will be called gb-netlink.ko
> +
> config GREYBUS_AUDIO
> tristate "Greybus Audio Class driver"
> depends on SOUND
> diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile
> index b26b9a3..d057f1d 100644
> --- a/drivers/staging/greybus/Makefile
> +++ b/drivers/staging/greybus/Makefile
> @@ -20,8 +20,10 @@ ccflags-y += -I$(src)
>
> # Greybus Host controller drivers
> gb-es2-y := es2.o
> +gb-netlink-y := netlink.o
>
> obj-$(CONFIG_GREYBUS_ES2) += gb-es2.o
> +obj-$(CONFIG_GREYBUS_NETLINK) += gb-netlink.o
>
> # Greybus class drivers
> gb-bootrom-y := bootrom.o
> diff --git a/drivers/staging/greybus/gb_netlink.h b/drivers/staging/greybus/gb_netlink.h
> new file mode 100644
> index 0000000..4af6fe5
> --- /dev/null
> +++ b/drivers/staging/greybus/gb_netlink.h
> @@ -0,0 +1,37 @@
> +/*
> + * Greybus Netlink driver for userspace controller
> + *
> + * Copyright (c) 2017 BayLibre SAS
> + *
> + * Released under the GPLv2 only.
> + */
> +
> +#ifndef __GB_NETLINK_H
> +#define __GB_NETLINK_H
> +
> +/* Maximum packet size */
> +#define GB_NETLINK_MTU 2048
> +/* Maximum number of Cports */
> +#define GB_NETLINK_NUM_CPORT 32
> +
> +#define GB_NL_NAME "GREYBUS"
> +#define GB_NL_PID 1
> +
> +enum {
> + GB_NL_A_UNSPEC,
> + GB_NL_A_DATA,
> + GB_NL_A_CPORT,
> + __GB_NL_A_MAX,
> +};
> +
> +#define GB_NL_A_MAX (__GB_NL_A_MAX - 1)
> +
> +enum {
> + GB_NL_C_UNSPEC,
> + GB_NL_C_MSG,
> + __GB_NL_C_MAX,
> +};
> +
> +#define GB_NL_C_MAX (__GB_NL_C_MAX - 1)
> +
> +#endif /* __GB_NETLINK_H */
> diff --git a/drivers/staging/greybus/netlink.c b/drivers/staging/greybus/netlink.c
> new file mode 100644
> index 0000000..84f3018
> --- /dev/null
> +++ b/drivers/staging/greybus/netlink.c
> @@ -0,0 +1,221 @@
> +/*
> + * Greybus Netlink driver for userspace controller
> + *
> + * Copyright (c) 2017 BayLibre SAS
> + *
> + * Released under the GPLv2 only.
> + */
> +
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <net/genetlink.h>
> +
> +#include "greybus.h"
> +#include "gb_netlink.h"
> +
> +static dev_t major_dev;
> +static struct class *gb_nl_class;
> +static struct genl_family gb_nl_family;
> +static struct gb_host_device *gb_nl_hd;
> +
> +#define VERSION_NR 1
> +
> +#define DEVICE_NAME "gb_netlink"
> +#define CLASS_NAME "gb_netlink"
> +
> +static int gb_netlink_msg(struct sk_buff *skb, struct genl_info *info)
> +{
> + struct nlattr *na;
> + u16 cport_id;
> + void *data;
> +
> + if (!info)
> + return -EPROTO;
> +
> + na = info->attrs[GB_NL_A_CPORT];
> + if (!na) {
> + dev_err(&gb_nl_hd->dev,
> + "Received message without cport id attribute\n");
> + return -EPROTO;
> + }
> +
> + cport_id = nla_get_u32(na);
> + if (!cport_id_valid(gb_nl_hd, cport_id)) {
> + dev_err(&gb_nl_hd->dev, "invalid cport id %u received",
> + cport_id);
> + return -EINVAL;
> + }
> +
> + na = info->attrs[GB_NL_A_DATA];
> + if (!na) {
> + dev_err(&gb_nl_hd->dev,
> + "Received message without data attribute\n");
> + return -EPROTO;
> + }
> +
> + data = nla_data(na);
> + if (!data) {
> + dev_err(&gb_nl_hd->dev,
> + "Received message without data\n");
> + return -EINVAL;
> + }
> +
> + greybus_data_rcvd(gb_nl_hd, cport_id, data, nla_len(na));
> +
> + return 0;
> +}
> +
> +static struct nla_policy gb_nl_policy[GB_NL_A_MAX + 1] = {
> + [GB_NL_A_DATA] = { .type = NLA_BINARY, .len = GB_NETLINK_MTU },
> + [GB_NL_A_CPORT] = { .type = NLA_U16},
> +};
> +
> +static struct genl_ops gb_nl_ops[] = {
> + {
> + .cmd = GB_NL_C_MSG,
> + .policy = gb_nl_policy,
> + .doit = gb_netlink_msg,
> + },
> +};
> +
> +static struct genl_family gb_nl_family = {
> + .hdrsize = 0,
> + .name = GB_NL_NAME,
> + .version = VERSION_NR,
> + .maxattr = GB_NL_A_MAX,
> + .ops = gb_nl_ops,
> + .n_ops = ARRAY_SIZE(gb_nl_ops),
> +};
> +
> +static int message_send(struct gb_host_device *hd, u16 cport_id,
> + struct gb_message *message, gfp_t gfp_mask)
> +{
> + struct nl_msg *nl_msg;
> + struct sk_buff *skb;
> + int retval = -ENOMEM;
> +
> + skb = genlmsg_new(sizeof(*message->header) + sizeof(u32) +
> + message->payload_size, GFP_KERNEL);
> + if (!skb)
> + goto err_out;
> +
> + nl_msg = genlmsg_put(skb, GB_NL_PID, 0,
> + &gb_nl_family, 0, GB_NL_C_MSG);
> + if (!nl_msg)
> + goto err_free;
> +
> + retval = nla_put_u32(skb, GB_NL_A_CPORT, cport_id);
> + if (retval)
> + goto err_cancel;
> +
> + retval = nla_put(skb, GB_NL_A_DATA,
> + sizeof(*message->header) + message->payload_size,
> + message->header);
> + if (retval)
> + goto err_cancel;
> +
> + genlmsg_end(skb, nl_msg);
> +
> + retval = genlmsg_unicast(&init_net, skb, GB_NL_PID);
> + if (retval)
> + goto err_cancel;
> +
> + greybus_message_sent(hd, message, 0);
> +
> + return 0;
> +
> +err_cancel:
> + genlmsg_cancel(skb, nl_msg);
> +err_free:
> + nlmsg_free(skb);
> +err_out:
> + return retval;
> +}
> +
> +static void message_cancel(struct gb_message *message)
> +{
> +}
> +
> +static struct gb_hd_driver tcpip_driver = {
> + .message_send = message_send,
> + .message_cancel = message_cancel,
> +};
> +
> +static void __exit gb_netlink_exit(void)
> +{
> + if (!gb_nl_hd)
> + return;
> +
> + gb_hd_del(gb_nl_hd);
> + gb_hd_put(gb_nl_hd);
> +
> + gb_nl_hd = NULL;
> +
> + unregister_chrdev_region(major_dev, 1);
> + device_destroy(gb_nl_class, major_dev);
> + class_destroy(gb_nl_class);
> +
> + genl_unregister_family(&gb_nl_family);
> +}
> +
> +static int __init gb_netlink_init(void)
> +{
> + int retval;
> + struct device *dev;
> + struct gb_host_device *gb_nl_hd;
> +
> + retval = genl_register_family(&gb_nl_family);
> + if (retval)
> + return retval;
> +
> + retval = alloc_chrdev_region(&major_dev, 0, 1, DEVICE_NAME);
> + if (retval)
> + goto err_genl_unregister;
> +
> + gb_nl_class = class_create(THIS_MODULE, CLASS_NAME);
> + if (IS_ERR(gb_nl_class)) {
> + retval = PTR_ERR(gb_nl_class);
> + goto err_chrdev_unregister;
> + }
> +
> + dev = device_create(gb_nl_class, NULL, major_dev, NULL, DEVICE_NAME);
> + if (IS_ERR(dev)) {
> + retval = PTR_ERR(dev);
> + goto err_class_destroy;
> + }
What do you do with this character device? Can you just use a misc
device instead?
thanks,
greg k-h
More information about the devel
mailing list