From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.9 required=3.0 tests=DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED,DKIM_VALID,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id E3A6DC43219 for ; Fri, 3 May 2019 22:45:16 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id ADD20206DF for ; Fri, 3 May 2019 22:45:16 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="AoJ4pJXp"; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ENuMxHM4" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org ADD20206DF Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+infradead-linux-arm-kernel=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=rqlwGLUcm0JJpEl4exBA3GHEwMyeoVR+l/vcbi+b7Yw=; b=AoJ4pJXpiIp1Oo92CahsIXvyhT GbG2zQfE2w+zt9GIiDsnqKYLp/2pvc0yIuEjVkYlQ4gRMw6xlq/sLWGGk6ozlPf0LvBzThbJRFaIw 4m7YWglbzd9W0Btb2LLt7uAapeLQFzWJHKLmPP+ICjNEIIJ/jcO5pMjDXBZJMpagP8YNX4tMUMHS6 jjiBt/uluNHdVxP9mmd32aypFlnFliNphowLN9E6M0UUr6lGcZrd/d8yItUMA4+9uky3c6KJfiN5k I8lfW3lCTfyX2TChf5iPFADAByFJB+UzW75L2UqMEijX0jdWJCSecYhgjqiala/2mRvxBAyRMTG6P FvrXtXfA==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1hMgvW-0001Fr-09; Fri, 03 May 2019 22:45:10 +0000 Received: from mail-pf1-x441.google.com ([2607:f8b0:4864:20::441]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1hMguA-0007l0-KY for linux-arm-kernel@lists.infradead.org; Fri, 03 May 2019 22:44:04 +0000 Received: by mail-pf1-x441.google.com with SMTP id 13so3267426pfw.9 for ; Fri, 03 May 2019 15:43:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=VKqCRHeg0fsFK9356/6v/Yj2MnA+Oi3b8lylOORxmpw=; b=ENuMxHM4QqqZfWKFYs/am9EDJJ03PNz9L8tOmZkupwN84bHtDHFVqqNNOr7z6vGAoK lihb6oABfEU7sF4tfsJFg+LsVuzTINDx1QQL5/gD5n6/J2rteMexyy/W5cRkmvspNK8i /wF7h5fNEu6uypk3GG+tCoaULmSbxBrm70ZZ09h2RHgIWsz/b0Jy05/msXUCHiowoPlZ EOzuvWyOS5jgPHIai+5FUZ3BZu10VXo8kFyGQ2g8BKvC8llk711MINaFe7PApEMb3lDi uclGNG6cxuCRgN9gRCEx3fJ7lk41FE7yf6x4K5M3gT8lCceCccwap9Qa0SM7SF+7q5L9 vZdA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=VKqCRHeg0fsFK9356/6v/Yj2MnA+Oi3b8lylOORxmpw=; b=cb+OfpSvhMmnhh5SW6ex5JQ5z18o7AybPy2Za7mMuDcA6kcpatzA4YIGMLA+icTFqm wh+9N39+6Vs/9UQQ0rnjgeRB8N1IWM3QCNCjOH30+/JNp1jnazCGsUQb5W7qoXculYHo lkPGSU+XffMrzTERi6zkjSW/bva3htuW9zKOBVG1ii5yBnDLDj2F6NKFOZ4uhBtB053C B6GOuk41sO00YWYoQtuQL5meAZzPJiKRDjQWWaL5crODbTByJXa0cqgUs8HIYB31u9ba Lrr1+8qq1buPnQIcbFgfTWDaU7gxLZHe6Od8GdjR6WG0uvhXJ9KdUWZug9iQQwR/5osL Y/pg== X-Gm-Message-State: APjAAAVAkBUHCpSxWg6HbK3PqULWMfXOnJEAlsVYjIKot8kfQB9gLARj mG4Da/KMJzZkR3BXqMFptFw= X-Google-Smtp-Source: APXvYqxINvZy7aag6peSoSGTgXhhz66R+B7RgUyns33buE9EjtdvG5h8HIUJKEuieCYba+70sd5klg== X-Received: by 2002:a63:161d:: with SMTP id w29mr14096649pgl.395.1556923425287; Fri, 03 May 2019 15:43:45 -0700 (PDT) Received: from majic.sklembedded.com (c-73-202-231-77.hsd1.ca.comcast.net. [73.202.231.77]) by smtp.googlemail.com with ESMTPSA id e62sm4793871pfa.50.2019.05.03.15.43.43 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 03 May 2019 15:43:44 -0700 (PDT) From: Steve Longerbeam To: linux-media@vger.kernel.org Subject: [PATCH v4 6/8] media: staging/imx: Re-organize modules Date: Fri, 3 May 2019 15:43:24 -0700 Message-Id: <20190503224326.21039-7-slongerbeam@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190503224326.21039-1-slongerbeam@gmail.com> References: <20190503224326.21039-1-slongerbeam@gmail.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190503_154347_025957_35FB1027 X-CRM114-Status: GOOD ( 24.01 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "open list:STAGING SUBSYSTEM" , Fabio Estevam , Pengutronix Kernel Team , Greg Kroah-Hartman , Sascha Hauer , open list , Rui Miguel Silva , NXP Linux Team , Philipp Zabel , Steve Longerbeam , Mauro Carvalho Chehab , Shawn Guo , "moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE" MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+infradead-linux-arm-kernel=archiver.kernel.org@lists.infradead.org Re-organize modules, and which objects are linked into those modules, so that: - imx6-media (renamed from imx-media) is the media driver module for imx5/6 only, and has no symbol exports. - imx6-media-csi (renamed from imx-media-csi) is the subdev driver module for imx5/6 CSI. It is now linked direcly with imx-media-fim, since only the imx5/6 CSI makes use of the frame interval monitor. - imx-media-common now only contains common code between imx5/6 and imx7 media drivers. It contains imx-media-utils, imx-media-of, imx-media-dev-common, and imx-media-capture. In order to acheive that, some functions common to imx5/6 and imx7 have been moved out of imx-media-dev.c and into imx-media-dev-common.c. Signed-off-by: Steve Longerbeam --- drivers/staging/media/imx/Makefile | 14 +- .../staging/media/imx/imx-media-dev-common.c | 345 +++++++++++++++++- drivers/staging/media/imx/imx-media-dev.c | 340 +---------------- drivers/staging/media/imx/imx-media-fim.c | 5 - drivers/staging/media/imx/imx-media-of.c | 3 + drivers/staging/media/imx/imx-media.h | 16 +- drivers/staging/media/imx/imx7-media-csi.c | 4 +- 7 files changed, 369 insertions(+), 358 deletions(-) diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile index 86f0c81b6a3b..aa6c4b4ad37e 100644 --- a/drivers/staging/media/imx/Makefile +++ b/drivers/staging/media/imx/Makefile @@ -1,14 +1,16 @@ # SPDX-License-Identifier: GPL-2.0 -imx-media-objs := imx-media-dev.o imx-media-internal-sd.o imx-media-of.o \ +imx6-media-objs := imx-media-dev.o imx-media-internal-sd.o \ imx-ic-common.o imx-ic-prp.o imx-ic-prpencvf.o imx-media-vdic.o -imx-media-objs += imx-media-dev-common.o -imx-media-common-objs := imx-media-utils.o imx-media-fim.o -obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media.o +imx-media-common-objs := imx-media-capture.o imx-media-dev-common.o \ + imx-media-of.o imx-media-utils.o + +imx6-media-csi-objs := imx-media-csi.o imx-media-fim.o + +obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx6-media.o obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media-common.o -obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media-capture.o -obj-$(CONFIG_VIDEO_IMX_CSI) += imx-media-csi.o +obj-$(CONFIG_VIDEO_IMX_CSI) += imx6-media-csi.o obj-$(CONFIG_VIDEO_IMX_CSI) += imx6-mipi-csi2.o obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-media-csi.o diff --git a/drivers/staging/media/imx/imx-media-dev-common.c b/drivers/staging/media/imx/imx-media-dev-common.c index 6cd93419b81d..89dc4ec8dadb 100644 --- a/drivers/staging/media/imx/imx-media-dev-common.c +++ b/drivers/staging/media/imx/imx-media-dev-common.c @@ -8,9 +8,342 @@ #include #include +#include +#include +#include +#include #include "imx-media.h" -static const struct v4l2_async_notifier_operations imx_media_subdev_ops = { +static inline struct imx_media_dev *notifier2dev(struct v4l2_async_notifier *n) +{ + return container_of(n, struct imx_media_dev, notifier); +} + +/* async subdev bound notifier */ +static int imx_media_subdev_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) +{ + v4l2_info(sd->v4l2_dev, "subdev %s bound\n", sd->name); + + return 0; +} + +/* + * Create the media links for all subdevs that registered. + * Called after all async subdevs have bound. + */ +static int imx_media_create_links(struct v4l2_async_notifier *notifier) +{ + struct imx_media_dev *imxmd = notifier2dev(notifier); + struct v4l2_subdev *sd; + + list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) { + switch (sd->grp_id) { + case IMX_MEDIA_GRP_ID_IPU_VDIC: + case IMX_MEDIA_GRP_ID_IPU_IC_PRP: + case IMX_MEDIA_GRP_ID_IPU_IC_PRPENC: + case IMX_MEDIA_GRP_ID_IPU_IC_PRPVF: + /* + * links have already been created for the + * sync-registered subdevs. + */ + break; + case IMX_MEDIA_GRP_ID_IPU_CSI0: + case IMX_MEDIA_GRP_ID_IPU_CSI1: + case IMX_MEDIA_GRP_ID_CSI: + imx_media_create_csi_of_links(imxmd, sd); + break; + default: + /* + * if this subdev has fwnode links, create media + * links for them. + */ + imx_media_create_of_links(imxmd, sd); + break; + } + } + + return 0; +} + +/* + * adds given video device to given imx-media source pad vdev list. + * Continues upstream from the pad entity's sink pads. + */ +static int imx_media_add_vdev_to_pad(struct imx_media_dev *imxmd, + struct imx_media_video_dev *vdev, + struct media_pad *srcpad) +{ + struct media_entity *entity = srcpad->entity; + struct imx_media_pad_vdev *pad_vdev; + struct list_head *pad_vdev_list; + struct media_link *link; + struct v4l2_subdev *sd; + int i, ret; + + /* skip this entity if not a v4l2_subdev */ + if (!is_media_entity_v4l2_subdev(entity)) + return 0; + + sd = media_entity_to_v4l2_subdev(entity); + + pad_vdev_list = to_pad_vdev_list(sd, srcpad->index); + if (!pad_vdev_list) { + v4l2_warn(&imxmd->v4l2_dev, "%s:%u has no vdev list!\n", + entity->name, srcpad->index); + /* + * shouldn't happen, but no reason to fail driver load, + * just skip this entity. + */ + return 0; + } + + /* just return if we've been here before */ + list_for_each_entry(pad_vdev, pad_vdev_list, list) { + if (pad_vdev->vdev == vdev) + return 0; + } + + dev_dbg(imxmd->md.dev, "adding %s to pad %s:%u\n", + vdev->vfd->entity.name, entity->name, srcpad->index); + + pad_vdev = devm_kzalloc(imxmd->md.dev, sizeof(*pad_vdev), GFP_KERNEL); + if (!pad_vdev) + return -ENOMEM; + + /* attach this vdev to this pad */ + pad_vdev->vdev = vdev; + list_add_tail(&pad_vdev->list, pad_vdev_list); + + /* move upstream from this entity's sink pads */ + for (i = 0; i < entity->num_pads; i++) { + struct media_pad *pad = &entity->pads[i]; + + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + continue; + + list_for_each_entry(link, &entity->links, list) { + if (link->sink != pad) + continue; + ret = imx_media_add_vdev_to_pad(imxmd, vdev, + link->source); + if (ret) + return ret; + } + } + + return 0; +} + +/* + * For every subdevice, allocate an array of list_head's, one list_head + * for each pad, to hold the list of video devices reachable from that + * pad. + */ +static int imx_media_alloc_pad_vdev_lists(struct imx_media_dev *imxmd) +{ + struct list_head *vdev_lists; + struct media_entity *entity; + struct v4l2_subdev *sd; + int i; + + list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) { + entity = &sd->entity; + vdev_lists = devm_kcalloc(imxmd->md.dev, + entity->num_pads, sizeof(*vdev_lists), + GFP_KERNEL); + if (!vdev_lists) + return -ENOMEM; + + /* attach to the subdev's host private pointer */ + sd->host_priv = vdev_lists; + + for (i = 0; i < entity->num_pads; i++) + INIT_LIST_HEAD(to_pad_vdev_list(sd, i)); + } + + return 0; +} + +/* form the vdev lists in all imx-media source pads */ +static int imx_media_create_pad_vdev_lists(struct imx_media_dev *imxmd) +{ + struct imx_media_video_dev *vdev; + struct media_link *link; + int ret; + + ret = imx_media_alloc_pad_vdev_lists(imxmd); + if (ret) + return ret; + + list_for_each_entry(vdev, &imxmd->vdev_list, list) { + link = list_first_entry(&vdev->vfd->entity.links, + struct media_link, list); + ret = imx_media_add_vdev_to_pad(imxmd, vdev, link->source); + if (ret) + return ret; + } + + return 0; +} + +/* async subdev complete notifier */ +int imx_media_probe_complete(struct v4l2_async_notifier *notifier) +{ + struct imx_media_dev *imxmd = notifier2dev(notifier); + int ret; + + mutex_lock(&imxmd->mutex); + + ret = imx_media_create_links(notifier); + if (ret) + goto unlock; + + ret = imx_media_create_pad_vdev_lists(imxmd); + if (ret) + goto unlock; + + ret = v4l2_device_register_subdev_nodes(&imxmd->v4l2_dev); +unlock: + mutex_unlock(&imxmd->mutex); + if (ret) + return ret; + + return media_device_register(&imxmd->md); +} +EXPORT_SYMBOL_GPL(imx_media_probe_complete); + +/* + * adds controls to a video device from an entity subdevice. + * Continues upstream from the entity's sink pads. + */ +static int imx_media_inherit_controls(struct imx_media_dev *imxmd, + struct video_device *vfd, + struct media_entity *entity) +{ + int i, ret = 0; + + if (is_media_entity_v4l2_subdev(entity)) { + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + + dev_dbg(imxmd->md.dev, + "adding controls to %s from %s\n", + vfd->entity.name, sd->entity.name); + + ret = v4l2_ctrl_add_handler(vfd->ctrl_handler, + sd->ctrl_handler, + NULL, true); + if (ret) + return ret; + } + + /* move upstream */ + for (i = 0; i < entity->num_pads; i++) { + struct media_pad *pad, *spad = &entity->pads[i]; + + if (!(spad->flags & MEDIA_PAD_FL_SINK)) + continue; + + pad = media_entity_remote_pad(spad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + continue; + + ret = imx_media_inherit_controls(imxmd, vfd, pad->entity); + if (ret) + break; + } + + return ret; +} + +static int imx_media_link_notify(struct media_link *link, u32 flags, + unsigned int notification) +{ + struct media_entity *source = link->source->entity; + struct imx_media_pad_vdev *pad_vdev; + struct list_head *pad_vdev_list; + struct imx_media_dev *imxmd; + struct video_device *vfd; + struct v4l2_subdev *sd; + int pad_idx, ret; + + ret = v4l2_pipeline_link_notify(link, flags, notification); + if (ret) + return ret; + + /* don't bother if source is not a subdev */ + if (!is_media_entity_v4l2_subdev(source)) + return 0; + + sd = media_entity_to_v4l2_subdev(source); + pad_idx = link->source->index; + + imxmd = dev_get_drvdata(sd->v4l2_dev->dev); + + pad_vdev_list = to_pad_vdev_list(sd, pad_idx); + if (!pad_vdev_list) { + /* nothing to do if source sd has no pad vdev list */ + return 0; + } + + /* + * Before disabling a link, reset controls for all video + * devices reachable from this link. + * + * After enabling a link, refresh controls for all video + * devices reachable from this link. + */ + if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH && + !(flags & MEDIA_LNK_FL_ENABLED)) { + list_for_each_entry(pad_vdev, pad_vdev_list, list) { + vfd = pad_vdev->vdev->vfd; + dev_dbg(imxmd->md.dev, + "reset controls for %s\n", + vfd->entity.name); + v4l2_ctrl_handler_free(vfd->ctrl_handler); + v4l2_ctrl_handler_init(vfd->ctrl_handler, 0); + } + } else if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && + (link->flags & MEDIA_LNK_FL_ENABLED)) { + list_for_each_entry(pad_vdev, pad_vdev_list, list) { + vfd = pad_vdev->vdev->vfd; + dev_dbg(imxmd->md.dev, + "refresh controls for %s\n", + vfd->entity.name); + ret = imx_media_inherit_controls(imxmd, vfd, + &vfd->entity); + if (ret) + break; + } + } + + return ret; +} + +static void imx_media_notify(struct v4l2_subdev *sd, unsigned int notification, + void *arg) +{ + struct media_entity *entity = &sd->entity; + int i; + + if (notification != V4L2_DEVICE_NOTIFY_EVENT) + return; + + for (i = 0; i < entity->num_pads; i++) { + struct media_pad *pad = &entity->pads[i]; + struct imx_media_pad_vdev *pad_vdev; + struct list_head *pad_vdev_list; + + pad_vdev_list = to_pad_vdev_list(sd, pad->index); + if (!pad_vdev_list) + continue; + list_for_each_entry(pad_vdev, pad_vdev_list, list) + v4l2_event_queue(pad_vdev->vdev->vfd, arg); + } +} + +static const struct v4l2_async_notifier_operations imx_media_notifier_ops = { .bound = imx_media_subdev_bound, .complete = imx_media_probe_complete, }; @@ -19,7 +352,8 @@ static const struct media_device_ops imx_media_md_ops = { .link_notify = imx_media_link_notify, }; -struct imx_media_dev *imx_media_dev_init(struct device *dev) +struct imx_media_dev *imx_media_dev_init(struct device *dev, + const struct media_device_ops *ops) { struct imx_media_dev *imxmd; int ret; @@ -31,7 +365,7 @@ struct imx_media_dev *imx_media_dev_init(struct device *dev) dev_set_drvdata(dev, imxmd); strscpy(imxmd->md.model, "imx-media", sizeof(imxmd->md.model)); - imxmd->md.ops = &imx_media_md_ops; + imxmd->md.ops = ops ? ops : &imx_media_md_ops; imxmd->md.dev = dev; mutex_init(&imxmd->mutex); @@ -65,7 +399,8 @@ struct imx_media_dev *imx_media_dev_init(struct device *dev) } EXPORT_SYMBOL_GPL(imx_media_dev_init); -int imx_media_dev_notifier_register(struct imx_media_dev *imxmd) +int imx_media_dev_notifier_register(struct imx_media_dev *imxmd, + const struct v4l2_async_notifier_operations *ops) { int ret; @@ -76,7 +411,7 @@ int imx_media_dev_notifier_register(struct imx_media_dev *imxmd) } /* prepare the async subdev notifier and register it */ - imxmd->notifier.ops = &imx_media_subdev_ops; + imxmd->notifier.ops = ops ? ops : &imx_media_notifier_ops; ret = v4l2_async_notifier_register(&imxmd->v4l2_dev, &imxmd->notifier); if (ret) { diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c index 55181756a940..422afb31f4ca 100644 --- a/drivers/staging/media/imx/imx-media-dev.c +++ b/drivers/staging/media/imx/imx-media-dev.c @@ -1,29 +1,18 @@ /* * V4L2 Media Controller Driver for Freescale i.MX5/6 SOC * - * Copyright (c) 2016 Mentor Graphics Inc. + * Copyright (c) 2016-2019 Mentor Graphics Inc. * * 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; either version 2 of the License, or * (at your option) any later version. */ -#include #include #include -#include -#include -#include #include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include