From patchwork Sat May 16 19:20:00 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: William Breathitt Gray X-Patchwork-Id: 11553723 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 173FF618 for ; Sat, 16 May 2020 19:21:03 +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 DA27420727 for ; Sat, 16 May 2020 19:21:02 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="Ucvvl84a"; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="uaAEw75z" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org DA27420727 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+patchwork-linux-arm=patchwork.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:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version: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=XNBK0vYZA0lV5ZgDtzXxJYsJcwM3fvQh/SHVcguBJrM=; b=Ucvvl84a4eEQgG JXHK9VV7WaYGZJfaJnOw8MYotr52YJgykURgp7py5O4C1krLX/3hcr+TF+w1gEJQvI91w18fR78FB E5lO4bgEgC+fkdNwzXXyjxCUKrSjXizwCYoQzVoliyu9Qa416PvWVNb03zSsHAz3kxhLEMcZFRC3F DLDvi+XYdcWb9AsbWBPkKjLXaBd8JqDcL0hGD3CUr2DwF1y0seWihU8dK2yKpPYaRW3N+jqkCFNHl SgW3OeL8nkSjIuYoPG7MXzhE1PKcLJMRaOQSuMOCDHdlPNKlLJm47HiZZ9DdqzPdXcrPpBbNUzcO9 /0BF7vPfssGsyU3S8LuA==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1ja2Md-0007xt-Vi; Sat, 16 May 2020 19:20:51 +0000 Received: from mail-qt1-x844.google.com ([2607:f8b0:4864:20::844]) by bombadil.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1ja2MT-0007qM-Rn for linux-arm-kernel@lists.infradead.org; Sat, 16 May 2020 19:20:44 +0000 Received: by mail-qt1-x844.google.com with SMTP id c24so4882425qtw.7 for ; Sat, 16 May 2020 12:20:41 -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 :mime-version:content-transfer-encoding; bh=l8MBXHzo1KA9f1dfH4gxXPreM1Deeb9WOZhWNKpCMBo=; b=uaAEw75zEFXcj4qy4eVGMzsmNy9dU/z5vCVgTaHh+GMq9vueGZfENtJoATNGnthq9c LJNA+f7utMjDy8vVm65NXPHKPJAvEhmFKlICIaW0ehP/Nic+u6YIUbB+x5oE7e1nSgj8 y8T3Ro/n9sN5olPXKLmHJNgQahE2rKUE2KEzsjEJsfZAZ2IMRV9NnW+2nCOgVCnFgSBt 3Rw+a4OQn1tpwkLsQIqCzzMGJuiFEL5TMw5sdYhnHbVMS6rYuX06yR6t2P6E1bvtvK/R tg5EAw9zr/zIeOM6y98YvJnCqI3m4vawOHxLdF8JWNMZPSUiHhU5E+LmrmKlOjxXBng6 kTvQ== 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:mime-version:content-transfer-encoding; bh=l8MBXHzo1KA9f1dfH4gxXPreM1Deeb9WOZhWNKpCMBo=; b=HKdQIUQ5I4XUAcEvY9ww8MDJTaiA4cUTsvhfwNgkxc7r5A3t8K9czC3qbrDqCZm6Sf SRe9/Ksy1A7JDB/8Oqzj/eO8iwwH+1AXc0jvRgbXrVg6Q8hWllz1jGkqqBnOvx3Bqpw+ xT9Z6NPqm2oa7DgAVzO6kvt0rzepn/o+ijS4rxuVKByWwCTizAGSXr7ZV+l1a6kR9Ju2 3zK4RGl61qyFsOVL6BCXPmaEaqpeOcCgjn9FMIlAMewZDLT3Kv5cWrLBBaBMd2NRSS/3 lgVWx5ueWpyw+25VjG5ArIREVScRjY3K0sBKM19j+FddtMZ1mkD9Ghj22kz3nPDBeotJ oIpw== X-Gm-Message-State: AOAM530sFJO0OpK+odfDKewtG8Yf2a80SoKV2HC2TP60e0pusDPi8thn 26pDfIedINl6EGlyqLEMMXc= X-Google-Smtp-Source: ABdhPJwm1GApw+QLvTEpxLUBK8Y+8YJynr03UcXo+f76HH66WDq5GrHR+U0g144axupK4kbscl2Mcg== X-Received: by 2002:ac8:2aa6:: with SMTP id b35mr9111295qta.153.1589656840988; Sat, 16 May 2020 12:20:40 -0700 (PDT) Received: from localhost.localdomain (072-189-064-225.res.spectrum.com. [72.189.64.225]) by smtp.gmail.com with ESMTPSA id g19sm3160409qke.32.2020.05.16.12.20.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 16 May 2020 12:20:40 -0700 (PDT) From: William Breathitt Gray To: jic23@kernel.org Subject: [PATCH v2 2/4] docs: counter: Update to reflect sysfs internalization Date: Sat, 16 May 2020 15:20:00 -0400 Message-Id: <2a8d7fe2e0ea435e19c89ba2aff15218c7b6eb91.1589654470.git.vilhelm.gray@gmail.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: References: MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200516_122041_947728_ACF2E599 X-CRM114-Status: GOOD ( 21.58 ) X-Spam-Score: -0.2 (/) X-Spam-Report: SpamAssassin version 3.4.4 on bombadil.infradead.org summary: Content analysis details: (-0.2 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at https://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:844 listed in] [list.dnswl.org] 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider [vilhelm.gray[at]gmail.com] 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.0 SPF_PASS SPF: sender matches SPF record 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kamel.bouhara@bootlin.com, gwendal@chromium.org, david@lechnology.com, linux-iio@vger.kernel.org, patrick.havelange@essensium.com, alexandre.belloni@bootlin.com, linux-kernel@vger.kernel.org, mcoquelin.stm32@gmail.com, William Breathitt Gray , fabrice.gasnier@st.com, syednwaris@gmail.com, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, alexandre.torgue@st.com Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org The Counter subsystem architecture and driver implementations have changed in order to handle Counter sysfs interactions in a more consistent way. This patch updates the Generic Counter interface documentation to reflect the changes. Signed-off-by: William Breathitt Gray --- Documentation/driver-api/generic-counter.rst | 215 +++++++++++++------ 1 file changed, 149 insertions(+), 66 deletions(-) diff --git a/Documentation/driver-api/generic-counter.rst b/Documentation/driver-api/generic-counter.rst index e622f8f6e56a..8f85c30dea0b 100644 --- a/Documentation/driver-api/generic-counter.rst +++ b/Documentation/driver-api/generic-counter.rst @@ -250,8 +250,8 @@ for defining a counter device. .. kernel-doc:: drivers/counter/counter.c :export: -Implementation -============== +Driver Implementation +===================== To support a counter device, a driver must first allocate the available Counter Signals via counter_signal structures. These Signals should @@ -267,25 +267,58 @@ respective counter_count structure. These counter_count structures are set to the counts array member of an allocated counter_device structure before the Counter is registered to the system. -Driver callbacks should be provided to the counter_device structure via -a constant counter_ops structure in order to communicate with the -device: to read and write various Signals and Counts, and to set and get -the "action mode" and "function mode" for various Synapses and Counts -respectively. +Driver callbacks must be provided to the counter_device structure in +order to communicate with the device: to read and write various Signals +and Counts, and to set and get the "action mode" and "function mode" for +various Synapses and Counts respectively. A defined counter_device structure may be registered to the system by passing it to the counter_register function, and unregistered by passing it to the counter_unregister function. Similarly, the -devm_counter_register and devm_counter_unregister functions may be used -if device memory-managed registration is desired. - -Extension sysfs attributes can be created for auxiliary functionality -and data by passing in defined counter_device_ext, counter_count_ext, -and counter_signal_ext structures. In these cases, the -counter_device_ext structure is used for global/miscellaneous exposure -and configuration of the respective Counter device, while the -counter_count_ext and counter_signal_ext structures allow for auxiliary -exposure and configuration of a specific Count or Signal respectively. +devm_counter_register function may be used if device memory-managed +registration is desired. + +The struct counter_data structure is used to define counter extensions +for Signals, Synapses, and Counts. + +The "type" member specifies the type of data (e.g. unsigned long, +boolean, etc.) handled by this extension. The "read" and "write" members +can then be set by the counter device driver with callbacks to handle +that data. + +Convenience macros such as `COUNTER_DATA_COUNT_U64` are provided for use +by driver authors. In particular, driver authors are expected to use +the provided macros for standard Counter subsystem attributes in order +to maintain a consistent interface for userspace. For example, a counter +device driver may define several standard attributes like so:: + + struct counter_data count_ext[] = { + COUNTER_DATA_DIRECTION(count_direction_read), + COUNTER_DATA_ENABLE(count_enable_read, count_enable_write), + COUNTER_DATA_CEILING(count_ceiling_read, count_ceiling_write), + }; + +This makes it intuitive to see, add, and modify the attributes that are +supported by this driver ("direction", "enable", and "ceiling") and to +maintain this code without getting lost in a web of struct braces. + +Callbacks must match the function type expected for the respective +component or extension. These function types are defined in the struct +counter_data structure as the "`*_read`" and "`*_write`" union members. + +The corresponding callback prototypes for the extensions mentioned in +the previous example above would be:: + + int count_direction_read(struct counter_device *counter, + struct counter_count *count, u8 *direction); + int count_enable_read(struct counter_device *counter, + struct counter_count *count, u8 *enable); + int count_enable_write(struct counter_device *counter, + struct counter_count *count, u8 enable); + int count_ceiling_read(struct counter_device *counter, + struct counter_count *count, u64 *ceiling); + int count_ceiling_write(struct counter_device *counter, + struct counter_count *count, u64 ceiling); Determining the type of extension to create is a matter of scope. @@ -313,52 +346,102 @@ Determining the type of extension to create is a matter of scope. chip overheated via a device extension called "error_overtemp": /sys/bus/counter/devices/counterX/error_overtemp -Architecture -============ - -When the Generic Counter interface counter module is loaded, the -counter_init function is called which registers a bus_type named -"counter" to the system. Subsequently, when the module is unloaded, the -counter_exit function is called which unregisters the bus_type named -"counter" from the system. - -Counter devices are registered to the system via the counter_register -function, and later removed via the counter_unregister function. The -counter_register function establishes a unique ID for the Counter -device and creates a respective sysfs directory, where X is the -mentioned unique ID: - - /sys/bus/counter/devices/counterX - -Sysfs attributes are created within the counterX directory to expose -functionality, configurations, and data relating to the Counts, Signals, -and Synapses of the Counter device, as well as options and information -for the Counter device itself. - -Each Signal has a directory created to house its relevant sysfs -attributes, where Y is the unique ID of the respective Signal: - - /sys/bus/counter/devices/counterX/signalY - -Similarly, each Count has a directory created to house its relevant -sysfs attributes, where Y is the unique ID of the respective Count: - - /sys/bus/counter/devices/counterX/countY - -For a more detailed breakdown of the available Generic Counter interface -sysfs attributes, please refer to the -Documentation/ABI/testing/sysfs-bus-counter file. - -The Signals and Counts associated with the Counter device are registered -to the system as well by the counter_register function. The -signal_read/signal_write driver callbacks are associated with their -respective Signal attributes, while the count_read/count_write and -function_get/function_set driver callbacks are associated with their -respective Count attributes; similarly, the same is true for the -action_get/action_set driver callbacks and their respective Synapse -attributes. If a driver callback is left undefined, then the respective -read/write permission is left disabled for the relevant attributes. - -Similarly, extension sysfs attributes are created for the defined -counter_device_ext, counter_count_ext, and counter_signal_ext -structures that are passed in. +Subsystem Architecture +====================== + +Counter drivers pass and take data natively (i.e. `u8`, `u64`, `char *`, +etc.) and the shared counter module handles the translation between the +sysfs interface. This gurantees a standard userspace interface for all +counter drivers, and helps generalize the Generic Counter driver ABI in +order to support the Generic Counter chrdev interface without +significant changes to the existing counter drivers. + +A high-level view of how a count value is passed down from a counter +driver can be exemplified by the following:: + + Count data request: + ~~~~~~~~~~~~~~~~~~~ + ---------------------- + / Counter device \ + +----------------------+ + | Count register: 0x28 | + +----------------------+ + | + ----------------- + / raw count data / + ----------------- + | + V + +----------------------------+ + | Counter device driver |----------+ + +----------------------------+ | + | Processes data from device | ------------------- + |----------------------------| / driver callbacks / + | Type: unsigned long | ------------------- + | Value: 42 | | + +----------------------------+ | + | | + ---------------- | + / unsigned long / | + ---------------- | + | | + | V + | +----------------------+ + | | Counter core | + | +----------------------+ + | | Routes device driver | + | | callbacks to the | + | | userspace interfaces | + | +----------------------+ + | | + | ------------------- + | / driver callbacks / + | ------------------- + | | + +-------+ | + | | + | +---------------+ + | | + V | + +--------------------+ | + | Counter sysfs |<-+ + +--------------------+ + | Translates to the | + | standard Counter | + | sysfs output | + |--------------------| + | Type: const char * | + | Value: "42" | + +--------------------+ + | + --------------- + / const char * / + --------------- + | + V + +--------------------------------------------------+ + | `/sys/bus/counter/devices/counterX/countY/count` | + +--------------------------------------------------+ + \ Count: "42" / + -------------------------------------------------- + +There are three primary components involved: + +Counter device driver +--------------------- +Communicates with the hardware device to read/write data; e.g. counter +drivers for quadrature encoders, timers, etc. + +Counter core +------------ +Registers the counter device driver to the system so that the respective +callbacks are called during userspace interaction. + +Counter sysfs +------------- +Translates counter data to the standard Counter sysfs interface format +and vice versa. + +Please refer to the `Documentation/ABI/testing/sysfs-bus-counter` file +for a detailed breakdown of the available Generic Counter interface +sysfs attributes. From patchwork Sat May 16 19:20:01 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: William Breathitt Gray X-Patchwork-Id: 11553727 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 87551618 for ; Sat, 16 May 2020 19:21:34 +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 5EE4D20727 for ; Sat, 16 May 2020 19:21:34 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="BVdw4wkT"; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="S45Y1WF1" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 5EE4D20727 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+patchwork-linux-arm=patchwork.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:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version: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=t083FM4uLZFWpKoB0nAjS1Yz5P/mF5L+cwMnmMOfpp4=; b=BVdw4wkT1vwJ7b kcip4s5ueyuE5OKnn+27jfKawugjgu3kxz8wUmSLFOUGipfq0kEh5Puupk2F4hM0UguE8Fl4Oalab LR5F+tyVpkHNVVMnHcVHOZMYbOjtQF89L3j5ipqGdv8DCIf/3Hd6+7THMFp2gYIOx+siDkWKyPuVh aF5nEHFEeGrW4JtUZ16oj8SN9/eJPBBgpUsiRbieRjF/jbpIw5DbU0cMevvs1rFgXEUr6FKPx+00s n61uuW4fqPQ4tXUb9igIEMedLmYpNN1VFpqmPoZ/GJyt3x+9WL4haYgsmprWtlUckIY7TwUAId3HD 9NXSZKEhWFWwDMkkHFTw==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1ja2NI-0000bj-4u; Sat, 16 May 2020 19:21:32 +0000 Received: from mail-qt1-x841.google.com ([2607:f8b0:4864:20::841]) by bombadil.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1ja2MW-0007qk-Ki for linux-arm-kernel@lists.infradead.org; Sat, 16 May 2020 19:20:48 +0000 Received: by mail-qt1-x841.google.com with SMTP id d7so4855276qtn.11 for ; Sat, 16 May 2020 12:20:43 -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 :mime-version:content-transfer-encoding; bh=11TPcs5o0DO7/jT3uUgXzZhv+LOOFIJIm6J1xwX89NM=; b=S45Y1WF14Mvl5uW6XjfDplmNiux5S/jxzW//xybxqmckyOsjKAtcZczMkNoY+d7Zej Ns5S8awJwWiZUJMZNFkp5xCa+MVJ1mWsUH88QmzQOJkPW5hE+eehs9CcCQ/dsiXsn5AO v7CUz8i1f7mta7KvBL1N3k9kfHX1wXCYc/MheS8KZFhEcOHTnT3cvZ3LAQme90+vcYz4 64ESiKiFuhCZQXGPIP06dBmPTrVyyaS5d31fjDM2c8RwI1b3wcwVqO3dZlq8/lbm03RB Mhnyub0GdbGadltkcdJan/dIg8+wR3VQ80B3vXBv5vqy4Gl0Mt78n07ZfMAGIkCTliA1 aJjw== 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:mime-version:content-transfer-encoding; bh=11TPcs5o0DO7/jT3uUgXzZhv+LOOFIJIm6J1xwX89NM=; b=SUEdgSa8Ez5o17Dd869+f+fhcy2L/ZYNZ4Ij/50Ehj5vA39R+v5zFnPPsnwjCUm9oM dZrIqnEPMTi9JyoCrx7gWHO2qpKKiP4aH/bP9rV6G9xtKe0Gjwih6jQYUkX8oEd+yjR2 Cl8bOv7l2eQaQWSV3IBxuN9cpX0K2Q8mubPDrJVTXCBLkUxILIAHf+LCS7SiQ683KTa0 bHlGZ22HhhbI3KqThLcxGtioxLrhBTB2Qwki1EeqLxjeFc+3uTbi5UgHyVyu7FxxQ6Au grb1N/YixztnW67xZODlZ1RUA1zt9bLLulMSxqgtA3gw4tCSFA0P7shBR6is/XBauTWh V2Gw== X-Gm-Message-State: AOAM531e87PXPtmlimvMUC98bBsfLCdvHJgoc4KL0/1s/tfDkRIaA4yt lowwn3Ec5bl+S98KqhljYsA= X-Google-Smtp-Source: ABdhPJwpDI9/8ACtrx1TEoFzVI/Dmdz21QGi4a9z2DiEdgyWMASwgBXIotutoeeH8htj2pTOfQLlCg== X-Received: by 2002:ac8:6e82:: with SMTP id c2mr9613014qtv.155.1589656842558; Sat, 16 May 2020 12:20:42 -0700 (PDT) Received: from localhost.localdomain (072-189-064-225.res.spectrum.com. [72.189.64.225]) by smtp.gmail.com with ESMTPSA id g19sm3160409qke.32.2020.05.16.12.20.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 16 May 2020 12:20:41 -0700 (PDT) From: William Breathitt Gray To: jic23@kernel.org Subject: [PATCH v2 3/4] counter: Add character device interface Date: Sat, 16 May 2020 15:20:01 -0400 Message-Id: X-Mailer: git-send-email 2.26.2 In-Reply-To: References: MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200516_122044_737265_10F8F256 X-CRM114-Status: GOOD ( 20.90 ) X-Spam-Score: -0.2 (/) X-Spam-Report: SpamAssassin version 3.4.4 on bombadil.infradead.org summary: Content analysis details: (-0.2 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at https://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:841 listed in] [list.dnswl.org] 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider [vilhelm.gray[at]gmail.com] 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.0 SPF_PASS SPF: sender matches SPF record 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kamel.bouhara@bootlin.com, gwendal@chromium.org, david@lechnology.com, linux-iio@vger.kernel.org, patrick.havelange@essensium.com, alexandre.belloni@bootlin.com, linux-kernel@vger.kernel.org, mcoquelin.stm32@gmail.com, William Breathitt Gray , fabrice.gasnier@st.com, syednwaris@gmail.com, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, alexandre.torgue@st.com Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org This patch introduces a character device interface for the Counter subsystem. Device control is exposed through standard character device read and write operations. The first 196095 bytes of the character device serve as a control selection area where control exposure of desired Counter components and extensions may be selected. Each byte serves as a boolean selection indicator for a respective Counter component or extension. The format of this area is as follows: * For each device extension, a byte is required. * For each Signal, a byte is reserved for the Signal component, and a byte is reserved for each Signal extension. * For each Count, a byte is reserved for the Count component, a byte is reserved for the count function, a byte is reserved for each Synapse action, and byte is reserved for each Count extension. The selected Counter components and extensions may then be interfaced after the first 196095 bytes via standard character device read/write operations. The number of bytes available for each component or extension is dependent on their respective data type: u8 will have 1 byte available, u64 will have 8 bytes available, strings will have 64 bytes available, etc. A high-level view of how a count value is passed down from a counter driver can be exemplified by the following: ---------------------- / Counter device \ +----------------------+ | Count register: 0x28 | +----------------------+ | ----------------- / raw count data / ----------------- | V +----------------------------+ | Counter device driver |----------+ +----------------------------+ | | Processes data from device | ------------------- |----------------------------| / driver callbacks / | Type: u64 | ------------------- | Value: 42 | | +----------------------------+ | | | ---------- | / u64 / | ---------- | | | | V | +----------------------+ | | Counter core | | +----------------------+ | | Routes device driver | | | callbacks to the | | | userspace interfaces | | +----------------------+ | | | ------------------- | / driver callbacks / | ------------------- | | +-------+---------------+ | | | | | +-------|-------+ | | | V | V +--------------------+ | +---------------------+ | Counter sysfs |<-+->| Counter chrdev | +--------------------+ +---------------------+ | Translates to the | | Translates to the | | standard Counter | | standard Counter | | sysfs output | | character device | |--------------------| |---------------------+ | Type: const char * | | Type: u64 | | Value: "42" | | Value: 42 | +--------------------+ +---------------------+ | | --------------- ---------- / const char * / / u64 / --------------- ---------- | | | V | +-----------+ | | read | | +-----------+ | \ Count: 42 / | ----------- | V +--------------------------------------------------+ | `/sys/bus/counter/devices/counterX/countY/count` | +--------------------------------------------------+ \ Count: "42" / -------------------------------------------------- Signed-off-by: William Breathitt Gray Reported-by: kbuild test robot --- MAINTAINERS | 1 + drivers/counter/Makefile | 2 +- drivers/counter/counter-chrdev.c | 656 +++++++++++++++++++++++++++++++ drivers/counter/counter-chrdev.h | 16 + drivers/counter/counter-core.c | 34 +- include/linux/counter.h | 16 + 6 files changed, 722 insertions(+), 3 deletions(-) create mode 100644 drivers/counter/counter-chrdev.c create mode 100644 drivers/counter/counter-chrdev.h diff --git a/MAINTAINERS b/MAINTAINERS index ef72b5755793..150ad8a9bb87 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4365,6 +4365,7 @@ F: Documentation/ABI/testing/sysfs-bus-counter* F: Documentation/driver-api/generic-counter.rst F: drivers/counter/ F: include/linux/counter.h +F: include/uapi/linux/counter.h F: include/uapi/linux/counter-types.h CPMAC ETHERNET DRIVER diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile index f48e337cbd85..d219b9c058e7 100644 --- a/drivers/counter/Makefile +++ b/drivers/counter/Makefile @@ -4,7 +4,7 @@ # obj-$(CONFIG_COUNTER) += counter.o -counter-y := counter-core.o counter-sysfs.o +counter-y := counter-core.o counter-sysfs.o counter-chrdev.o obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o obj-$(CONFIG_STM32_TIMER_CNT) += stm32-timer-cnt.o diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c new file mode 100644 index 000000000000..7fd55bf71e47 --- /dev/null +++ b/drivers/counter/counter-chrdev.c @@ -0,0 +1,656 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Generic Counter character device interface + * Copyright (C) 2020 William Breathitt Gray + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum counter_owner_type { + COUNTER_OWNER_TYPE_DEVICE, + COUNTER_OWNER_TYPE_SIGNAL, + COUNTER_OWNER_TYPE_COUNT, +}; + +struct counter_control { + loff_t offset; + size_t size; + struct list_head l; + + struct counter_data data; + enum counter_owner_type type; + void *owner; +}; + +static int counter_data_u8_read(struct counter_device *const counter, + const struct counter_control *const control, + u8 __user *const buf) +{ + const struct counter_data *const data = &control->data; + int err; + u8 val; + + switch (control->type) { + case COUNTER_OWNER_TYPE_DEVICE: + err = data->device_u8_read(counter, &val); + break; + case COUNTER_OWNER_TYPE_SIGNAL: + err = data->signal_u8_read(counter, control->owner, &val); + break; + case COUNTER_OWNER_TYPE_COUNT: + if (data->type == COUNTER_DATA_TYPE_SYNAPSE_ACTION) + err = data->action_read(counter, control->owner, + data->priv, &val); + else + err = data->count_u8_read(counter, control->owner, + &val); + break; + default: return -EINVAL; + } + if (err) + return err; + + return put_user(val, buf); +} + +static int counter_data_u64_read(struct counter_device *const counter, + const struct counter_control *const control, + char __user *const buf, const size_t len, + const size_t offset) +{ + const struct counter_data *const data = &control->data; + int err; + u64 val; + + switch (control->type) { + case COUNTER_OWNER_TYPE_DEVICE: + err = data->device_u64_read(counter, &val); + break; + case COUNTER_OWNER_TYPE_SIGNAL: + err = data->signal_u64_read(counter, control->owner, &val); + break; + case COUNTER_OWNER_TYPE_COUNT: + err = data->count_u64_read(counter, control->owner, &val); + break; + default: return -EINVAL; + } + if (err) + return err; + + return copy_to_user(buf, (const char *)&val + offset, len); +} + +static int counter_data_string_read(struct counter_device *const counter, + const struct counter_control *const control, + char __user *const buf, const size_t len, + const size_t offset) +{ + const struct counter_data *const data = &control->data; + int err; + char str[64] = ""; + + switch (control->type) { + case COUNTER_OWNER_TYPE_DEVICE: + err = data->device_string_read(counter, str, sizeof(str)); + break; + case COUNTER_OWNER_TYPE_SIGNAL: + err = data->signal_string_read(counter, control->owner, str, + sizeof(str)); + break; + case COUNTER_OWNER_TYPE_COUNT: + err = data->count_string_read(counter, control->owner, str, + sizeof(str)); + break; + default: return -EINVAL; + } + if (err < 0) + return err; + + return copy_to_user(buf, str + offset, len); +} + +static int counter_control_read(struct counter_device *const counter, + const struct counter_control *const control, + char __user *const buf, const size_t len, + const size_t offset) +{ + /* Check if read operation is supported */ + if (!control->data.device_u8_read) + return -EFAULT; + + switch (control->data.type) { + case COUNTER_DATA_TYPE_U8: + case COUNTER_DATA_TYPE_BOOL: + case COUNTER_DATA_TYPE_SIGNAL: + case COUNTER_DATA_TYPE_COUNT_FUNCTION: + case COUNTER_DATA_TYPE_SYNAPSE_ACTION: + case COUNTER_DATA_TYPE_COUNT_DIRECTION: + case COUNTER_DATA_TYPE_COUNT_MODE: + return counter_data_u8_read(counter, control, buf); + case COUNTER_DATA_TYPE_U64: + return counter_data_u64_read(counter, control, buf, len, + offset); + case COUNTER_DATA_TYPE_STRING: + return counter_data_string_read(counter, control, buf, len, + offset); + } + + return -EINVAL; +} + +static ssize_t counter_chrdev_read(struct file *filp, char __user *buf, + size_t len, loff_t *f_ps) +{ + const loff_t start_ps = *f_ps; + struct counter_device *const counter = filp->private_data; + size_t read_size; + struct counter_control *control; + int err; + loff_t next_control_ps; + + /* Handle Counter control selection */ + if (*f_ps < COUNTER_SELECTION_SIZE) { + read_size = (*f_ps + len > COUNTER_SELECTION_SIZE) ? + COUNTER_SELECTION_SIZE - *f_ps : len; + + if (copy_to_user(buf, counter->selection + *f_ps, read_size)) + return -EFAULT; + + *f_ps += read_size; + buf += read_size; + len -= read_size; + if (!len) + goto exit_chrdev_read; + } + + /* Handle controls */ + list_for_each_entry(control, &counter->control_list, l) { + next_control_ps = control->offset + control->size; + + /* Find first control item */ + if (*f_ps >= next_control_ps) + continue; + + read_size = (*f_ps + len > next_control_ps) ? + next_control_ps - *f_ps : len; + + err = counter_control_read(counter, control, buf, read_size, + control->offset - *f_ps); + if (err) + return err; + + *f_ps += read_size; + buf += read_size; + len -= read_size; + if (!len) + goto exit_chrdev_read; + } + +exit_chrdev_read: + return *f_ps - start_ps; +} + +static int counter_control_ext_add(const u8 *const selection, + const enum counter_owner_type type, + void *const owner, const size_t num_ext, + const struct counter_data *const ext, + struct list_head *const cntrl_list, + loff_t *const offset) +{ + struct counter_control *p = list_last_entry(cntrl_list, typeof(*p), l); + struct counter_control *n; + size_t i; + struct counter_control *control; + int err; + + for (i = 0; i < num_ext; i++) { + /* Check if extension is selected */ + if (!selection[i]) + continue; + + /* Generate control list item */ + control = kzalloc(sizeof(*control), GFP_KERNEL); + if (!control) { + err = -ENOMEM; + goto err_control_ext_create; + } + list_add_tail(&control->l, cntrl_list); + + /* Configure control list item */ + control->data = ext[i], + control->type = type; + control->owner = owner; + control->offset = *offset; + switch (control->data.type) { + case COUNTER_DATA_TYPE_U8: + case COUNTER_DATA_TYPE_BOOL: + case COUNTER_DATA_TYPE_COUNT_DIRECTION: + case COUNTER_DATA_TYPE_COUNT_MODE: + control->size = sizeof(u8); + break; + case COUNTER_DATA_TYPE_U64: + control->size = sizeof(u64); + break; + case COUNTER_DATA_TYPE_STRING: + control->size = 64; + break; + default: + err = -EINVAL; + goto err_control_ext_create; + } + *offset += control->size; + } + + return 0; + +err_control_ext_create: + list_for_each_entry_safe_continue(p, n, cntrl_list, l) { + list_del(&p->l); + kfree(p); + } + return err; +} + +static void counter_control_list_free(struct list_head *const cntrl_list) +{ + struct counter_control *p, *n; + + list_for_each_entry_safe(p, n, cntrl_list, l) { + list_del(&p->l); + kfree(p); + } +} + +static int counter_control_list_create(struct counter_device *const counter) +{ + const u8 *selection = counter->selection; + loff_t offset = COUNTER_SELECTION_SIZE; + size_t i, j; + struct counter_signal *signal; + struct counter_count *count; + struct counter_control *control; + int err; + + /* Clean up old list */ + counter_control_list_free(&counter->control_list); + + /* Handle device extensions */ + err = counter_control_ext_add(selection, COUNTER_OWNER_TYPE_DEVICE, + NULL, counter->num_ext, counter->ext, + &counter->control_list, &offset); + if (err) + goto err_control_create; + selection += counter->num_ext; + + /* Handle Signals */ + for (i = 0; i < counter->num_signals; i++) { + signal = counter->signals + i; + + /* Check if Signal is selected */ + if (*selection++) { + /* Generate control list item */ + control = kzalloc(sizeof(*control), GFP_KERNEL); + if (!control) { + err = -ENOMEM; + goto err_control_create; + } + list_add_tail(&control->l, &counter->control_list); + + /* Configure control list item */ + control->data.type = COUNTER_DATA_TYPE_SIGNAL, + control->data.signal_u8_read = counter->signal_read; + control->type = COUNTER_OWNER_TYPE_SIGNAL; + control->owner = signal; + control->offset = offset; + control->size = sizeof(u8); + offset += control->size; + } + + /* Handle extensions */ + err = counter_control_ext_add(selection, + COUNTER_OWNER_TYPE_SIGNAL, signal, + signal->num_ext, signal->ext, + &counter->control_list, &offset); + if (err) + goto err_control_create; + selection += signal->num_ext; + } + + /* Handle Counts */ + for (i = 0; i < counter->num_counts; i++) { + count = counter->counts + i; + + /* Check if Count is selected */ + if (*selection++) { + /* Generate control list item */ + control = kzalloc(sizeof(*control), GFP_KERNEL); + if (!control) { + err = -ENOMEM; + goto err_control_create; + } + list_add_tail(&control->l, &counter->control_list); + + /* Configure control list item */ + control->data.type = COUNTER_DATA_TYPE_U64, + control->data.count_u64_read = counter->count_read; + control->data.count_u64_write = counter->count_write; + control->type = COUNTER_OWNER_TYPE_COUNT; + control->owner = count; + control->offset = offset; + control->size = sizeof(u64); + offset += control->size; + } + + /* Handle count function */ + if (*selection++) { + /* Generate control list item */ + control = kzalloc(sizeof(*control), GFP_KERNEL); + if (!control) { + err = -ENOMEM; + goto err_control_create; + } + list_add_tail(&control->l, &counter->control_list); + + /* Configure control list item */ + control->data.type = COUNTER_DATA_TYPE_COUNT_FUNCTION, + control->data.count_u8_read = counter->function_read; + control->data.count_u8_write = counter->function_write; + control->type = COUNTER_OWNER_TYPE_COUNT; + control->owner = count; + control->offset = offset; + control->size = sizeof(u8); + offset += control->size; + } + + /* Handle Synapses */ + for (j = 0; j < count->num_synapses; j++) { + /* Check if extension is selected */ + if (!*selection++) + continue; + + /* Generate control list item */ + control = kzalloc(sizeof(*control), GFP_KERNEL); + if (!control) { + err = -ENOMEM; + goto err_control_create; + } + list_add_tail(&control->l, &counter->control_list); + + /* Configure control list item */ + control->data.type = COUNTER_DATA_TYPE_SYNAPSE_ACTION; + control->data.action_read = counter->action_read; + control->data.action_write = counter->action_write; + control->data.priv = count->synapses + j; + control->type = COUNTER_OWNER_TYPE_COUNT; + control->owner = count; + control->offset = offset; + control->size = sizeof(u8); + offset += control->size; + } + + /* Handle extensions */ + err = counter_control_ext_add(selection, + COUNTER_OWNER_TYPE_COUNT, count, + count->num_ext, count->ext, + &counter->control_list, &offset); + if (err) + goto err_control_create; + selection += count->num_ext; + } + + return 0; + +err_control_create: + counter_control_list_free(&counter->control_list); + return err; +} + +static int counter_data_u8_write(struct counter_device *const counter, + const struct counter_control *const control, + const u8 __user *const buf) +{ + const struct counter_data *const data = &control->data; + int err; + u8 val; + + err = get_user(val, buf); + if (err) + return err; + + switch (control->type) { + case COUNTER_OWNER_TYPE_DEVICE: + err = data->device_u8_write(counter, val); + break; + case COUNTER_OWNER_TYPE_SIGNAL: + err = data->signal_u8_write(counter, control->owner, val); + break; + case COUNTER_OWNER_TYPE_COUNT: + if (data->type == COUNTER_DATA_TYPE_SYNAPSE_ACTION) + err = data->action_write(counter, control->owner, + data->priv, val); + else + err = data->count_u8_write(counter, control->owner, + val); + break; + default: return -EINVAL; + } + + return err; +} + +static int counter_data_u64_write(struct counter_device *const counter, + const struct counter_control *const control, + const char __user *const buf, + const size_t len, const size_t offset) +{ + const struct counter_data *const data = &control->data; + int err; + u64 val = 0; + + err = copy_from_user((char *)&val + offset, buf, len); + if (err) + return err; + + switch (control->type) { + case COUNTER_OWNER_TYPE_DEVICE: + err = data->device_u64_write(counter, val); + break; + case COUNTER_OWNER_TYPE_SIGNAL: + err = data->signal_u64_write(counter, control->owner, val); + break; + case COUNTER_OWNER_TYPE_COUNT: + err = data->count_u64_write(counter, control->owner, val); + break; + default: return -EINVAL; + } + + return err; +} + +static int counter_data_string_write(struct counter_device *const counter, + const struct counter_control *const control, + const char __user *const buf, + const size_t len, const size_t offset) +{ + const struct counter_data *const data = &control->data; + int err; + char str[64] = ""; + + err = copy_from_user(str + offset, buf, len); + if (err) + return err; + + switch (control->type) { + case COUNTER_OWNER_TYPE_DEVICE: + err = data->device_string_write(counter, str, sizeof(str)); + break; + case COUNTER_OWNER_TYPE_SIGNAL: + err = data->signal_string_write(counter, control->owner, str, + sizeof(str)); + break; + case COUNTER_OWNER_TYPE_COUNT: + err = data->count_string_write(counter, control->owner, str, + sizeof(str)); + break; + default: return -EINVAL; + } + if (err < 0) + return err; + + return 0; +} + +static int counter_control_write(struct counter_device *const counter, + const struct counter_control *const control, + const char __user *const buf, const size_t len, + const size_t offset) +{ + /* Check if write operation is supported */ + if (!control->data.device_u8_write) + return -EFAULT; + + switch (control->data.type) { + case COUNTER_DATA_TYPE_U8: + case COUNTER_DATA_TYPE_BOOL: + case COUNTER_DATA_TYPE_SIGNAL: + case COUNTER_DATA_TYPE_COUNT_FUNCTION: + case COUNTER_DATA_TYPE_SYNAPSE_ACTION: + case COUNTER_DATA_TYPE_COUNT_DIRECTION: + case COUNTER_DATA_TYPE_COUNT_MODE: + return counter_data_u8_write(counter, control, buf); + case COUNTER_DATA_TYPE_U64: + return counter_data_u64_write(counter, control, buf, len, + offset); + case COUNTER_DATA_TYPE_STRING: + return counter_data_string_write(counter, control, buf, len, + offset); + } + + return -EINVAL; +} + +static ssize_t counter_chrdev_write(struct file *filp, const char __user *buf, + size_t len, loff_t *f_ps) +{ + const loff_t start_ps = *f_ps; + struct counter_device *const counter = filp->private_data; + size_t write_size; + struct counter_control *control; + int err; + loff_t next_control_ps; + + /* Handle Counter control selection */ + if (*f_ps < COUNTER_SELECTION_SIZE) { + write_size = (*f_ps + len > COUNTER_SELECTION_SIZE) ? + COUNTER_SELECTION_SIZE - *f_ps : len; + if (copy_from_user(counter->selection + *f_ps, buf, write_size)) + return -EFAULT; + + /* Create new list based on updated selection array */ + err = counter_control_list_create(counter); + if (err) + return err; + + *f_ps += write_size; + buf += write_size; + len -= write_size; + if (!len) + goto exit_chrdev_write; + } + + /* Handle controls */ + list_for_each_entry(control, &counter->control_list, l) { + next_control_ps = control->offset + control->size; + + /* Find first control item */ + if (*f_ps >= next_control_ps) + continue; + + write_size = (*f_ps + len > next_control_ps) ? + next_control_ps - *f_ps : len; + + err = counter_control_write(counter, control, buf, write_size, + control->offset - *f_ps); + if (err) + return err; + + *f_ps += write_size; + buf += write_size; + len -= write_size; + if (!len) + goto exit_chrdev_write; + } + + return -EFAULT; + +exit_chrdev_write: + return *f_ps - start_ps; +} + +static int counter_chrdev_open(struct inode *inode, struct file *filp) +{ + struct counter_device *const counter = container_of(inode->i_cdev, + typeof(*counter), + chrdev); + + get_device(&counter->dev); + filp->private_data = counter; + + return generic_file_open(inode, filp); +} + +static int counter_chrdev_release(struct inode *inode, struct file *filp) +{ + struct counter_device *const counter = container_of(inode->i_cdev, + typeof(*counter), + chrdev); + + put_device(&counter->dev); + + return 0; +} + +static const struct file_operations counter_fops = { + .llseek = generic_file_llseek, + .read = counter_chrdev_read, + .write = counter_chrdev_write, + .open = counter_chrdev_open, + .release = counter_chrdev_release, +}; + +int counter_chrdev_add(struct counter_device *const counter, + const dev_t counter_devt) +{ + struct device *const dev = &counter->dev; + struct cdev *const chrdev = &counter->chrdev; + + /* Initialize Counter character device control selection */ + memset(counter->selection, 0, sizeof(counter->selection)); + + /* Initialize Counter character device selected controls list */ + INIT_LIST_HEAD(&counter->control_list); + + /* Initialize character device */ + cdev_init(chrdev, &counter_fops); + dev->devt = MKDEV(MAJOR(counter_devt), counter->id); + cdev_set_parent(chrdev, &dev->kobj); + + return cdev_add(chrdev, dev->devt, 1); +} + +void counter_chrdev_free(struct counter_device *const counter) +{ + cdev_del(&counter->chrdev); + counter_control_list_free(&counter->control_list); +} diff --git a/drivers/counter/counter-chrdev.h b/drivers/counter/counter-chrdev.h new file mode 100644 index 000000000000..7ab0797d3857 --- /dev/null +++ b/drivers/counter/counter-chrdev.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Counter character device interface + * Copyright (C) 2020 William Breathitt Gray + */ +#ifndef _COUNTER_CHRDEV_H_ +#define _COUNTER_CHRDEV_H_ + +#include +#include + +int counter_chrdev_add(struct counter_device *const counter, + const dev_t counter_devt); +void counter_chrdev_free(struct counter_device *const counter); + +#endif /* _COUNTER_CHRDEV_H_ */ diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c index 499664809c75..2d0886648201 100644 --- a/drivers/counter/counter-core.c +++ b/drivers/counter/counter-core.c @@ -6,11 +6,14 @@ #include #include #include +#include #include #include #include #include +#include +#include "counter-chrdev.h" #include "counter-sysfs.h" /* Provides a unique ID for each counter device */ @@ -33,6 +36,8 @@ static struct bus_type counter_bus_type = { .name = "counter" }; +static dev_t counter_devt; + /** * counter_register - register Counter to the system * @counter: pointer to Counter to register @@ -62,10 +67,15 @@ int counter_register(struct counter_device *const counter) device_initialize(dev); dev_set_drvdata(dev, counter); + /* Add Counter character device */ + err = counter_chrdev_add(counter, counter_devt); + if (err) + goto err_free_id; + /* Add Counter sysfs attributes */ err = counter_sysfs_add(counter); if (err) - goto err_free_id; + goto err_free_chrdev; /* Add device to system */ err = device_add(dev); @@ -76,6 +86,8 @@ int counter_register(struct counter_device *const counter) err_free_sysfs: counter_sysfs_free(counter); +err_free_chrdev: + counter_chrdev_free(counter); err_free_id: ida_simple_remove(&counter_ida, counter->id); return err; @@ -93,6 +105,7 @@ void counter_unregister(struct counter_device *const counter) if (counter) { device_del(&counter->dev); counter_sysfs_free(counter); + counter_chrdev_free(counter); } } EXPORT_SYMBOL_GPL(counter_unregister); @@ -139,13 +152,30 @@ int devm_counter_register(struct device *dev, } EXPORT_SYMBOL_GPL(devm_counter_register); +#define COUNTER_DEV_MAX 256 + static int __init counter_init(void) { - return bus_register(&counter_bus_type); + int err; + + err = bus_register(&counter_bus_type); + if (err < 0) + return err; + + err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX, "counter"); + if (err < 0) + goto err_unregister_bus; + + return 0; + +err_unregister_bus: + bus_unregister(&counter_bus_type); + return err; } static void __exit counter_exit(void) { + unregister_chrdev_region(counter_devt, COUNTER_DEV_MAX); bus_unregister(&counter_bus_type); } diff --git a/include/linux/counter.h b/include/linux/counter.h index e8abbaf47a4b..22e91d00a880 100644 --- a/include/linux/counter.h +++ b/include/linux/counter.h @@ -6,6 +6,7 @@ #ifndef _COUNTER_H_ #define _COUNTER_H_ +#include #include #include #include @@ -170,6 +171,9 @@ struct counter_attribute_group { * @priv: optional private data supplied by driver * @id: unique ID used to identify the Counter * @dev: internal device structure + * @chrdev: internal character device structure + * @selection: Counter character device control selection + * @control_list: Counter character device selected controls * @dynamic_names_list: List for dynamic names * @groups_list: attribute groups list (for Signals, Counts, and ext) * @num_groups: number of attribute groups containers @@ -208,6 +212,18 @@ struct counter_device { int id; struct device dev; + struct cdev chrdev; + +#define MAX_EXT 255 +#define MAX_SIGNALS 255 +#define MAX_SYNAPSES 255 +#define MAX_COUNTS 255 +#define SIGNALS_SELECTION_SIZE ((1 + MAX_EXT) * MAX_SIGNALS) +#define COUNTS_SELECTION_SIZE ((1 + 1 + MAX_SYNAPSES + MAX_EXT) * MAX_COUNTS) +#define COUNTER_SELECTION_SIZE (MAX_EXT + SIGNALS_SELECTION_SIZE + COUNTS_SELECTION_SIZE) + u8 selection[COUNTER_SELECTION_SIZE]; + struct list_head control_list; + struct list_head dynamic_names_list; struct counter_attribute_group *groups_list; size_t num_groups; From patchwork Sat May 16 19:20:02 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: William Breathitt Gray X-Patchwork-Id: 11553725 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 44580912 for ; Sat, 16 May 2020 19:21:21 +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 18F5B20727 for ; Sat, 16 May 2020 19:21:21 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="fCGnR8w3"; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="fbL3q6av" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 18F5B20727 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+patchwork-linux-arm=patchwork.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:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version: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=8fg2V4Vk3ru7vqM92gzeVqD1A6XhmmLvMIdhtT/kU7s=; b=fCGnR8w3f/mm5o PZG/FPkh881pEjBz7UotLSKGyBMEX9bLytWuXHk8aper3GPDMvL/gREoL27VXxD7iS2NykviARrfg CDV1xvSSmSFyZVqNLtYQKmhFMGGVyFCWdIV5HW0Bpg/GZk2ujA5TaxNAQy8vtHL9uaVWagMwjV4cp he8UwUYfi614CAMUnR9Jd6o2D8q9c7GvfUPbSkZI6RU1S66kLSwJGraOd4W1iXaoXHR6b2zC8iO5q dqy6QzjudI7WF61lnrzp7UgmFX/q3kGxnsjIBsCqUknHiY1HPFz5btzlgk9n1YHBbzc+nSRUkLnD/ en67xSlRGbc6As1Chugg==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1ja2Mx-0008HS-4L; Sat, 16 May 2020 19:21:11 +0000 Received: from mail-qt1-x841.google.com ([2607:f8b0:4864:20::841]) by bombadil.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1ja2MX-0007rS-0W for linux-arm-kernel@lists.infradead.org; Sat, 16 May 2020 19:20:47 +0000 Received: by mail-qt1-x841.google.com with SMTP id o19so4859556qtr.10 for ; Sat, 16 May 2020 12:20:44 -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 :mime-version:content-transfer-encoding; bh=VwCAFe7MBCqJ2a3UC8HemYjtQtYkC6zmx8xai56rLfc=; b=fbL3q6avYx3M4P/cPgZcdIzEGnk6vehyBCli11hV3Wksa00cJmRvC5npEJwONngXvf M3obtU/uKlPmhiGi8MPXLeQdNY/fXaPcJqVDmdaxCm+qHAzhabob/nSgZ0AzEYI89GZV 3RxjJINcfVKvL/y40rhATW7mTT2sSC0CFUYXhBesM9gEqOz8HBeU3aT2lUX9xFmx5Lx1 vG26CYvW2Cy2OpUIG4cWmUg+78ydQZ4wEXREg+LhLlUqtFfYVpA+eZmD4+y2Enh3B2Av bJXgBWK49dxmEIQ3PCeyuFpY8uE801G7gfas4jkxeJioISFCsF6g9pQUI7Bk8zNC+2eA q3kA== 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:mime-version:content-transfer-encoding; bh=VwCAFe7MBCqJ2a3UC8HemYjtQtYkC6zmx8xai56rLfc=; b=Uk5eT2xlh1mViFGpHQ4j9UITHHymS3R4P3zTNShLmJMjnaNvvHxQhLWHCukcA6o1qn mL5fJ9jRo7+jXYYU99NXoWbZ6cIvOiF7v7iL1BjspHAa9vcXF/lRR9w5gm0fq6X89C9T V5fEj04MaKB9EIBoodFusepHUsMGoiY0gUoiN6kyEC1N1f1BxOaJSchV84Nfyoljal2W TQux9Tpqif9S0ReqHnayTUK7ksa9jWburNIi7jkyJHCHd/vIFNMO0QAU+ubr6QtqAW5v t1uxQjr2JKJ8cqPcqQycbiJ3OKbZU33HZ6PHS5sYkfycd1XbfniMHZ964BQU9QhX9kEL buLw== X-Gm-Message-State: AOAM530yQKwBJm71osJIlP8RUHEJeSPWN0S5fpTtSzD7s+0oASV7jUUc TUnGPmu3SC/Aa7tf8qPvj4cbzTsJe0SMcA== X-Google-Smtp-Source: ABdhPJzo2+BnZF7E3lsdOjU0oT5HoCFBnq5wOVRK4nYC/5f97QFj0Ggf8cSSDoGXGVI7c9IUtE053Q== X-Received: by 2002:ac8:7b35:: with SMTP id l21mr9455431qtu.21.1589656844149; Sat, 16 May 2020 12:20:44 -0700 (PDT) Received: from localhost.localdomain (072-189-064-225.res.spectrum.com. [72.189.64.225]) by smtp.gmail.com with ESMTPSA id g19sm3160409qke.32.2020.05.16.12.20.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 16 May 2020 12:20:43 -0700 (PDT) From: William Breathitt Gray To: jic23@kernel.org Subject: [PATCH v2 4/4] docs: counter: Document character device interface Date: Sat, 16 May 2020 15:20:02 -0400 Message-Id: X-Mailer: git-send-email 2.26.2 In-Reply-To: References: MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200516_122045_089548_7C4A84C4 X-CRM114-Status: GOOD ( 13.06 ) X-Spam-Score: -0.2 (/) X-Spam-Report: SpamAssassin version 3.4.4 on bombadil.infradead.org summary: Content analysis details: (-0.2 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at https://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:841 listed in] [list.dnswl.org] 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider [vilhelm.gray[at]gmail.com] 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.0 SPF_PASS SPF: sender matches SPF record 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kamel.bouhara@bootlin.com, gwendal@chromium.org, david@lechnology.com, linux-iio@vger.kernel.org, patrick.havelange@essensium.com, alexandre.belloni@bootlin.com, linux-kernel@vger.kernel.org, mcoquelin.stm32@gmail.com, William Breathitt Gray , fabrice.gasnier@st.com, syednwaris@gmail.com, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, alexandre.torgue@st.com Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org This patch adds high-level documentation about the Counter subsystem character device interface. Signed-off-by: William Breathitt Gray --- Documentation/driver-api/generic-counter.rst | 112 +++++++++++++------ 1 file changed, 76 insertions(+), 36 deletions(-) diff --git a/Documentation/driver-api/generic-counter.rst b/Documentation/driver-api/generic-counter.rst index 8f85c30dea0b..58045b33b576 100644 --- a/Documentation/driver-api/generic-counter.rst +++ b/Documentation/driver-api/generic-counter.rst @@ -223,19 +223,6 @@ whether an input line is differential or single-ended) and instead focus on the core idea of what the data and process represent (e.g. position as interpreted from quadrature encoding data). -Userspace Interface -=================== - -Several sysfs attributes are generated by the Generic Counter interface, -and reside under the /sys/bus/counter/devices/counterX directory, where -counterX refers to the respective counter device. Please see -Documentation/ABI/testing/sysfs-bus-counter for detailed -information on each Generic Counter interface sysfs attribute. - -Through these sysfs attributes, programs and scripts may interact with -the Generic Counter paradigm Counts, Signals, and Synapses of respective -counter devices. - Driver API ========== @@ -377,13 +364,13 @@ driver can be exemplified by the following:: +----------------------------+ | | Processes data from device | ------------------- |----------------------------| / driver callbacks / - | Type: unsigned long | ------------------- + | Type: u64 | ------------------- | Value: 42 | | +----------------------------+ | | | - ---------------- | - / unsigned long / | - ---------------- | + ---------- | + / u64 / | + ---------- | | | | V | +----------------------+ @@ -398,25 +385,32 @@ driver can be exemplified by the following:: | / driver callbacks / | ------------------- | | - +-------+ | + +-------+---------------+ | + | | | + | +-------|-------+ + | | | + V | V + +--------------------+ | +---------------------+ + | Counter sysfs |<-+->| Counter chrdev | + +--------------------+ +---------------------+ + | Translates to the | | Translates to the | + | standard Counter | | standard Counter | + | sysfs output | | character device | + |--------------------| |---------------------+ + | Type: const char * | | Type: u64 | + | Value: "42" | | Value: 42 | + +--------------------+ +---------------------+ | | - | +---------------+ - | | - V | - +--------------------+ | - | Counter sysfs |<-+ - +--------------------+ - | Translates to the | - | standard Counter | - | sysfs output | - |--------------------| - | Type: const char * | - | Value: "42" | - +--------------------+ - | - --------------- - / const char * / - --------------- + --------------- ---------- + / const char * / / u64 / + --------------- ---------- + | | + | V + | +-----------+ + | | read | + | +-----------+ + | \ Count: 42 / + | ----------- | V +--------------------------------------------------+ @@ -425,7 +419,7 @@ driver can be exemplified by the following:: \ Count: "42" / -------------------------------------------------- -There are three primary components involved: +There are four primary components involved: Counter device driver --------------------- @@ -445,3 +439,49 @@ and vice versa. Please refer to the `Documentation/ABI/testing/sysfs-bus-counter` file for a detailed breakdown of the available Generic Counter interface sysfs attributes. + +Counter chrdev +-------------- +Translates counter data to the standard Counter character device; data +is transferred via standard character device read/write calls. + +Sysfs Interface +=============== + +Several sysfs attributes are generated by the Generic Counter interface, +and reside under the `/sys/bus/counter/devices/counterX` directory, +where `X` is to the respective counter device id. Please see +Documentation/ABI/testing/sysfs-bus-counter for detailed information on +each Generic Counter interface sysfs attribute. + +Through these sysfs attributes, programs and scripts may interact with +the Generic Counter paradigm Counts, Signals, and Synapses of respective +counter devices. + +Counter Character Device +======================== + +Counter character device nodes are created under the `/dev` directory as +`counterX`, where `X` is the respective counter device id. Defines for +the standard Counter data types are exposed via the userspace +`include/uapi/linux/counter-types.h` file. + +The first 196095 bytes of the character device serve as a control +selection area where control exposure of desired Counter components and +extensions may be selected. Each byte serves as a boolean selection +indicator for a respective Counter component or extension. The format of +this area is as follows: + +* For each device extension, a byte is required. +* For each Signal, a byte is reserved for the Signal component, and a + byte is reserved for each Signal extension. +* For each Count, a byte is reserved for the Count component, a byte is + reserved for the count function, a byte is reserved for each Synapse + action, and byte is reserved for each Count extension. + +The selected Counter components and extensions may then be interfaced +after the first 196095 bytes via standard character device read/write +operations. The number of bytes available for each component or +extension is dependent on their respective data type: u8 will have 1 +byte available, u64 will have 8 bytes available, strings will have 64 +bytes available, etc.