[{"id":162,"web_url":"https://patchwork.libcamera.org/comment/162/","msgid":"<8cc7c773-dd1a-c5dd-6d76-119f03cc52a7@ideasonboard.com>","date":"2019-01-01T22:06:14","subject":"Re: [libcamera-devel] [PATCH 4/4] libcamera: mediadevice: Improve\n\tdocumentation","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Hi Laurent,\n\nOn 01/01/2019 21:23, Laurent Pinchart wrote:\n> Improve the documentation of the media device operation, including how\n> it handles the lifetime of media objects.\n> \n\nSome potential issues to consider inline.\n\nWith those fixed,\n\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n>  src/libcamera/include/media_device.h |   2 +-\n>  src/libcamera/media_device.cpp       | 113 ++++++++++++++++-----------\n>  2 files changed, 70 insertions(+), 45 deletions(-)\n> \n> diff --git a/src/libcamera/include/media_device.h b/src/libcamera/include/media_device.h\n> index fd1e12d1bbcb..d787be391882 100644\n> --- a/src/libcamera/include/media_device.h\n> +++ b/src/libcamera/include/media_device.h\n> @@ -44,7 +44,7 @@ private:\n>  \n>  \tstd::map<unsigned int, MediaObject *> objects_;\n>  \tMediaObject *object(unsigned int id);\n> -\tbool addObject(MediaObject *obj);\n> +\tbool addObject(MediaObject *object);\n>  \tvoid clear();\n>  \n>  \tstd::vector<MediaEntity *> entities_;\n> diff --git a/src/libcamera/media_device.cpp b/src/libcamera/media_device.cpp\n> index 4c77d3787391..2470c0be806e 100644\n> --- a/src/libcamera/media_device.cpp\n> +++ b/src/libcamera/media_device.cpp\n> @@ -32,37 +32,41 @@ namespace libcamera {\n>   * \\brief The MediaDevice represents a Media Controller device with its full\n>   * graph of connected objects.\n>   *\n> - * Media devices are created with an empty graph, which must be populated from\n> + * A MediaDevice instance is associated with a media controller device node when\n> + * created, and that association is kept for the lifetime of the MediaDevice\n> + * instance.\n>   *\n> + * The instance is created with an empty media graph. Before performing any\n> + * other operation, it must be opened with the open() function and the media\n> + * graph populated by calling populate(). Instances of MediaEntity, MediaPad and\n> + * MediaLink are created to model the media graph, and stored in a map indexed\n> + * by object id.\n>   *\n> - * The caller is responsible for opening the MediaDevice explicitly before\n> - * operating on it, and shall close it when not needed anymore, as access\n> - * to the MediaDevice is exclusive.\n\n> + * Once successfully populated the graph is valid, as reported by the valid()\n\n\"Once successfully populated the graph is valid\" sounds like it needs a\nbreather comma after populated.\n\nAlternatively - re-order as:\n\nThe graph is valid once successfully populated, as reported by the\nvalid() function.\n\n\n\n> + * function. It can be queried to list all entities(), or entities can be\n> + * looked up by name with getEntityByName(). The graph can be traversed from\n> + * entity to entity through pads and links as exposed by the corresponding\n> + * classes.\n>   *\n> - * A MediaDevice is created empty and gets populated by inspecting the media\n> - * graph topology using the MEDIA_IOC_G_TOPOLOGY ioctls. Representation\n> - * of each entity, pad and link described are created using MediaObject\n> - * derived classes.\n> - *\n> - * All MediaObject are stored in a global pool, where they could be retrieved\n> - * from by their globally unique id.\n> - *\n> - * References to MediaEntity registered in the graph are stored in a vector\n> - * to allow easier by-name lookup, and the list of MediaEntities is accessible.\n> + * An open media device will keep an open file handle for the underlying media\n> + * controller device node. It can be closed at any time with a call to close().\n> + * This will not invalidate the media graph and all cached media object remain\n\ns/media object/media objects/\n\n> + * valid and can be accessed normally. The device can then be later reopened if\n> + * needed to perform other operations that interect with the device node.\n\ns/interect/interact/ ?\n\n>   */\n>  \n>  /**\n>   * \\brief Construct a MediaDevice\n>   * \\param devnode The media device node path\n> + *\n> + * Once constructed the media device is invalid, and must be opened and\n> + * populated with open() and populate() before the media graph can be queried.\n>   */\n>  MediaDevice::MediaDevice(const std::string &devnode)\n>  \t: devnode_(devnode), fd_(-1), valid_(false)\n>  {\n>  }\n>  \n> -/**\n> - * \\brief Close the media device file descriptor and delete all object\n> - */\n>  MediaDevice::~MediaDevice()\n>  {\n>  \tif (fd_ != -1)\n> @@ -71,11 +75,18 @@ MediaDevice::~MediaDevice()\n>  }\n>  \n>  /**\n> - * \\brief Open a media device and retrieve informations from it\n> + * \\brief Open a media device and retrieve device information\n>   *\n> - * The function fails if the media device is already open or if either\n> - * open or the media device information retrieval operations fail.\n> - * \\return 0 for success or a negative error number otherwise\n> + * Before populating the media graph or performing any operation that interact\n> + * with the device node associated with the media device, the device node must\n> + * be opened.\n> + *\n> + * This function also retrieves media device information from the device node,\n> + * which can be queried through driver().\n> + *\n> + * If the device is already open the function returns -EBUSY.\n> + *\n> + * \\return 0 on success or a negative error code otherwise\n>   */\n>  int MediaDevice::open()\n>  {\n> @@ -108,10 +119,17 @@ int MediaDevice::open()\n>  }\n>  \n>  /**\n> - * \\brief Close the file descriptor associated with the media device.\n> + * \\brief Close the media device\n> + *\n> + * This function closes the media device node. It does not invalidate the media\n> + * graph and all cached media object remain valid and can be accessed normally.\n\ns/object/objects/\n\n> + * Once closed no operation interacting with the media device node can be\n> + * performed until the device is opened again.\n>   *\n> - * After this function has been called, for the MediaDevice to be operated on,\n> - * the caller shall open it again.\n> + * Closing an already closed device is allowed and will not performed any\n\ns/performed/perform/\n\n> + * operation.\n> + *\n> + * \\sa open()\n>   */\n>  void MediaDevice::close()\n>  {\n> @@ -130,9 +148,9 @@ void MediaDevice::close()\n>   * stored as MediaEntity, MediaPad and MediaLink respectively, with cross-\n>   * references between objects. Interfaces are not processed.\n>   *\n> - * MediaEntities are stored in a global list in the MediaDevice itself to ease\n> - * lookup, while MediaPads are accessible from the MediaEntity they belong\n> - * to only and MediaLinks from the MediaPad they connect.\n> + * Entities are stored in a separate list in the MediaDevice to ease lookup,\n> + * while pads are accessible from the entity they belong to and links from the\n> + * pad they connect.\n\ns/pad/pads/\n\n>   *\n>   * \\return 0 on success, a negative error code otherwise\n>   */\n> @@ -241,8 +259,9 @@ MediaEntity *MediaDevice::getEntityByName(const std::string &name)\n>   * object id.\n>   */\n>  \n> -/*\n> - * MediaObject pool lookup by id.\n> +/**\n> + * \\brief Retrieve the media graph object specified by \\a id\n> + * \\return The graph object, or nullptr if no object with \\a id is found\n>   */\n>  MediaObject *MediaDevice::object(unsigned int id)\n>  {\n> @@ -250,33 +269,40 @@ MediaObject *MediaDevice::object(unsigned int id)\n>  \treturn (it == objects_.end()) ? nullptr : it->second;\n>  }\n>  \n> -/*\n> - * Add a new object to the global objects pool and fail if the object\n> - * has already been registered.\n> +/**\n> + * \\brief Add a media object to the media graph\n> + *\n> + * If the \\a object has a unique id it is added to the media graph, and its\n> + * lifetime will be managed by the media device. Otherwise the object isn't\n> + * added to the graph and the caller must delete it.\n> + *\n> + * \\return true if the object was successfully added to the graph and false\n> + * otherwise\n>   */\n> -bool MediaDevice::addObject(MediaObject *obj)\n> +bool MediaDevice::addObject(MediaObject *object)\n>  {\n>  \n> -\tif (objects_.find(obj->id()) != objects_.end()) {\n> -\t\tLOG(Error) << \"Element with id \" << obj->id()\n> +\tif (objects_.find(object->id()) != objects_.end()) {\n> +\t\tLOG(Error) << \"Element with id \" << object->id()\n>  \t\t\t   << \" already enumerated.\";\n>  \t\treturn false;\n>  \t}\n>  \n> -\tobjects_[obj->id()] = obj;\n> +\tobjects_[object->id()] = object;\n>  \n>  \treturn true;\n>  }\n>  \n>  /**\n> - * \\brief Delete all media objects in the MediaDevice.\n> + * \\brief Delete all graph objects in the media device\n> + *\n> + * Clear the media graph and delete all the objects it contains. After this\n> + * function returns any previously obtained pointer to a media graph object\n> + * becomes invalid.\n>   *\n> - * Delete all MediaEntities; entities will then delete their pads,\n> - * and each source pad will delete links.\n> + * The media device graph state is reset to invalid when the graph is cleared.\n>   *\n> - * After this function has been called, the media graph will be unpopulated\n> - * and its media objects deleted. The media device has to be populated\n> - * before it could be used again.\n> + * \\sa valid()\n\nWhat does \\sa valid() do here?\n\n>   */\n>  void MediaDevice::clear()\n>  {\n> @@ -295,8 +321,7 @@ void MediaDevice::clear()\n>  \n>  /*\n>   * For each entity in the media graph create a MediaEntity and store a\n> - * reference in the MediaObject global pool and in the global vector of\n> - * entities.\n> + * reference in the media device objects map and entities list.\n>   */\n>  bool MediaDevice::populateEntities(const struct media_v2_topology &topology)\n>  {\n>","headers":{"Return-Path":"<kieran.bingham@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id BE25460B31\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  1 Jan 2019 23:06:18 +0100 (CET)","from [192.168.0.21]\n\t(cpc89242-aztw30-2-0-cust488.18-1.cable.virginm.net [86.31.129.233])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 69751505;\n\tTue,  1 Jan 2019 23:06:17 +0100 (CET)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1546380377;\n\tbh=Au3/bhrrW+sq2QbXVRCeuSmBl8Q0hx6UEQIRKwOe+rU=;\n\th=Reply-To:Subject:To:References:From:Date:In-Reply-To:From;\n\tb=vpCsjP9q/pfy+CaezEA2nh+aRa78/9KfiS1rtqaSgbh8JfFGUWR8pYAD7Pp9zMi0U\n\tOJjdXOuzQcrktSwRW1ArIvJVrQTFINlGPC1YgOCR7qAFssKUGHKza6FZ0RdXg+8zdU\n\tdyOH5T1VFNuvsccMbkRc54WnCM1YPYpISOmzpYYM=","Reply-To":"kieran.bingham@ideasonboard.com","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20190101212328.18361-1-laurent.pinchart@ideasonboard.com>\n\t<20190101212328.18361-4-laurent.pinchart@ideasonboard.com>","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Openpgp":"preference=signencrypt","Autocrypt":"addr=kieran.bingham@ideasonboard.com; keydata=\n\t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\n\tsbsRB8KNNvVXBOVNwko86rQqF9drZuw=","Organization":"Ideas on Board","Message-ID":"<8cc7c773-dd1a-c5dd-6d76-119f03cc52a7@ideasonboard.com>","Date":"Tue, 1 Jan 2019 22:06:14 +0000","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101\n\tThunderbird/60.2.1","MIME-Version":"1.0","In-Reply-To":"<20190101212328.18361-4-laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=utf-8","Content-Language":"en-GB","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [PATCH 4/4] libcamera: mediadevice: Improve\n\tdocumentation","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.23","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Tue, 01 Jan 2019 22:06:18 -0000"}},{"id":164,"web_url":"https://patchwork.libcamera.org/comment/164/","msgid":"<1634054.kfvb6Ge3Ek@avalon>","date":"2019-01-02T00:43:41","subject":"Re: [libcamera-devel] [PATCH 4/4] libcamera: mediadevice: Improve\n\tdocumentation","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Kieran,\n\nOn Wednesday, 2 January 2019 00:06:14 EET Kieran Bingham wrote:\n> On 01/01/2019 21:23, Laurent Pinchart wrote:\n> > Improve the documentation of the media device operation, including how\n> > it handles the lifetime of media objects.\n> \n> Some potential issues to consider inline.\n> \n> With those fixed,\n> \n> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> \n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> > \n> >  src/libcamera/include/media_device.h |   2 +-\n> >  src/libcamera/media_device.cpp       | 113 ++++++++++++++++-----------\n> >  2 files changed, 70 insertions(+), 45 deletions(-)\n\n[snip]\n\n> > diff --git a/src/libcamera/media_device.cpp\n> > b/src/libcamera/media_device.cpp index 4c77d3787391..2470c0be806e 100644\n> > --- a/src/libcamera/media_device.cpp\n> > +++ b/src/libcamera/media_device.cpp\n\n[snip]\n\n> >  /**\n> > - * \\brief Delete all media objects in the MediaDevice.\n> > + * \\brief Delete all graph objects in the media device\n> > + *\n> > + * Clear the media graph and delete all the objects it contains. After\n> > this + * function returns any previously obtained pointer to a media\n> > graph object + * becomes invalid.\n> >   *\n> > - * Delete all MediaEntities; entities will then delete their pads,\n> > - * and each source pad will delete links.\n> > + * The media device graph state is reset to invalid when the graph is\n> > cleared.\n> >   *\n> > - * After this function has been called, the media graph will be\n> > unpopulated - * and its media objects deleted. The media device has to be\n> > populated - * before it could be used again.\n> > + * \\sa valid()\n> \n> What does \\sa valid() do here?\n\nIt links to the valid() function (sa stands for see also).\n\n> >   */\n> >  void MediaDevice::clear()\n> >  {","headers":{"Return-Path":"<laurent.pinchart@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id E5965600CC\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  2 Jan 2019 01:42:41 +0100 (CET)","from avalon.localnet (dfj612ybrt5fhg77mgycy-3.rev.dnainternet.fi\n\t[IPv6:2001:14ba:21f5:5b00:2e86:4862:ef6a:2804])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 5B292505;\n\tWed,  2 Jan 2019 01:42:41 +0100 (CET)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1546389761;\n\tbh=OHnFA3RXdephbWy6YAumRnu8VvIK1nzpjw7JA7hrJ2Q=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=i9c2Xjea5SlvlP/eBvDEXt6ZpldVMJgh09lKwapL+q8lr1OgwCnAAwHA4NkXiJcGq\n\tIbNGrm9d6pnb5bxkgGOvBPzQG2LRI4IVpaj+I7PkD+y44zECkgcR8oP8AjtWvaqKV0\n\t/GvzkHDcztnwR4Oab3+CKbOKL70T2uy72E1TNAWo=","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"kieran.bingham@ideasonboard.com","Cc":"libcamera-devel@lists.libcamera.org","Date":"Wed, 02 Jan 2019 02:43:41 +0200","Message-ID":"<1634054.kfvb6Ge3Ek@avalon>","Organization":"Ideas on Board Oy","In-Reply-To":"<8cc7c773-dd1a-c5dd-6d76-119f03cc52a7@ideasonboard.com>","References":"<20190101212328.18361-1-laurent.pinchart@ideasonboard.com>\n\t<20190101212328.18361-4-laurent.pinchart@ideasonboard.com>\n\t<8cc7c773-dd1a-c5dd-6d76-119f03cc52a7@ideasonboard.com>","MIME-Version":"1.0","Content-Transfer-Encoding":"7Bit","Content-Type":"text/plain; charset=\"us-ascii\"","Subject":"Re: [libcamera-devel] [PATCH 4/4] libcamera: mediadevice: Improve\n\tdocumentation","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.23","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Wed, 02 Jan 2019 00:42:42 -0000"}},{"id":170,"web_url":"https://patchwork.libcamera.org/comment/170/","msgid":"<20190102092923.efeptsp4d7ntinqk@uno.localdomain>","date":"2019-01-02T09:29:23","subject":"Re: [libcamera-devel] [PATCH 4/4] libcamera: mediadevice: Improve\n\tdocumentation","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/people/3/","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"content":"Hi Laurent\n   thanks for the patch.\n\nThe documentation is now better, thanks. Writing proper doc is harder\nthan writing proper code :/\n\nOn Tue, Jan 01, 2019 at 11:23:28PM +0200, Laurent Pinchart wrote:\n> Improve the documentation of the media device operation, including how\n> it handles the lifetime of media objects.\n>\n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n>  src/libcamera/include/media_device.h |   2 +-\n>  src/libcamera/media_device.cpp       | 113 ++++++++++++++++-----------\n>  2 files changed, 70 insertions(+), 45 deletions(-)\n>\n> diff --git a/src/libcamera/include/media_device.h b/src/libcamera/include/media_device.h\n> index fd1e12d1bbcb..d787be391882 100644\n> --- a/src/libcamera/include/media_device.h\n> +++ b/src/libcamera/include/media_device.h\n> @@ -44,7 +44,7 @@ private:\n>\n>  \tstd::map<unsigned int, MediaObject *> objects_;\n>  \tMediaObject *object(unsigned int id);\n> -\tbool addObject(MediaObject *obj);\n> +\tbool addObject(MediaObject *object);\n>  \tvoid clear();\n>\n>  \tstd::vector<MediaEntity *> entities_;\n> diff --git a/src/libcamera/media_device.cpp b/src/libcamera/media_device.cpp\n> index 4c77d3787391..2470c0be806e 100644\n> --- a/src/libcamera/media_device.cpp\n> +++ b/src/libcamera/media_device.cpp\n> @@ -32,37 +32,41 @@ namespace libcamera {\n>   * \\brief The MediaDevice represents a Media Controller device with its full\n>   * graph of connected objects.\n>   *\n> - * Media devices are created with an empty graph, which must be populated from\n> + * A MediaDevice instance is associated with a media controller device node when\n> + * created, and that association is kept for the lifetime of the MediaDevice\n> + * instance.\n>   *\n> + * The instance is created with an empty media graph. Before performing any\n> + * other operation, it must be opened with the open() function and the media\n> + * graph populated by calling populate(). Instances of MediaEntity, MediaPad and\n> + * MediaLink are created to model the media graph, and stored in a map indexed\n> + * by object id.\n>   *\n> - * The caller is responsible for opening the MediaDevice explicitly before\n> - * operating on it, and shall close it when not needed anymore, as access\n> - * to the MediaDevice is exclusive.\n> + * Once successfully populated the graph is valid, as reported by the valid()\n> + * function. It can be queried to list all entities(), or entities can be\n> + * looked up by name with getEntityByName(). The graph can be traversed from\n> + * entity to entity through pads and links as exposed by the corresponding\n> + * classes.\n>   *\n> - * A MediaDevice is created empty and gets populated by inspecting the media\n> - * graph topology using the MEDIA_IOC_G_TOPOLOGY ioctls. Representation\n> - * of each entity, pad and link described are created using MediaObject\n> - * derived classes.\n> - *\n> - * All MediaObject are stored in a global pool, where they could be retrieved\n> - * from by their globally unique id.\n> - *\n> - * References to MediaEntity registered in the graph are stored in a vector\n> - * to allow easier by-name lookup, and the list of MediaEntities is accessible.\n> + * An open media device will keep an open file handle for the underlying media\n> + * controller device node. It can be closed at any time with a call to close().\n> + * This will not invalidate the media graph and all cached media object remain\n\ns/cached media object/cached media objects/\n\n> + * valid and can be accessed normally. The device can then be later reopened if\n> + * needed to perform other operations that interect with the device node.\n\ns/interect/interact\n\n\n>   */\n>\n>  /**\n>   * \\brief Construct a MediaDevice\n>   * \\param devnode The media device node path\n> + *\n> + * Once constructed the media device is invalid, and must be opened and\n> + * populated with open() and populate() before the media graph can be queried.\n>   */\n>  MediaDevice::MediaDevice(const std::string &devnode)\n>  \t: devnode_(devnode), fd_(-1), valid_(false)\n>  {\n>  }\n>\n> -/**\n> - * \\brief Close the media device file descriptor and delete all object\n> - */\n>  MediaDevice::~MediaDevice()\n>  {\n>  \tif (fd_ != -1)\n> @@ -71,11 +75,18 @@ MediaDevice::~MediaDevice()\n>  }\n>\n>  /**\n> - * \\brief Open a media device and retrieve informations from it\n> + * \\brief Open a media device and retrieve device information\n>   *\n> - * The function fails if the media device is already open or if either\n> - * open or the media device information retrieval operations fail.\n> - * \\return 0 for success or a negative error number otherwise\n> + * Before populating the media graph or performing any operation that interact\n> + * with the device node associated with the media device, the device node must\n> + * be opened.\n> + *\n> + * This function also retrieves media device information from the device node,\n> + * which can be queried through driver().\n> + *\n> + * If the device is already open the function returns -EBUSY.\n> + *\n> + * \\return 0 on success or a negative error code otherwise\n>   */\n>  int MediaDevice::open()\n>  {\n> @@ -108,10 +119,17 @@ int MediaDevice::open()\n>  }\n>\n>  /**\n> - * \\brief Close the file descriptor associated with the media device.\n> + * \\brief Close the media device\n> + *\n> + * This function closes the media device node. It does not invalidate the media\n> + * graph and all cached media object remain valid and can be accessed normally.\n\ns/cached media object/cached media objects/\n\n> + * Once closed no operation interacting with the media device node can be\n> + * performed until the device is opened again.\n>   *\n> - * After this function has been called, for the MediaDevice to be operated on,\n> - * the caller shall open it again.\n> + * Closing an already closed device is allowed and will not performed any\n> + * operation.\n\ns/perform/performed\n\nEven if I would have said \"Closing an already closed device is a safe non-op\".\n\n> + *\n> + * \\sa open()\n>   */\n>  void MediaDevice::close()\n>  {\n> @@ -130,9 +148,9 @@ void MediaDevice::close()\n>   * stored as MediaEntity, MediaPad and MediaLink respectively, with cross-\n>   * references between objects. Interfaces are not processed.\n>   *\n> - * MediaEntities are stored in a global list in the MediaDevice itself to ease\n> - * lookup, while MediaPads are accessible from the MediaEntity they belong\n> - * to only and MediaLinks from the MediaPad they connect.\n> + * Entities are stored in a separate list in the MediaDevice to ease lookup,\n> + * while pads are accessible from the entity they belong to and links from the\n> + * pad they connect.\n\nWe should establish a rule when to use the type name (eg. MediaEntity)\nand when a generic name (eg Entities) in documentation.\n\n>   *\n>   * \\return 0 on success, a negative error code otherwise\n>   */\n> @@ -241,8 +259,9 @@ MediaEntity *MediaDevice::getEntityByName(const std::string &name)\n>   * object id.\n>   */\n>\n> -/*\n> - * MediaObject pool lookup by id.\n> +/**\n> + * \\brief Retrieve the media graph object specified by \\a id\n> + * \\return The graph object, or nullptr if no object with \\a id is found\n>   */\n\nThis is a private method. Should we doxygen them? I originally\ncommented them without the starting /**\n\nafaict documentation on private methods and fields does not show up in the\ngenerated output even if I'm sure this can be changed.\n\n>  MediaObject *MediaDevice::object(unsigned int id)\n>  {\n> @@ -250,33 +269,40 @@ MediaObject *MediaDevice::object(unsigned int id)\n>  \treturn (it == objects_.end()) ? nullptr : it->second;\n>  }\n>\n> -/*\n> - * Add a new object to the global objects pool and fail if the object\n> - * has already been registered.\n> +/**\n> + * \\brief Add a media object to the media graph\n> + *\n> + * If the \\a object has a unique id it is added to the media graph, and its\n> + * lifetime will be managed by the media device. Otherwise the object isn't\n> + * added to the graph and the caller must delete it.\n> + *\n> + * \\return true if the object was successfully added to the graph and false\n> + * otherwise\n\nSame question as above.\n\nThanks\n  j\n\n\n>   */\n> -bool MediaDevice::addObject(MediaObject *obj)\n> +bool MediaDevice::addObject(MediaObject *object)\n>  {\n>\n> -\tif (objects_.find(obj->id()) != objects_.end()) {\n> -\t\tLOG(Error) << \"Element with id \" << obj->id()\n> +\tif (objects_.find(object->id()) != objects_.end()) {\n> +\t\tLOG(Error) << \"Element with id \" << object->id()\n>  \t\t\t   << \" already enumerated.\";\n>  \t\treturn false;\n>  \t}\n>\n> -\tobjects_[obj->id()] = obj;\n> +\tobjects_[object->id()] = object;\n>\n>  \treturn true;\n>  }\n>\n>  /**\n> - * \\brief Delete all media objects in the MediaDevice.\n> + * \\brief Delete all graph objects in the media device\n> + *\n> + * Clear the media graph and delete all the objects it contains. After this\n> + * function returns any previously obtained pointer to a media graph object\n> + * becomes invalid.\n>   *\n> - * Delete all MediaEntities; entities will then delete their pads,\n> - * and each source pad will delete links.\n> + * The media device graph state is reset to invalid when the graph is cleared.\n>   *\n> - * After this function has been called, the media graph will be unpopulated\n> - * and its media objects deleted. The media device has to be populated\n> - * before it could be used again.\n> + * \\sa valid()\n>   */\n>  void MediaDevice::clear()\n>  {\n> @@ -295,8 +321,7 @@ void MediaDevice::clear()\n>\n>  /*\n>   * For each entity in the media graph create a MediaEntity and store a\n> - * reference in the MediaObject global pool and in the global vector of\n> - * entities.\n> + * reference in the media device objects map and entities list.\n>   */\n>  bool MediaDevice::populateEntities(const struct media_v2_topology &topology)\n>  {\n> --\n> Regards,\n>\n> Laurent Pinchart\n>\n> _______________________________________________\n> libcamera-devel mailing list\n> libcamera-devel@lists.libcamera.org\n> https://lists.libcamera.org/listinfo/libcamera-devel","headers":{"Return-Path":"<jacopo@jmondi.org>","Received":["from relay2-d.mail.gandi.net (relay2-d.mail.gandi.net\n\t[217.70.183.194])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id B4EA460B30\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  2 Jan 2019 10:29:19 +0100 (CET)","from uno.localdomain (2-224-242-101.ip172.fastwebnet.it\n\t[2.224.242.101]) (Authenticated sender: jacopo@jmondi.org)\n\tby relay2-d.mail.gandi.net (Postfix) with ESMTPSA id 2792940002;\n\tWed,  2 Jan 2019 09:29:18 +0000 (UTC)"],"X-Originating-IP":"2.224.242.101","Date":"Wed, 2 Jan 2019 10:29:23 +0100","From":"Jacopo Mondi <jacopo@jmondi.org>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20190102092923.efeptsp4d7ntinqk@uno.localdomain>","References":"<20190101212328.18361-1-laurent.pinchart@ideasonboard.com>\n\t<20190101212328.18361-4-laurent.pinchart@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"multipart/signed; micalg=pgp-sha256;\n\tprotocol=\"application/pgp-signature\"; boundary=\"qg3jugy77wt64xaq\"","Content-Disposition":"inline","In-Reply-To":"<20190101212328.18361-4-laurent.pinchart@ideasonboard.com>","User-Agent":"NeoMutt/20180716","Subject":"Re: [libcamera-devel] [PATCH 4/4] libcamera: mediadevice: Improve\n\tdocumentation","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.23","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Wed, 02 Jan 2019 09:29:19 -0000"}},{"id":172,"web_url":"https://patchwork.libcamera.org/comment/172/","msgid":"<2474198.7UPOdpxZI3@avalon>","date":"2019-01-02T10:05:40","subject":"Re: [libcamera-devel] [PATCH 4/4] libcamera: mediadevice: Improve\n\tdocumentation","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Jacopo,\n\nOn Wednesday, 2 January 2019 11:29:23 EET Jacopo Mondi wrote:\n> Hi Laurent\n>    thanks for the patch.\n> \n> The documentation is now better, thanks. Writing proper doc is harder\n> than writing proper code :/\n\nTell me about it :-S\n\n> On Tue, Jan 01, 2019 at 11:23:28PM +0200, Laurent Pinchart wrote:\n> > Improve the documentation of the media device operation, including how\n> > it handles the lifetime of media objects.\n> > \n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> > \n> >  src/libcamera/include/media_device.h |   2 +-\n> >  src/libcamera/media_device.cpp       | 113 ++++++++++++++++-----------\n> >  2 files changed, 70 insertions(+), 45 deletions(-)\n> > \n> > diff --git a/src/libcamera/include/media_device.h\n> > b/src/libcamera/include/media_device.h index fd1e12d1bbcb..d787be391882\n> > 100644\n> > --- a/src/libcamera/include/media_device.h\n> > +++ b/src/libcamera/include/media_device.h\n> > @@ -44,7 +44,7 @@ private:\n> >  \tstd::map<unsigned int, MediaObject *> objects_;\n> >  \tMediaObject *object(unsigned int id);\n> > -\tbool addObject(MediaObject *obj);\n> > +\tbool addObject(MediaObject *object);\n> >  \tvoid clear();\n> >  \t\n> >  \tstd::vector<MediaEntity *> entities_;\n> > diff --git a/src/libcamera/media_device.cpp\n> > b/src/libcamera/media_device.cpp index 4c77d3787391..2470c0be806e 100644\n> > --- a/src/libcamera/media_device.cpp\n> > +++ b/src/libcamera/media_device.cpp\n> > @@ -32,37 +32,41 @@ namespace libcamera {\n> >   * \\brief The MediaDevice represents a Media Controller device with its\n> >   full\n> >   * graph of connected objects.\n> >   *\n> > - * Media devices are created with an empty graph, which must be populated\n> > from + * A MediaDevice instance is associated with a media controller\n> > device node when + * created, and that association is kept for the\n> > lifetime of the MediaDevice + * instance.\n> >   *\n> > + * The instance is created with an empty media graph. Before performing\n> > any + * other operation, it must be opened with the open() function and\n> > the media + * graph populated by calling populate(). Instances of\n> > MediaEntity, MediaPad and + * MediaLink are created to model the media\n> > graph, and stored in a map indexed + * by object id.\n> >   *\n> > - * The caller is responsible for opening the MediaDevice explicitly\n> > before\n> > - * operating on it, and shall close it when not needed anymore, as access\n> > - * to the MediaDevice is exclusive.\n> > + * Once successfully populated the graph is valid, as reported by the\n> > valid() + * function. It can be queried to list all entities(), or\n> > entities can be + * looked up by name with getEntityByName(). The graph\n> > can be traversed from + * entity to entity through pads and links as\n> > exposed by the corresponding + * classes.\n> >   *\n> > - * A MediaDevice is created empty and gets populated by inspecting the\n> > media - * graph topology using the MEDIA_IOC_G_TOPOLOGY ioctls.\n> > Representation - * of each entity, pad and link described are created\n> > using MediaObject - * derived classes.\n> > - *\n> > - * All MediaObject are stored in a global pool, where they could be\n> > retrieved - * from by their globally unique id.\n> > - *\n> > - * References to MediaEntity registered in the graph are stored in a\n> > vector - * to allow easier by-name lookup, and the list of MediaEntities\n> > is accessible. + * An open media device will keep an open file handle for\n> > the underlying media + * controller device node. It can be closed at any\n> > time with a call to close(). + * This will not invalidate the media graph\n> > and all cached media object remain\n> \n> s/cached media object/cached media objects/\n\nFixed already.\n\n> > + * valid and can be accessed normally. The device can then be later\n> > reopened if + * needed to perform other operations that interect with the\n> > device node.\n> \n> s/interect/interact\n\nDitto.\n\n> >   */\n\n[snip]\n\n> >  /**\n> > - * \\brief Close the file descriptor associated with the media device.\n> > + * \\brief Close the media device\n> > + *\n> > + * This function closes the media device node. It does not invalidate the\n> > media + * graph and all cached media object remain valid and can be\n> > accessed normally.\n> \n> s/cached media object/cached media objects/\n\nAnd here too.\n\n> > + * Once closed no operation interacting with the media device node can be\n> > + * performed until the device is opened again.\n> >   *\n> > - * After this function has been called, for the MediaDevice to be\n> > operated on, - * the caller shall open it again.\n> > + * Closing an already closed device is allowed and will not performed any\n> > + * operation.\n> \n> s/perform/performed\n\nSame.\n\n> Even if I would have said \"Closing an already closed device is a safe\n> non-op\".\n\nIt's usually spelled no-op :-)\n\n> > + *\n> > + * \\sa open()\n> >   */\n> >  void MediaDevice::close()\n> >  {\n> > @@ -130,9 +148,9 @@ void MediaDevice::close()\n> >   * stored as MediaEntity, MediaPad and MediaLink respectively, with\n> >   cross-\n> >   * references between objects. Interfaces are not processed.\n> >   *\n> > - * MediaEntities are stored in a global list in the MediaDevice itself to\n> > ease - * lookup, while MediaPads are accessible from the MediaEntity they\n> > belong - * to only and MediaLinks from the MediaPad they connect.\n> > + * Entities are stored in a separate list in the MediaDevice to ease\n> > lookup, + * while pads are accessible from the entity they belong to and\n> > links from the + * pad they connect.\n> \n> We should establish a rule when to use the type name (eg. MediaEntity)\n> and when a generic name (eg Entities) in documentation.\n\nI agree. When the term is used as a noun I think a generic name should be \nused. This is the case here in my opinion because you use a plural form.\n\n> >   *\n> >   * \\return 0 on success, a negative error code otherwise\n> >   */\n> > @@ -241,8 +259,9 @@ MediaEntity *MediaDevice::getEntityByName(const\n> > std::string &name)\n> >   * object id.\n> >   */\n> > \n> > -/*\n> > - * MediaObject pool lookup by id.\n> > +/**\n> > + * \\brief Retrieve the media graph object specified by \\a id\n> > + * \\return The graph object, or nullptr if no object with \\a id is found\n> >   */\n> \n> This is a private method. Should we doxygen them? I originally\n> commented them without the starting /**\n> \n> afaict documentation on private methods and fields does not show up in the\n> generated output even if I'm sure this can be changed.\n\nI think using the doxygen syntax to document private methods makes sense to be \nconsistent. Whether private methods should be documented or not can be \ndebated. In general I believe they should when documentation has added value, \nespecially when the methods are complex and information not immediately \nvisible from reading the code should be conveyed to the library developers. \nI'm fine skipping documentation for simple private methods.\n\nI wish doxygen would still check syntax of comment blocks for private methods, \neven if it doesn't output documentation.\n\n> >  MediaObject *MediaDevice::object(unsigned int id)\n> >  {\n> > @@ -250,33 +269,40 @@ MediaObject *MediaDevice::object(unsigned int id)\n> >  \treturn (it == objects_.end()) ? nullptr : it->second;\n> >  }\n> > \n> > -/*\n> > - * Add a new object to the global objects pool and fail if the object\n> > - * has already been registered.\n> > +/**\n> > + * \\brief Add a media object to the media graph\n> > + *\n> > + * If the \\a object has a unique id it is added to the media graph, and\n> > its + * lifetime will be managed by the media device. Otherwise the\n> > object isn't + * added to the graph and the caller must delete it.\n> > + *\n> > + * \\return true if the object was successfully added to the graph and\n> > false + * otherwise\n> \n> Same question as above.\n\nIn this case I think it's important to state that a failed call to addObject() \nleaves the responsibility of deleting the object to the caller. The object() \nmethod above could be left undocumented as it's quite straightforward.\n\n> >   */\n> > -bool MediaDevice::addObject(MediaObject *obj)\n> > +bool MediaDevice::addObject(MediaObject *object)\n> >  {\n> > -\tif (objects_.find(obj->id()) != objects_.end()) {\n> > -\t\tLOG(Error) << \"Element with id \" << obj->id()\n> > +\tif (objects_.find(object->id()) != objects_.end()) {\n> > +\t\tLOG(Error) << \"Element with id \" << object->id()\n> >  \t\t\t   << \" already enumerated.\";\n> >  \t\treturn false;\n> >  \t}\n> > \n> > -\tobjects_[obj->id()] = obj;\n> > +\tobjects_[object->id()] = object;\n> > \n> >  \treturn true;\n> >  }\n\n[snip]","headers":{"Return-Path":"<laurent.pinchart@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 197CE60B30\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  2 Jan 2019 11:04:40 +0100 (CET)","from avalon.localnet (dfj612ybrt5fhg77mgycy-3.rev.dnainternet.fi\n\t[IPv6:2001:14ba:21f5:5b00:2e86:4862:ef6a:2804])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 8D219505;\n\tWed,  2 Jan 2019 11:04:39 +0100 (CET)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1546423479;\n\tbh=biuX2Ffwe/P/V6rIZAbCPj+PK2zw0qmBoJtOfQ0IaE4=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=fORuDp4B9XMb6ext6bfcZjXdsG61Do4mUIh466fU7SOmz6RLv38noMecS7N/rElBb\n\tEFmLHq4qMN/ypWFxnc6qHSXnI7VKnTXGCaYtYGwHl4gKy34b3q8gxRIcXYZMnQ21DQ\n\tBh1PWrr3H8R5t4W0JqSUikTYIeXe7kipkcsqhVJY=","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Jacopo Mondi <jacopo@jmondi.org>","Cc":"libcamera-devel@lists.libcamera.org","Date":"Wed, 02 Jan 2019 12:05:40 +0200","Message-ID":"<2474198.7UPOdpxZI3@avalon>","Organization":"Ideas on Board Oy","In-Reply-To":"<20190102092923.efeptsp4d7ntinqk@uno.localdomain>","References":"<20190101212328.18361-1-laurent.pinchart@ideasonboard.com>\n\t<20190101212328.18361-4-laurent.pinchart@ideasonboard.com>\n\t<20190102092923.efeptsp4d7ntinqk@uno.localdomain>","MIME-Version":"1.0","Content-Transfer-Encoding":"7Bit","Content-Type":"text/plain; charset=\"us-ascii\"","Subject":"Re: [libcamera-devel] [PATCH 4/4] libcamera: mediadevice: Improve\n\tdocumentation","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.23","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Wed, 02 Jan 2019 10:04:40 -0000"}}]