From 1a79f22de8102678e15ef9789c161fefd00fc802 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 7 Nov 2017 14:58:50 +0100 Subject: staging: most: add SPDX identifiers to all most driver files It's good to have SPDX identifiers in all files to make it easier to audit the kernel tree for correct licenses. Update the drivers/staging/most files files with the correct SPDX license identifier based on the license text in the file itself. The SPDX identifier is a legally binding shorthand, which can be used instead of the full boiler plate text. This work is based on a script and data from Thomas Gleixner, Philippe Ombredanne, and Kate Stewart. Cc: Michael Fabry Cc: Christian Gromm Cc: Thomas Gleixner Cc: Kate Stewart Cc: Philippe Ombredanne Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/aim-cdev/cdev.c | 1 + drivers/staging/most/aim-network/networking.c | 1 + drivers/staging/most/aim-sound/sound.c | 1 + drivers/staging/most/aim-v4l2/video.c | 1 + drivers/staging/most/hdm-dim2/dim2_errors.h | 1 + drivers/staging/most/hdm-dim2/dim2_hal.c | 1 + drivers/staging/most/hdm-dim2/dim2_hal.h | 1 + drivers/staging/most/hdm-dim2/dim2_hdm.c | 1 + drivers/staging/most/hdm-dim2/dim2_hdm.h | 1 + drivers/staging/most/hdm-dim2/dim2_reg.h | 1 + drivers/staging/most/hdm-dim2/dim2_sysfs.c | 1 + drivers/staging/most/hdm-dim2/dim2_sysfs.h | 1 + drivers/staging/most/hdm-i2c/hdm_i2c.c | 1 + drivers/staging/most/hdm-usb/hdm_usb.c | 1 + drivers/staging/most/mostcore/core.c | 1 + drivers/staging/most/mostcore/mostcore.h | 1 + 16 files changed, 16 insertions(+) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/aim-cdev/cdev.c b/drivers/staging/most/aim-cdev/cdev.c index 1e5cbc893496..d6e7e85d9ece 100644 --- a/drivers/staging/most/aim-cdev/cdev.c +++ b/drivers/staging/most/aim-cdev/cdev.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * cdev.c - Application interfacing module for character devices * diff --git a/drivers/staging/most/aim-network/networking.c b/drivers/staging/most/aim-network/networking.c index 936f013c350e..e2dff6d5cf30 100644 --- a/drivers/staging/most/aim-network/networking.c +++ b/drivers/staging/most/aim-network/networking.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Networking AIM - Networking Application Interface Module for MostCore * diff --git a/drivers/staging/most/aim-sound/sound.c b/drivers/staging/most/aim-sound/sound.c index ea1366a44008..0f596ca532d8 100644 --- a/drivers/staging/most/aim-sound/sound.c +++ b/drivers/staging/most/aim-sound/sound.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * sound.c - Audio Application Interface Module for Mostcore * diff --git a/drivers/staging/most/aim-v4l2/video.c b/drivers/staging/most/aim-v4l2/video.c index e0748416aee5..e089d139b943 100644 --- a/drivers/staging/most/aim-v4l2/video.c +++ b/drivers/staging/most/aim-v4l2/video.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * V4L2 AIM - V4L2 Application Interface Module for MostCore * diff --git a/drivers/staging/most/hdm-dim2/dim2_errors.h b/drivers/staging/most/hdm-dim2/dim2_errors.h index 66343ba426c1..073d93cf4927 100644 --- a/drivers/staging/most/hdm-dim2/dim2_errors.h +++ b/drivers/staging/most/hdm-dim2/dim2_errors.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * dim2_errors.h - Definitions of errors for DIM2 HAL API * (MediaLB, Device Interface Macro IP, OS62420) diff --git a/drivers/staging/most/hdm-dim2/dim2_hal.c b/drivers/staging/most/hdm-dim2/dim2_hal.c index 91484643d289..f0a45863c8cb 100644 --- a/drivers/staging/most/hdm-dim2/dim2_hal.c +++ b/drivers/staging/most/hdm-dim2/dim2_hal.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * dim2_hal.c - DIM2 HAL implementation * (MediaLB, Device Interface Macro IP, OS62420) diff --git a/drivers/staging/most/hdm-dim2/dim2_hal.h b/drivers/staging/most/hdm-dim2/dim2_hal.h index 6df6ea5f7da4..df678124117c 100644 --- a/drivers/staging/most/hdm-dim2/dim2_hal.h +++ b/drivers/staging/most/hdm-dim2/dim2_hal.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * dim2_hal.h - DIM2 HAL interface * (MediaLB, Device Interface Macro IP, OS62420) diff --git a/drivers/staging/most/hdm-dim2/dim2_hdm.c b/drivers/staging/most/hdm-dim2/dim2_hdm.c index df7021c522b3..c59c8a526d06 100644 --- a/drivers/staging/most/hdm-dim2/dim2_hdm.c +++ b/drivers/staging/most/hdm-dim2/dim2_hdm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * dim2_hdm.c - MediaLB DIM2 Hardware Dependent Module * diff --git a/drivers/staging/most/hdm-dim2/dim2_hdm.h b/drivers/staging/most/hdm-dim2/dim2_hdm.h index 4050e7c764ed..dc0293a7be1f 100644 --- a/drivers/staging/most/hdm-dim2/dim2_hdm.h +++ b/drivers/staging/most/hdm-dim2/dim2_hdm.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * dim2_hdm.h - MediaLB DIM2 HDM Header * diff --git a/drivers/staging/most/hdm-dim2/dim2_reg.h b/drivers/staging/most/hdm-dim2/dim2_reg.h index f7d9fbcd29f2..c94220daec33 100644 --- a/drivers/staging/most/hdm-dim2/dim2_reg.h +++ b/drivers/staging/most/hdm-dim2/dim2_reg.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * dim2_reg.h - Definitions for registers of DIM2 * (MediaLB, Device Interface Macro IP, OS62420) diff --git a/drivers/staging/most/hdm-dim2/dim2_sysfs.c b/drivers/staging/most/hdm-dim2/dim2_sysfs.c index d8b22f91d2c3..144ab4281e31 100644 --- a/drivers/staging/most/hdm-dim2/dim2_sysfs.c +++ b/drivers/staging/most/hdm-dim2/dim2_sysfs.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * dim2_sysfs.c - MediaLB sysfs information * diff --git a/drivers/staging/most/hdm-dim2/dim2_sysfs.h b/drivers/staging/most/hdm-dim2/dim2_sysfs.h index b71dd027ebc7..50d17176a1b8 100644 --- a/drivers/staging/most/hdm-dim2/dim2_sysfs.h +++ b/drivers/staging/most/hdm-dim2/dim2_sysfs.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * dim2_sysfs.h - MediaLB sysfs information * diff --git a/drivers/staging/most/hdm-i2c/hdm_i2c.c b/drivers/staging/most/hdm-i2c/hdm_i2c.c index 2b4de404e46a..6272353dc8c3 100644 --- a/drivers/staging/most/hdm-i2c/hdm_i2c.c +++ b/drivers/staging/most/hdm-i2c/hdm_i2c.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * hdm_i2c.c - Hardware Dependent Module for I2C Interface * diff --git a/drivers/staging/most/hdm-usb/hdm_usb.c b/drivers/staging/most/hdm-usb/hdm_usb.c index 667dacac81f0..368e0718bd6e 100644 --- a/drivers/staging/most/hdm-usb/hdm_usb.c +++ b/drivers/staging/most/hdm-usb/hdm_usb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * hdm_usb.c - Hardware dependent module for USB * diff --git a/drivers/staging/most/mostcore/core.c b/drivers/staging/most/mostcore/core.c index 069269db394c..80040849160d 100644 --- a/drivers/staging/most/mostcore/core.c +++ b/drivers/staging/most/mostcore/core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * core.c - Implementation of core module of MOST Linux driver stack * diff --git a/drivers/staging/most/mostcore/mostcore.h b/drivers/staging/most/mostcore/mostcore.h index 915e5159d1eb..6f15590417f5 100644 --- a/drivers/staging/most/mostcore/mostcore.h +++ b/drivers/staging/most/mostcore/mostcore.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * mostcore.h - Interface between MostCore, * Hardware Dependent Module (HDM) and Application Interface Module (AIM). -- cgit From c1dcb35bf18e8644f9487f5cd15e15fdd0bf1142 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 7 Nov 2017 14:58:51 +0100 Subject: staging: most: Remove redundant license text Now that the SPDX tag is in all most driver files, that identifies the license in a specific and legally-defined manner. So the extra GPL text wording can be removed as it is no longer needed at all. This is done on a quest to remove the 700+ different ways that files in the kernel describe the GPL license text. And there's unneeded stuff like the address (sometimes incorrect) for the FSF which is never needed. No copyright headers or other non-license-description text was removed. Cc: Christian Gromm Cc: Michael Fabry Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/aim-cdev/cdev.c | 7 ------- drivers/staging/most/aim-network/networking.c | 7 ------- drivers/staging/most/aim-sound/sound.c | 7 ------- drivers/staging/most/aim-v4l2/video.c | 7 ------- drivers/staging/most/hdm-dim2/dim2_errors.h | 7 ------- drivers/staging/most/hdm-dim2/dim2_hal.c | 7 ------- drivers/staging/most/hdm-dim2/dim2_hal.h | 7 ------- drivers/staging/most/hdm-dim2/dim2_hdm.c | 7 ------- drivers/staging/most/hdm-dim2/dim2_hdm.h | 7 ------- drivers/staging/most/hdm-dim2/dim2_reg.h | 7 ------- drivers/staging/most/hdm-dim2/dim2_sysfs.c | 7 ------- drivers/staging/most/hdm-dim2/dim2_sysfs.h | 7 ------- drivers/staging/most/hdm-i2c/hdm_i2c.c | 7 ------- drivers/staging/most/hdm-usb/hdm_usb.c | 7 ------- drivers/staging/most/mostcore/core.c | 7 ------- drivers/staging/most/mostcore/mostcore.h | 7 ------- 16 files changed, 112 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/aim-cdev/cdev.c b/drivers/staging/most/aim-cdev/cdev.c index d6e7e85d9ece..5010c7bf8772 100644 --- a/drivers/staging/most/aim-cdev/cdev.c +++ b/drivers/staging/most/aim-cdev/cdev.c @@ -3,13 +3,6 @@ * cdev.c - Application interfacing module for character devices * * Copyright (C) 2013-2015 Microchip Technology Germany II GmbH & Co. KG - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This file is licensed under GPLv2. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/staging/most/aim-network/networking.c b/drivers/staging/most/aim-network/networking.c index e2dff6d5cf30..d98eb893d44c 100644 --- a/drivers/staging/most/aim-network/networking.c +++ b/drivers/staging/most/aim-network/networking.c @@ -3,13 +3,6 @@ * Networking AIM - Networking Application Interface Module for MostCore * * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This file is licensed under GPLv2. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/staging/most/aim-sound/sound.c b/drivers/staging/most/aim-sound/sound.c index 0f596ca532d8..d84a6e1b56a1 100644 --- a/drivers/staging/most/aim-sound/sound.c +++ b/drivers/staging/most/aim-sound/sound.c @@ -3,13 +3,6 @@ * sound.c - Audio Application Interface Module for Mostcore * * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This file is licensed under GPLv2. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/staging/most/aim-v4l2/video.c b/drivers/staging/most/aim-v4l2/video.c index e089d139b943..72e9a9a5d8b3 100644 --- a/drivers/staging/most/aim-v4l2/video.c +++ b/drivers/staging/most/aim-v4l2/video.c @@ -3,13 +3,6 @@ * V4L2 AIM - V4L2 Application Interface Module for MostCore * * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This file is licensed under GPLv2. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/staging/most/hdm-dim2/dim2_errors.h b/drivers/staging/most/hdm-dim2/dim2_errors.h index 073d93cf4927..8b90196076d5 100644 --- a/drivers/staging/most/hdm-dim2/dim2_errors.h +++ b/drivers/staging/most/hdm-dim2/dim2_errors.h @@ -4,13 +4,6 @@ * (MediaLB, Device Interface Macro IP, OS62420) * * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This file is licensed under GPLv2. */ #ifndef _MOST_DIM_ERRORS_H diff --git a/drivers/staging/most/hdm-dim2/dim2_hal.c b/drivers/staging/most/hdm-dim2/dim2_hal.c index f0a45863c8cb..f98ac935729c 100644 --- a/drivers/staging/most/hdm-dim2/dim2_hal.c +++ b/drivers/staging/most/hdm-dim2/dim2_hal.c @@ -4,13 +4,6 @@ * (MediaLB, Device Interface Macro IP, OS62420) * * Copyright (C) 2015-2016, Microchip Technology Germany II GmbH & Co. KG - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This file is licensed under GPLv2. */ /* Author: Andrey Shvetsov */ diff --git a/drivers/staging/most/hdm-dim2/dim2_hal.h b/drivers/staging/most/hdm-dim2/dim2_hal.h index df678124117c..fce9ae96121b 100644 --- a/drivers/staging/most/hdm-dim2/dim2_hal.h +++ b/drivers/staging/most/hdm-dim2/dim2_hal.h @@ -4,13 +4,6 @@ * (MediaLB, Device Interface Macro IP, OS62420) * * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This file is licensed under GPLv2. */ #ifndef _DIM2_HAL_H diff --git a/drivers/staging/most/hdm-dim2/dim2_hdm.c b/drivers/staging/most/hdm-dim2/dim2_hdm.c index c59c8a526d06..312f4f75ef06 100644 --- a/drivers/staging/most/hdm-dim2/dim2_hdm.c +++ b/drivers/staging/most/hdm-dim2/dim2_hdm.c @@ -3,13 +3,6 @@ * dim2_hdm.c - MediaLB DIM2 Hardware Dependent Module * * Copyright (C) 2015-2016, Microchip Technology Germany II GmbH & Co. KG - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This file is licensed under GPLv2. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/staging/most/hdm-dim2/dim2_hdm.h b/drivers/staging/most/hdm-dim2/dim2_hdm.h index dc0293a7be1f..5f380b648bd7 100644 --- a/drivers/staging/most/hdm-dim2/dim2_hdm.h +++ b/drivers/staging/most/hdm-dim2/dim2_hdm.h @@ -3,13 +3,6 @@ * dim2_hdm.h - MediaLB DIM2 HDM Header * * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This file is licensed under GPLv2. */ #ifndef DIM2_HDM_H diff --git a/drivers/staging/most/hdm-dim2/dim2_reg.h b/drivers/staging/most/hdm-dim2/dim2_reg.h index c94220daec33..2b2fca4f6451 100644 --- a/drivers/staging/most/hdm-dim2/dim2_reg.h +++ b/drivers/staging/most/hdm-dim2/dim2_reg.h @@ -4,13 +4,6 @@ * (MediaLB, Device Interface Macro IP, OS62420) * * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This file is licensed under GPLv2. */ #ifndef DIM2_OS62420_H diff --git a/drivers/staging/most/hdm-dim2/dim2_sysfs.c b/drivers/staging/most/hdm-dim2/dim2_sysfs.c index 144ab4281e31..3a2ad355cab1 100644 --- a/drivers/staging/most/hdm-dim2/dim2_sysfs.c +++ b/drivers/staging/most/hdm-dim2/dim2_sysfs.c @@ -3,13 +3,6 @@ * dim2_sysfs.c - MediaLB sysfs information * * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This file is licensed under GPLv2. */ /* Author: Andrey Shvetsov */ diff --git a/drivers/staging/most/hdm-dim2/dim2_sysfs.h b/drivers/staging/most/hdm-dim2/dim2_sysfs.h index 50d17176a1b8..e46dc4ba3946 100644 --- a/drivers/staging/most/hdm-dim2/dim2_sysfs.h +++ b/drivers/staging/most/hdm-dim2/dim2_sysfs.h @@ -3,13 +3,6 @@ * dim2_sysfs.h - MediaLB sysfs information * * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This file is licensed under GPLv2. */ /* Author: Andrey Shvetsov */ diff --git a/drivers/staging/most/hdm-i2c/hdm_i2c.c b/drivers/staging/most/hdm-i2c/hdm_i2c.c index 6272353dc8c3..ed6793a1bc93 100644 --- a/drivers/staging/most/hdm-i2c/hdm_i2c.c +++ b/drivers/staging/most/hdm-i2c/hdm_i2c.c @@ -3,13 +3,6 @@ * hdm_i2c.c - Hardware Dependent Module for I2C Interface * * Copyright (C) 2013-2015, Microchip Technology Germany II GmbH & Co. KG - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This file is licensed under GPLv2. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/staging/most/hdm-usb/hdm_usb.c b/drivers/staging/most/hdm-usb/hdm_usb.c index 368e0718bd6e..c01b93bb134f 100644 --- a/drivers/staging/most/hdm-usb/hdm_usb.c +++ b/drivers/staging/most/hdm-usb/hdm_usb.c @@ -3,13 +3,6 @@ * hdm_usb.c - Hardware dependent module for USB * * Copyright (C) 2013-2015 Microchip Technology Germany II GmbH & Co. KG - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This file is licensed under GPLv2. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/staging/most/mostcore/core.c b/drivers/staging/most/mostcore/core.c index 80040849160d..cb70e1029a93 100644 --- a/drivers/staging/most/mostcore/core.c +++ b/drivers/staging/most/mostcore/core.c @@ -3,13 +3,6 @@ * core.c - Implementation of core module of MOST Linux driver stack * * Copyright (C) 2013-2015 Microchip Technology Germany II GmbH & Co. KG - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This file is licensed under GPLv2. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/staging/most/mostcore/mostcore.h b/drivers/staging/most/mostcore/mostcore.h index 6f15590417f5..f6fdba0c5eaf 100644 --- a/drivers/staging/most/mostcore/mostcore.h +++ b/drivers/staging/most/mostcore/mostcore.h @@ -4,13 +4,6 @@ * Hardware Dependent Module (HDM) and Application Interface Module (AIM). * * Copyright (C) 2013-2015, Microchip Technology Germany II GmbH & Co. KG - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This file is licensed under GPLv2. */ /* -- cgit From 057301cd972e44fa97b4834d9421878fe5edc059 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:35 +0100 Subject: staging: most: move core files This patch moves the core files to the root dir of the driver. This is needed to clean up the directory layout. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/Kconfig | 13 +- drivers/staging/most/Makefile | 5 +- drivers/staging/most/aim-cdev/Makefile | 2 +- drivers/staging/most/aim-cdev/cdev.c | 2 +- drivers/staging/most/aim-network/Makefile | 2 +- drivers/staging/most/aim-network/networking.c | 2 +- drivers/staging/most/aim-sound/Makefile | 2 +- drivers/staging/most/aim-sound/sound.c | 2 +- drivers/staging/most/aim-v4l2/Makefile | 2 +- drivers/staging/most/aim-v4l2/video.c | 2 +- drivers/staging/most/core.c | 1943 +++++++++++++++++++++++++ drivers/staging/most/core.h | 319 ++++ drivers/staging/most/hdm-dim2/Makefile | 2 +- drivers/staging/most/hdm-dim2/dim2_hdm.c | 2 +- drivers/staging/most/hdm-i2c/Makefile | 2 +- drivers/staging/most/hdm-i2c/hdm_i2c.c | 2 +- drivers/staging/most/hdm-usb/Makefile | 2 +- drivers/staging/most/hdm-usb/hdm_usb.c | 2 +- drivers/staging/most/mostcore/Kconfig | 14 - drivers/staging/most/mostcore/Makefile | 3 - drivers/staging/most/mostcore/core.c | 1943 ------------------------- drivers/staging/most/mostcore/mostcore.h | 319 ---- 22 files changed, 2288 insertions(+), 2299 deletions(-) create mode 100644 drivers/staging/most/core.c create mode 100644 drivers/staging/most/core.h delete mode 100644 drivers/staging/most/mostcore/Kconfig delete mode 100644 drivers/staging/most/mostcore/Makefile delete mode 100644 drivers/staging/most/mostcore/core.c delete mode 100644 drivers/staging/most/mostcore/mostcore.h (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/Kconfig b/drivers/staging/most/Kconfig index 0b9b9b539f70..2045f7ac5081 100644 --- a/drivers/staging/most/Kconfig +++ b/drivers/staging/most/Kconfig @@ -1,10 +1,15 @@ menuconfig MOST - tristate "MOST driver" + tristate "MOST support" depends on HAS_DMA - select MOSTCORE default n ---help--- - This option allows you to enable support for MOST Network transceivers. + Say Y here if you want to enable MOST support. + This driver needs at least one additional component to enable the + desired access from userspace (e.g. character devices) and one that + matches the network controller's hardware interface (e.g. USB). + + To compile this driver as a module, choose M here: the + module will be called most_core. If in doubt, say N here. @@ -12,8 +17,6 @@ menuconfig MOST if MOST -source "drivers/staging/most/mostcore/Kconfig" - source "drivers/staging/most/aim-cdev/Kconfig" source "drivers/staging/most/aim-network/Kconfig" diff --git a/drivers/staging/most/Makefile b/drivers/staging/most/Makefile index f5bbb9deaab5..7f6aa9c515fa 100644 --- a/drivers/staging/most/Makefile +++ b/drivers/staging/most/Makefile @@ -1,5 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_MOSTCORE) += mostcore/ +obj-$(CONFIG_MOST) += most_core.o +most_core-y := core.o +ccflags-y += -Idrivers/staging/ + obj-$(CONFIG_AIM_CDEV) += aim-cdev/ obj-$(CONFIG_AIM_NETWORK) += aim-network/ obj-$(CONFIG_AIM_SOUND) += aim-sound/ diff --git a/drivers/staging/most/aim-cdev/Makefile b/drivers/staging/most/aim-cdev/Makefile index 0bcc6c637b75..b7afcb40997d 100644 --- a/drivers/staging/most/aim-cdev/Makefile +++ b/drivers/staging/most/aim-cdev/Makefile @@ -1,4 +1,4 @@ obj-$(CONFIG_AIM_CDEV) += aim_cdev.o aim_cdev-objs := cdev.o -ccflags-y += -Idrivers/staging/most/mostcore/ \ No newline at end of file +ccflags-y += -Idrivers/staging/ diff --git a/drivers/staging/most/aim-cdev/cdev.c b/drivers/staging/most/aim-cdev/cdev.c index 5010c7bf8772..3ae9246c0c5c 100644 --- a/drivers/staging/most/aim-cdev/cdev.c +++ b/drivers/staging/most/aim-cdev/cdev.c @@ -16,7 +16,7 @@ #include #include #include -#include "mostcore.h" +#include "most/core.h" static dev_t aim_devno; static struct class *aim_class; diff --git a/drivers/staging/most/aim-network/Makefile b/drivers/staging/most/aim-network/Makefile index 840c1dd94873..a874aac8d285 100644 --- a/drivers/staging/most/aim-network/Makefile +++ b/drivers/staging/most/aim-network/Makefile @@ -1,4 +1,4 @@ obj-$(CONFIG_AIM_NETWORK) += aim_network.o aim_network-objs := networking.o -ccflags-y += -Idrivers/staging/most/mostcore/ +ccflags-y += -Idrivers/staging/ diff --git a/drivers/staging/most/aim-network/networking.c b/drivers/staging/most/aim-network/networking.c index d98eb893d44c..5e082d7d0e82 100644 --- a/drivers/staging/most/aim-network/networking.c +++ b/drivers/staging/most/aim-network/networking.c @@ -15,7 +15,7 @@ #include #include #include -#include "mostcore.h" +#include "most/core.h" #define MEP_HDR_LEN 8 #define MDP_HDR_LEN 16 diff --git a/drivers/staging/most/aim-sound/Makefile b/drivers/staging/most/aim-sound/Makefile index beba9586fd28..d41b85baa83f 100644 --- a/drivers/staging/most/aim-sound/Makefile +++ b/drivers/staging/most/aim-sound/Makefile @@ -1,4 +1,4 @@ obj-$(CONFIG_AIM_SOUND) += aim_sound.o aim_sound-objs := sound.o -ccflags-y += -Idrivers/staging/most/mostcore/ +ccflags-y += -Idrivers/staging/ diff --git a/drivers/staging/most/aim-sound/sound.c b/drivers/staging/most/aim-sound/sound.c index d84a6e1b56a1..5826f710c925 100644 --- a/drivers/staging/most/aim-sound/sound.c +++ b/drivers/staging/most/aim-sound/sound.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #define DRIVER_NAME "sound" diff --git a/drivers/staging/most/aim-v4l2/Makefile b/drivers/staging/most/aim-v4l2/Makefile index 69a7524b466c..a8e8b4930355 100644 --- a/drivers/staging/most/aim-v4l2/Makefile +++ b/drivers/staging/most/aim-v4l2/Makefile @@ -2,4 +2,4 @@ obj-$(CONFIG_AIM_V4L2) += aim_v4l2.o aim_v4l2-objs := video.o -ccflags-y += -Idrivers/staging/most/mostcore/ +ccflags-y += -Idrivers/staging/ diff --git a/drivers/staging/most/aim-v4l2/video.c b/drivers/staging/most/aim-v4l2/video.c index 72e9a9a5d8b3..3c813ed7f3ec 100644 --- a/drivers/staging/most/aim-v4l2/video.c +++ b/drivers/staging/most/aim-v4l2/video.c @@ -21,7 +21,7 @@ #include #include -#include "mostcore.h" +#include "most/core.h" #define V4L2_AIM_MAX_INPUT 1 diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c new file mode 100644 index 000000000000..8fe3f2ecd4e4 --- /dev/null +++ b/drivers/staging/most/core.c @@ -0,0 +1,1943 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * core.c - Implementation of core module of MOST Linux driver stack + * + * Copyright (C) 2013-2015 Microchip Technology Germany II GmbH & Co. KG + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_CHANNELS 64 +#define STRING_SIZE 80 + +static struct class *most_class; +static struct device *core_dev; +static struct ida mdev_id; +static int dummy_num_buffers; + +struct most_c_aim_obj { + struct most_aim *ptr; + int refs; + int num_buffers; +}; + +struct most_c_obj { + struct kobject kobj; + struct completion cleanup; + atomic_t mbo_ref; + atomic_t mbo_nq_level; + u16 channel_id; + bool is_poisoned; + struct mutex start_mutex; + struct mutex nq_mutex; /* nq thread synchronization */ + int is_starving; + struct most_interface *iface; + struct most_inst_obj *inst; + struct most_channel_config cfg; + bool keep_mbo; + bool enqueue_halt; + struct list_head fifo; + spinlock_t fifo_lock; + struct list_head halt_fifo; + struct list_head list; + struct most_c_aim_obj aim0; + struct most_c_aim_obj aim1; + struct list_head trash_fifo; + struct task_struct *hdm_enqueue_task; + wait_queue_head_t hdm_fifo_wq; +}; + +#define to_c_obj(d) container_of(d, struct most_c_obj, kobj) + +struct most_inst_obj { + int dev_id; + struct most_interface *iface; + struct list_head channel_list; + struct most_c_obj *channel[MAX_CHANNELS]; + struct kobject kobj; + struct list_head list; +}; + +static const struct { + int most_ch_data_type; + const char *name; +} ch_data_type[] = { + { MOST_CH_CONTROL, "control\n" }, + { MOST_CH_ASYNC, "async\n" }, + { MOST_CH_SYNC, "sync\n" }, + { MOST_CH_ISOC, "isoc\n"}, + { MOST_CH_ISOC, "isoc_avp\n"}, +}; + +#define to_inst_obj(d) container_of(d, struct most_inst_obj, kobj) + +/** + * list_pop_mbo - retrieves the first MBO of the list and removes it + * @ptr: the list head to grab the MBO from. + */ +#define list_pop_mbo(ptr) \ +({ \ + struct mbo *_mbo = list_first_entry(ptr, struct mbo, list); \ + list_del(&_mbo->list); \ + _mbo; \ +}) + +/* ___ ___ + * ___C H A N N E L___ + */ + +/** + * struct most_c_attr - to access the attributes of a channel object + * @attr: attributes of a channel + * @show: pointer to the show function + * @store: pointer to the store function + */ +struct most_c_attr { + struct attribute attr; + ssize_t (*show)(struct most_c_obj *d, + struct most_c_attr *attr, + char *buf); + ssize_t (*store)(struct most_c_obj *d, + struct most_c_attr *attr, + const char *buf, + size_t count); +}; + +#define to_channel_attr(a) container_of(a, struct most_c_attr, attr) + +/** + * channel_attr_show - show function of channel object + * @kobj: pointer to its kobject + * @attr: pointer to its attributes + * @buf: buffer + */ +static ssize_t channel_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct most_c_attr *channel_attr = to_channel_attr(attr); + struct most_c_obj *c_obj = to_c_obj(kobj); + + if (!channel_attr->show) + return -EIO; + + return channel_attr->show(c_obj, channel_attr, buf); +} + +/** + * channel_attr_store - store function of channel object + * @kobj: pointer to its kobject + * @attr: pointer to its attributes + * @buf: buffer + * @len: length of buffer + */ +static ssize_t channel_attr_store(struct kobject *kobj, + struct attribute *attr, + const char *buf, + size_t len) +{ + struct most_c_attr *channel_attr = to_channel_attr(attr); + struct most_c_obj *c_obj = to_c_obj(kobj); + + if (!channel_attr->store) + return -EIO; + return channel_attr->store(c_obj, channel_attr, buf, len); +} + +static const struct sysfs_ops most_channel_sysfs_ops = { + .show = channel_attr_show, + .store = channel_attr_store, +}; + +/** + * most_free_mbo_coherent - free an MBO and its coherent buffer + * @mbo: buffer to be released + * + */ +static void most_free_mbo_coherent(struct mbo *mbo) +{ + struct most_c_obj *c = mbo->context; + u16 const coherent_buf_size = c->cfg.buffer_size + c->cfg.extra_len; + + dma_free_coherent(NULL, coherent_buf_size, mbo->virt_address, + mbo->bus_address); + kfree(mbo); + if (atomic_sub_and_test(1, &c->mbo_ref)) + complete(&c->cleanup); +} + +/** + * flush_channel_fifos - clear the channel fifos + * @c: pointer to channel object + */ +static void flush_channel_fifos(struct most_c_obj *c) +{ + unsigned long flags, hf_flags; + struct mbo *mbo, *tmp; + + if (list_empty(&c->fifo) && list_empty(&c->halt_fifo)) + return; + + spin_lock_irqsave(&c->fifo_lock, flags); + list_for_each_entry_safe(mbo, tmp, &c->fifo, list) { + list_del(&mbo->list); + spin_unlock_irqrestore(&c->fifo_lock, flags); + most_free_mbo_coherent(mbo); + spin_lock_irqsave(&c->fifo_lock, flags); + } + spin_unlock_irqrestore(&c->fifo_lock, flags); + + spin_lock_irqsave(&c->fifo_lock, hf_flags); + list_for_each_entry_safe(mbo, tmp, &c->halt_fifo, list) { + list_del(&mbo->list); + spin_unlock_irqrestore(&c->fifo_lock, hf_flags); + most_free_mbo_coherent(mbo); + spin_lock_irqsave(&c->fifo_lock, hf_flags); + } + spin_unlock_irqrestore(&c->fifo_lock, hf_flags); + + if (unlikely((!list_empty(&c->fifo) || !list_empty(&c->halt_fifo)))) + pr_info("WARN: fifo | trash fifo not empty\n"); +} + +/** + * flush_trash_fifo - clear the trash fifo + * @c: pointer to channel object + */ +static int flush_trash_fifo(struct most_c_obj *c) +{ + struct mbo *mbo, *tmp; + unsigned long flags; + + spin_lock_irqsave(&c->fifo_lock, flags); + list_for_each_entry_safe(mbo, tmp, &c->trash_fifo, list) { + list_del(&mbo->list); + spin_unlock_irqrestore(&c->fifo_lock, flags); + most_free_mbo_coherent(mbo); + spin_lock_irqsave(&c->fifo_lock, flags); + } + spin_unlock_irqrestore(&c->fifo_lock, flags); + return 0; +} + +/** + * most_channel_release - release function of channel object + * @kobj: pointer to channel's kobject + */ +static void most_channel_release(struct kobject *kobj) +{ + struct most_c_obj *c = to_c_obj(kobj); + + kfree(c); +} + +static ssize_t available_directions_show(struct most_c_obj *c, + struct most_c_attr *attr, + char *buf) +{ + unsigned int i = c->channel_id; + + strcpy(buf, ""); + if (c->iface->channel_vector[i].direction & MOST_CH_RX) + strcat(buf, "rx "); + if (c->iface->channel_vector[i].direction & MOST_CH_TX) + strcat(buf, "tx "); + strcat(buf, "\n"); + return strlen(buf); +} + +static ssize_t available_datatypes_show(struct most_c_obj *c, + struct most_c_attr *attr, + char *buf) +{ + unsigned int i = c->channel_id; + + strcpy(buf, ""); + if (c->iface->channel_vector[i].data_type & MOST_CH_CONTROL) + strcat(buf, "control "); + if (c->iface->channel_vector[i].data_type & MOST_CH_ASYNC) + strcat(buf, "async "); + if (c->iface->channel_vector[i].data_type & MOST_CH_SYNC) + strcat(buf, "sync "); + if (c->iface->channel_vector[i].data_type & MOST_CH_ISOC) + strcat(buf, "isoc "); + strcat(buf, "\n"); + return strlen(buf); +} + +static ssize_t number_of_packet_buffers_show(struct most_c_obj *c, + struct most_c_attr *attr, + char *buf) +{ + unsigned int i = c->channel_id; + + return snprintf(buf, PAGE_SIZE, "%d\n", + c->iface->channel_vector[i].num_buffers_packet); +} + +static ssize_t number_of_stream_buffers_show(struct most_c_obj *c, + struct most_c_attr *attr, + char *buf) +{ + unsigned int i = c->channel_id; + + return snprintf(buf, PAGE_SIZE, "%d\n", + c->iface->channel_vector[i].num_buffers_streaming); +} + +static ssize_t size_of_packet_buffer_show(struct most_c_obj *c, + struct most_c_attr *attr, + char *buf) +{ + unsigned int i = c->channel_id; + + return snprintf(buf, PAGE_SIZE, "%d\n", + c->iface->channel_vector[i].buffer_size_packet); +} + +static ssize_t size_of_stream_buffer_show(struct most_c_obj *c, + struct most_c_attr *attr, + char *buf) +{ + unsigned int i = c->channel_id; + + return snprintf(buf, PAGE_SIZE, "%d\n", + c->iface->channel_vector[i].buffer_size_streaming); +} + +static ssize_t channel_starving_show(struct most_c_obj *c, + struct most_c_attr *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", c->is_starving); +} + +static ssize_t set_number_of_buffers_show(struct most_c_obj *c, + struct most_c_attr *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.num_buffers); +} + +static ssize_t set_number_of_buffers_store(struct most_c_obj *c, + struct most_c_attr *attr, + const char *buf, + size_t count) +{ + int ret = kstrtou16(buf, 0, &c->cfg.num_buffers); + + if (ret) + return ret; + return count; +} + +static ssize_t set_buffer_size_show(struct most_c_obj *c, + struct most_c_attr *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.buffer_size); +} + +static ssize_t set_buffer_size_store(struct most_c_obj *c, + struct most_c_attr *attr, + const char *buf, + size_t count) +{ + int ret = kstrtou16(buf, 0, &c->cfg.buffer_size); + + if (ret) + return ret; + return count; +} + +static ssize_t set_direction_show(struct most_c_obj *c, + struct most_c_attr *attr, + char *buf) +{ + if (c->cfg.direction & MOST_CH_TX) + return snprintf(buf, PAGE_SIZE, "tx\n"); + else if (c->cfg.direction & MOST_CH_RX) + return snprintf(buf, PAGE_SIZE, "rx\n"); + return snprintf(buf, PAGE_SIZE, "unconfigured\n"); +} + +static ssize_t set_direction_store(struct most_c_obj *c, + struct most_c_attr *attr, + const char *buf, + size_t count) +{ + if (!strcmp(buf, "dir_rx\n")) { + c->cfg.direction = MOST_CH_RX; + } else if (!strcmp(buf, "rx\n")) { + c->cfg.direction = MOST_CH_RX; + } else if (!strcmp(buf, "dir_tx\n")) { + c->cfg.direction = MOST_CH_TX; + } else if (!strcmp(buf, "tx\n")) { + c->cfg.direction = MOST_CH_TX; + } else { + pr_info("WARN: invalid attribute settings\n"); + return -EINVAL; + } + return count; +} + +static ssize_t set_datatype_show(struct most_c_obj *c, + struct most_c_attr *attr, + char *buf) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ch_data_type); i++) { + if (c->cfg.data_type & ch_data_type[i].most_ch_data_type) + return snprintf(buf, PAGE_SIZE, ch_data_type[i].name); + } + return snprintf(buf, PAGE_SIZE, "unconfigured\n"); +} + +static ssize_t set_datatype_store(struct most_c_obj *c, + struct most_c_attr *attr, + const char *buf, + size_t count) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ch_data_type); i++) { + if (!strcmp(buf, ch_data_type[i].name)) { + c->cfg.data_type = ch_data_type[i].most_ch_data_type; + break; + } + } + + if (i == ARRAY_SIZE(ch_data_type)) { + pr_info("WARN: invalid attribute settings\n"); + return -EINVAL; + } + return count; +} + +static ssize_t set_subbuffer_size_show(struct most_c_obj *c, + struct most_c_attr *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.subbuffer_size); +} + +static ssize_t set_subbuffer_size_store(struct most_c_obj *c, + struct most_c_attr *attr, + const char *buf, + size_t count) +{ + int ret = kstrtou16(buf, 0, &c->cfg.subbuffer_size); + + if (ret) + return ret; + return count; +} + +static ssize_t set_packets_per_xact_show(struct most_c_obj *c, + struct most_c_attr *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.packets_per_xact); +} + +static ssize_t set_packets_per_xact_store(struct most_c_obj *c, + struct most_c_attr *attr, + const char *buf, + size_t count) +{ + int ret = kstrtou16(buf, 0, &c->cfg.packets_per_xact); + + if (ret) + return ret; + return count; +} + +static struct most_c_attr most_c_attrs[] = { + __ATTR_RO(available_directions), + __ATTR_RO(available_datatypes), + __ATTR_RO(number_of_packet_buffers), + __ATTR_RO(number_of_stream_buffers), + __ATTR_RO(size_of_stream_buffer), + __ATTR_RO(size_of_packet_buffer), + __ATTR_RO(channel_starving), + __ATTR_RW(set_buffer_size), + __ATTR_RW(set_number_of_buffers), + __ATTR_RW(set_direction), + __ATTR_RW(set_datatype), + __ATTR_RW(set_subbuffer_size), + __ATTR_RW(set_packets_per_xact), +}; + +/** + * most_channel_def_attrs - array of default attributes of channel object + */ +static struct attribute *most_channel_def_attrs[] = { + &most_c_attrs[0].attr, + &most_c_attrs[1].attr, + &most_c_attrs[2].attr, + &most_c_attrs[3].attr, + &most_c_attrs[4].attr, + &most_c_attrs[5].attr, + &most_c_attrs[6].attr, + &most_c_attrs[7].attr, + &most_c_attrs[8].attr, + &most_c_attrs[9].attr, + &most_c_attrs[10].attr, + &most_c_attrs[11].attr, + &most_c_attrs[12].attr, + NULL, +}; + +static struct kobj_type most_channel_ktype = { + .sysfs_ops = &most_channel_sysfs_ops, + .release = most_channel_release, + .default_attrs = most_channel_def_attrs, +}; + +static struct kset *most_channel_kset; + +/** + * create_most_c_obj - allocates a channel object + * @name: name of the channel object + * @parent: parent kobject + * + * This create a channel object and registers it with sysfs. + * Returns a pointer to the object or NULL when something went wrong. + */ +static struct most_c_obj * +create_most_c_obj(const char *name, struct kobject *parent) +{ + struct most_c_obj *c; + int retval; + + c = kzalloc(sizeof(*c), GFP_KERNEL); + if (!c) + return NULL; + c->kobj.kset = most_channel_kset; + retval = kobject_init_and_add(&c->kobj, &most_channel_ktype, parent, + "%s", name); + if (retval) { + kobject_put(&c->kobj); + return NULL; + } + kobject_uevent(&c->kobj, KOBJ_ADD); + return c; +} + +/* ___ ___ + * ___I N S T A N C E___ + */ + +static struct list_head instance_list; + +/** + * struct most_inst_attribute - to access the attributes of instance object + * @attr: attributes of an instance + * @show: pointer to the show function + * @store: pointer to the store function + */ +struct most_inst_attribute { + struct attribute attr; + ssize_t (*show)(struct most_inst_obj *d, + struct most_inst_attribute *attr, + char *buf); + ssize_t (*store)(struct most_inst_obj *d, + struct most_inst_attribute *attr, + const char *buf, + size_t count); +}; + +#define to_instance_attr(a) \ + container_of(a, struct most_inst_attribute, attr) + +/** + * instance_attr_show - show function for an instance object + * @kobj: pointer to kobject + * @attr: pointer to attribute struct + * @buf: buffer + */ +static ssize_t instance_attr_show(struct kobject *kobj, + struct attribute *attr, + char *buf) +{ + struct most_inst_attribute *instance_attr; + struct most_inst_obj *instance_obj; + + instance_attr = to_instance_attr(attr); + instance_obj = to_inst_obj(kobj); + + if (!instance_attr->show) + return -EIO; + + return instance_attr->show(instance_obj, instance_attr, buf); +} + +/** + * instance_attr_store - store function for an instance object + * @kobj: pointer to kobject + * @attr: pointer to attribute struct + * @buf: buffer + * @len: length of buffer + */ +static ssize_t instance_attr_store(struct kobject *kobj, + struct attribute *attr, + const char *buf, + size_t len) +{ + struct most_inst_attribute *instance_attr; + struct most_inst_obj *instance_obj; + + instance_attr = to_instance_attr(attr); + instance_obj = to_inst_obj(kobj); + + if (!instance_attr->store) + return -EIO; + + return instance_attr->store(instance_obj, instance_attr, buf, len); +} + +static const struct sysfs_ops most_inst_sysfs_ops = { + .show = instance_attr_show, + .store = instance_attr_store, +}; + +/** + * most_inst_release - release function for instance object + * @kobj: pointer to instance's kobject + * + * This frees the allocated memory for the instance object + */ +static void most_inst_release(struct kobject *kobj) +{ + struct most_inst_obj *inst = to_inst_obj(kobj); + + kfree(inst); +} + +static ssize_t description_show(struct most_inst_obj *instance_obj, + struct most_inst_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", + instance_obj->iface->description); +} + +static ssize_t interface_show(struct most_inst_obj *instance_obj, + struct most_inst_attribute *attr, + char *buf) +{ + switch (instance_obj->iface->interface) { + case ITYPE_LOOPBACK: + return snprintf(buf, PAGE_SIZE, "loopback\n"); + case ITYPE_I2C: + return snprintf(buf, PAGE_SIZE, "i2c\n"); + case ITYPE_I2S: + return snprintf(buf, PAGE_SIZE, "i2s\n"); + case ITYPE_TSI: + return snprintf(buf, PAGE_SIZE, "tsi\n"); + case ITYPE_HBI: + return snprintf(buf, PAGE_SIZE, "hbi\n"); + case ITYPE_MEDIALB_DIM: + return snprintf(buf, PAGE_SIZE, "mlb_dim\n"); + case ITYPE_MEDIALB_DIM2: + return snprintf(buf, PAGE_SIZE, "mlb_dim2\n"); + case ITYPE_USB: + return snprintf(buf, PAGE_SIZE, "usb\n"); + case ITYPE_PCIE: + return snprintf(buf, PAGE_SIZE, "pcie\n"); + } + return snprintf(buf, PAGE_SIZE, "unknown\n"); +} + +static struct most_inst_attribute most_inst_attr_description = + __ATTR_RO(description); + +static struct most_inst_attribute most_inst_attr_interface = + __ATTR_RO(interface); + +static struct attribute *most_inst_def_attrs[] = { + &most_inst_attr_description.attr, + &most_inst_attr_interface.attr, + NULL, +}; + +static struct kobj_type most_inst_ktype = { + .sysfs_ops = &most_inst_sysfs_ops, + .release = most_inst_release, + .default_attrs = most_inst_def_attrs, +}; + +static struct kset *most_inst_kset; + +/** + * create_most_inst_obj - creates an instance object + * @name: name of the object to be created + * + * This allocates memory for an instance structure, assigns the proper kset + * and registers it with sysfs. + * + * Returns a pointer to the instance object or NULL when something went wrong. + */ +static struct most_inst_obj *create_most_inst_obj(const char *name) +{ + struct most_inst_obj *inst; + int retval; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return NULL; + inst->kobj.kset = most_inst_kset; + retval = kobject_init_and_add(&inst->kobj, &most_inst_ktype, NULL, + "%s", name); + if (retval) { + kobject_put(&inst->kobj); + return NULL; + } + kobject_uevent(&inst->kobj, KOBJ_ADD); + return inst; +} + +/** + * destroy_most_inst_obj - MOST instance release function + * @inst: pointer to the instance object + * + * This decrements the reference counter of the instance object. + * If the reference count turns zero, its release function is called + */ +static void destroy_most_inst_obj(struct most_inst_obj *inst) +{ + struct most_c_obj *c, *tmp; + + list_for_each_entry_safe(c, tmp, &inst->channel_list, list) { + flush_trash_fifo(c); + flush_channel_fifos(c); + kobject_put(&c->kobj); + } + kobject_put(&inst->kobj); +} + +/* ___ ___ + * ___A I M___ + */ +struct most_aim_obj { + struct kobject kobj; + struct list_head list; + struct most_aim *driver; +}; + +#define to_aim_obj(d) container_of(d, struct most_aim_obj, kobj) + +static struct list_head aim_list; + +/** + * struct most_aim_attribute - to access the attributes of AIM object + * @attr: attributes of an AIM + * @show: pointer to the show function + * @store: pointer to the store function + */ +struct most_aim_attribute { + struct attribute attr; + ssize_t (*show)(struct most_aim_obj *d, + struct most_aim_attribute *attr, + char *buf); + ssize_t (*store)(struct most_aim_obj *d, + struct most_aim_attribute *attr, + const char *buf, + size_t count); +}; + +#define to_aim_attr(a) container_of(a, struct most_aim_attribute, attr) + +/** + * aim_attr_show - show function of an AIM object + * @kobj: pointer to kobject + * @attr: pointer to attribute struct + * @buf: buffer + */ +static ssize_t aim_attr_show(struct kobject *kobj, + struct attribute *attr, + char *buf) +{ + struct most_aim_attribute *aim_attr; + struct most_aim_obj *aim_obj; + + aim_attr = to_aim_attr(attr); + aim_obj = to_aim_obj(kobj); + + if (!aim_attr->show) + return -EIO; + + return aim_attr->show(aim_obj, aim_attr, buf); +} + +/** + * aim_attr_store - store function of an AIM object + * @kobj: pointer to kobject + * @attr: pointer to attribute struct + * @buf: buffer + * @len: length of buffer + */ +static ssize_t aim_attr_store(struct kobject *kobj, + struct attribute *attr, + const char *buf, + size_t len) +{ + struct most_aim_attribute *aim_attr; + struct most_aim_obj *aim_obj; + + aim_attr = to_aim_attr(attr); + aim_obj = to_aim_obj(kobj); + + if (!aim_attr->store) + return -EIO; + return aim_attr->store(aim_obj, aim_attr, buf, len); +} + +static const struct sysfs_ops most_aim_sysfs_ops = { + .show = aim_attr_show, + .store = aim_attr_store, +}; + +/** + * most_aim_release - AIM release function + * @kobj: pointer to AIM's kobject + */ +static void most_aim_release(struct kobject *kobj) +{ + struct most_aim_obj *aim_obj = to_aim_obj(kobj); + + kfree(aim_obj); +} + +static ssize_t links_show(struct most_aim_obj *aim_obj, + struct most_aim_attribute *attr, + char *buf) +{ + struct most_c_obj *c; + struct most_inst_obj *i; + int offs = 0; + + list_for_each_entry(i, &instance_list, list) { + list_for_each_entry(c, &i->channel_list, list) { + if (c->aim0.ptr == aim_obj->driver || + c->aim1.ptr == aim_obj->driver) { + offs += snprintf(buf + offs, PAGE_SIZE - offs, + "%s:%s\n", + kobject_name(&i->kobj), + kobject_name(&c->kobj)); + } + } + } + + return offs; +} + +/** + * split_string - parses and changes string in the buffer buf and + * splits it into two mandatory and one optional substrings. + * + * @buf: complete string from attribute 'add_channel' + * @a: address of pointer to 1st substring (=instance name) + * @b: address of pointer to 2nd substring (=channel name) + * @c: optional address of pointer to 3rd substring (=user defined name) + * + * Examples: + * + * Input: "mdev0:ch6:my_channel\n" or + * "mdev0:ch6:my_channel" + * + * Output: *a -> "mdev0", *b -> "ch6", *c -> "my_channel" + * + * Input: "mdev1:ep81\n" + * Output: *a -> "mdev1", *b -> "ep81", *c -> "" + * + * Input: "mdev1:ep81" + * Output: *a -> "mdev1", *b -> "ep81", *c == NULL + */ +static int split_string(char *buf, char **a, char **b, char **c) +{ + *a = strsep(&buf, ":"); + if (!*a) + return -EIO; + + *b = strsep(&buf, ":\n"); + if (!*b) + return -EIO; + + if (c) + *c = strsep(&buf, ":\n"); + + return 0; +} + +/** + * get_channel_by_name - get pointer to channel object + * @mdev: name of the device instance + * @mdev_ch: name of the respective channel + * + * This retrieves the pointer to a channel object. + */ +static struct +most_c_obj *get_channel_by_name(char *mdev, char *mdev_ch) +{ + struct most_c_obj *c, *tmp; + struct most_inst_obj *i, *i_tmp; + int found = 0; + + list_for_each_entry_safe(i, i_tmp, &instance_list, list) { + if (!strcmp(kobject_name(&i->kobj), mdev)) { + found++; + break; + } + } + if (unlikely(!found)) + return ERR_PTR(-EIO); + + list_for_each_entry_safe(c, tmp, &i->channel_list, list) { + if (!strcmp(kobject_name(&c->kobj), mdev_ch)) { + found++; + break; + } + } + if (unlikely(found < 2)) + return ERR_PTR(-EIO); + return c; +} + +/** + * add_link_store - store() function for add_link attribute + * @aim_obj: pointer to AIM object + * @attr: its attributes + * @buf: buffer + * @len: buffer length + * + * This parses the string given by buf and splits it into + * three substrings. Note: third substring is optional. In case a cdev + * AIM is loaded the optional 3rd substring will make up the name of + * device node in the /dev directory. If omitted, the device node will + * inherit the channel's name within sysfs. + * + * Searches for a pair of device and channel and probes the AIM + * + * Example: + * (1) echo "mdev0:ch6:my_rxchannel" >add_link + * (2) echo "mdev1:ep81" >add_link + * + * (1) would create the device node /dev/my_rxchannel + * (2) would create the device node /dev/mdev1-ep81 + */ +static ssize_t add_link_store(struct most_aim_obj *aim_obj, + struct most_aim_attribute *attr, + const char *buf, + size_t len) +{ + struct most_c_obj *c; + struct most_aim **aim_ptr; + char buffer[STRING_SIZE]; + char *mdev; + char *mdev_ch; + char *mdev_devnod; + char devnod_buf[STRING_SIZE]; + int ret; + size_t max_len = min_t(size_t, len + 1, STRING_SIZE); + + strlcpy(buffer, buf, max_len); + + ret = split_string(buffer, &mdev, &mdev_ch, &mdev_devnod); + if (ret) + return ret; + + if (!mdev_devnod || *mdev_devnod == 0) { + snprintf(devnod_buf, sizeof(devnod_buf), "%s-%s", mdev, + mdev_ch); + mdev_devnod = devnod_buf; + } + + c = get_channel_by_name(mdev, mdev_ch); + if (IS_ERR(c)) + return -ENODEV; + + if (!c->aim0.ptr) + aim_ptr = &c->aim0.ptr; + else if (!c->aim1.ptr) + aim_ptr = &c->aim1.ptr; + else + return -ENOSPC; + + *aim_ptr = aim_obj->driver; + ret = aim_obj->driver->probe_channel(c->iface, c->channel_id, + &c->cfg, &c->kobj, mdev_devnod); + if (ret) { + *aim_ptr = NULL; + return ret; + } + + return len; +} + +/** + * remove_link_store - store function for remove_link attribute + * @aim_obj: pointer to AIM object + * @attr: its attributes + * @buf: buffer + * @len: buffer length + * + * Example: + * echo "mdev0:ep81" >remove_link + */ +static ssize_t remove_link_store(struct most_aim_obj *aim_obj, + struct most_aim_attribute *attr, + const char *buf, + size_t len) +{ + struct most_c_obj *c; + char buffer[STRING_SIZE]; + char *mdev; + char *mdev_ch; + int ret; + size_t max_len = min_t(size_t, len + 1, STRING_SIZE); + + strlcpy(buffer, buf, max_len); + ret = split_string(buffer, &mdev, &mdev_ch, NULL); + if (ret) + return ret; + + c = get_channel_by_name(mdev, mdev_ch); + if (IS_ERR(c)) + return -ENODEV; + + if (aim_obj->driver->disconnect_channel(c->iface, c->channel_id)) + return -EIO; + if (c->aim0.ptr == aim_obj->driver) + c->aim0.ptr = NULL; + if (c->aim1.ptr == aim_obj->driver) + c->aim1.ptr = NULL; + return len; +} + +static struct most_aim_attribute most_aim_attrs[] = { + __ATTR_RO(links), + __ATTR_WO(add_link), + __ATTR_WO(remove_link), +}; + +static struct attribute *most_aim_def_attrs[] = { + &most_aim_attrs[0].attr, + &most_aim_attrs[1].attr, + &most_aim_attrs[2].attr, + NULL, +}; + +static struct kobj_type most_aim_ktype = { + .sysfs_ops = &most_aim_sysfs_ops, + .release = most_aim_release, + .default_attrs = most_aim_def_attrs, +}; + +static struct kset *most_aim_kset; + +/** + * create_most_aim_obj - creates an AIM object + * @name: name of the AIM + * + * This creates an AIM object assigns the proper kset and registers + * it with sysfs. + * Returns a pointer to the object or NULL if something went wrong. + */ +static struct most_aim_obj *create_most_aim_obj(const char *name) +{ + struct most_aim_obj *most_aim; + int retval; + + most_aim = kzalloc(sizeof(*most_aim), GFP_KERNEL); + if (!most_aim) + return NULL; + most_aim->kobj.kset = most_aim_kset; + retval = kobject_init_and_add(&most_aim->kobj, &most_aim_ktype, + NULL, "%s", name); + if (retval) { + kobject_put(&most_aim->kobj); + return NULL; + } + kobject_uevent(&most_aim->kobj, KOBJ_ADD); + return most_aim; +} + +/** + * destroy_most_aim_obj - AIM release function + * @p: pointer to AIM object + * + * This decrements the reference counter of the AIM object. If the + * reference count turns zero, its release function will be called. + */ +static void destroy_most_aim_obj(struct most_aim_obj *p) +{ + kobject_put(&p->kobj); +} + +/* ___ ___ + * ___C O R E___ + */ + +/** + * Instantiation of the MOST bus + */ +static struct bus_type most_bus = { + .name = "most", +}; + +/** + * Instantiation of the core driver + */ +static struct device_driver mostcore = { + .name = "mostcore", + .bus = &most_bus, +}; + +static inline void trash_mbo(struct mbo *mbo) +{ + unsigned long flags; + struct most_c_obj *c = mbo->context; + + spin_lock_irqsave(&c->fifo_lock, flags); + list_add(&mbo->list, &c->trash_fifo); + spin_unlock_irqrestore(&c->fifo_lock, flags); +} + +static bool hdm_mbo_ready(struct most_c_obj *c) +{ + bool empty; + + if (c->enqueue_halt) + return false; + + spin_lock_irq(&c->fifo_lock); + empty = list_empty(&c->halt_fifo); + spin_unlock_irq(&c->fifo_lock); + + return !empty; +} + +static void nq_hdm_mbo(struct mbo *mbo) +{ + unsigned long flags; + struct most_c_obj *c = mbo->context; + + spin_lock_irqsave(&c->fifo_lock, flags); + list_add_tail(&mbo->list, &c->halt_fifo); + spin_unlock_irqrestore(&c->fifo_lock, flags); + wake_up_interruptible(&c->hdm_fifo_wq); +} + +static int hdm_enqueue_thread(void *data) +{ + struct most_c_obj *c = data; + struct mbo *mbo; + int ret; + typeof(c->iface->enqueue) enqueue = c->iface->enqueue; + + while (likely(!kthread_should_stop())) { + wait_event_interruptible(c->hdm_fifo_wq, + hdm_mbo_ready(c) || + kthread_should_stop()); + + mutex_lock(&c->nq_mutex); + spin_lock_irq(&c->fifo_lock); + if (unlikely(c->enqueue_halt || list_empty(&c->halt_fifo))) { + spin_unlock_irq(&c->fifo_lock); + mutex_unlock(&c->nq_mutex); + continue; + } + + mbo = list_pop_mbo(&c->halt_fifo); + spin_unlock_irq(&c->fifo_lock); + + if (c->cfg.direction == MOST_CH_RX) + mbo->buffer_length = c->cfg.buffer_size; + + ret = enqueue(mbo->ifp, mbo->hdm_channel_id, mbo); + mutex_unlock(&c->nq_mutex); + + if (unlikely(ret)) { + pr_err("hdm enqueue failed\n"); + nq_hdm_mbo(mbo); + c->hdm_enqueue_task = NULL; + return 0; + } + } + + return 0; +} + +static int run_enqueue_thread(struct most_c_obj *c, int channel_id) +{ + struct task_struct *task = + kthread_run(hdm_enqueue_thread, c, "hdm_fifo_%d", + channel_id); + + if (IS_ERR(task)) + return PTR_ERR(task); + + c->hdm_enqueue_task = task; + return 0; +} + +/** + * arm_mbo - recycle MBO for further usage + * @mbo: buffer object + * + * This puts an MBO back to the list to have it ready for up coming + * tx transactions. + * + * In case the MBO belongs to a channel that recently has been + * poisoned, the MBO is scheduled to be trashed. + * Calls the completion handler of an attached AIM. + */ +static void arm_mbo(struct mbo *mbo) +{ + unsigned long flags; + struct most_c_obj *c; + + BUG_ON((!mbo) || (!mbo->context)); + c = mbo->context; + + if (c->is_poisoned) { + trash_mbo(mbo); + return; + } + + spin_lock_irqsave(&c->fifo_lock, flags); + ++*mbo->num_buffers_ptr; + list_add_tail(&mbo->list, &c->fifo); + spin_unlock_irqrestore(&c->fifo_lock, flags); + + if (c->aim0.refs && c->aim0.ptr->tx_completion) + c->aim0.ptr->tx_completion(c->iface, c->channel_id); + + if (c->aim1.refs && c->aim1.ptr->tx_completion) + c->aim1.ptr->tx_completion(c->iface, c->channel_id); +} + +/** + * arm_mbo_chain - helper function that arms an MBO chain for the HDM + * @c: pointer to interface channel + * @dir: direction of the channel + * @compl: pointer to completion function + * + * This allocates buffer objects including the containing DMA coherent + * buffer and puts them in the fifo. + * Buffers of Rx channels are put in the kthread fifo, hence immediately + * submitted to the HDM. + * + * Returns the number of allocated and enqueued MBOs. + */ +static int arm_mbo_chain(struct most_c_obj *c, int dir, + void (*compl)(struct mbo *)) +{ + unsigned int i; + int retval; + struct mbo *mbo; + u32 coherent_buf_size = c->cfg.buffer_size + c->cfg.extra_len; + + atomic_set(&c->mbo_nq_level, 0); + + for (i = 0; i < c->cfg.num_buffers; i++) { + mbo = kzalloc(sizeof(*mbo), GFP_KERNEL); + if (!mbo) { + retval = i; + goto _exit; + } + mbo->context = c; + mbo->ifp = c->iface; + mbo->hdm_channel_id = c->channel_id; + mbo->virt_address = dma_alloc_coherent(NULL, + coherent_buf_size, + &mbo->bus_address, + GFP_KERNEL); + if (!mbo->virt_address) { + pr_info("WARN: No DMA coherent buffer.\n"); + retval = i; + goto _error1; + } + mbo->complete = compl; + mbo->num_buffers_ptr = &dummy_num_buffers; + if (dir == MOST_CH_RX) { + nq_hdm_mbo(mbo); + atomic_inc(&c->mbo_nq_level); + } else { + arm_mbo(mbo); + } + } + return i; + +_error1: + kfree(mbo); +_exit: + return retval; +} + +/** + * most_submit_mbo - submits an MBO to fifo + * @mbo: pointer to the MBO + */ +void most_submit_mbo(struct mbo *mbo) +{ + if (WARN_ONCE(!mbo || !mbo->context, + "bad mbo or missing channel reference\n")) + return; + + nq_hdm_mbo(mbo); +} +EXPORT_SYMBOL_GPL(most_submit_mbo); + +/** + * most_write_completion - write completion handler + * @mbo: pointer to MBO + * + * This recycles the MBO for further usage. In case the channel has been + * poisoned, the MBO is scheduled to be trashed. + */ +static void most_write_completion(struct mbo *mbo) +{ + struct most_c_obj *c; + + BUG_ON((!mbo) || (!mbo->context)); + + c = mbo->context; + if (mbo->status == MBO_E_INVAL) + pr_info("WARN: Tx MBO status: invalid\n"); + if (unlikely(c->is_poisoned || (mbo->status == MBO_E_CLOSE))) + trash_mbo(mbo); + else + arm_mbo(mbo); +} + +/** + * get_channel_by_iface - get pointer to channel object + * @iface: pointer to interface instance + * @id: channel ID + * + * This retrieves a pointer to a channel of the given interface and channel ID. + */ +static struct +most_c_obj *get_channel_by_iface(struct most_interface *iface, int id) +{ + struct most_inst_obj *i; + + if (unlikely(!iface)) { + pr_err("Bad interface\n"); + return NULL; + } + if (unlikely((id < 0) || (id >= iface->num_channels))) { + pr_err("Channel index (%d) out of range\n", id); + return NULL; + } + i = iface->priv; + if (unlikely(!i)) { + pr_err("interface is not registered\n"); + return NULL; + } + return i->channel[id]; +} + +int channel_has_mbo(struct most_interface *iface, int id, struct most_aim *aim) +{ + struct most_c_obj *c = get_channel_by_iface(iface, id); + unsigned long flags; + int empty; + + if (unlikely(!c)) + return -EINVAL; + + if (c->aim0.refs && c->aim1.refs && + ((aim == c->aim0.ptr && c->aim0.num_buffers <= 0) || + (aim == c->aim1.ptr && c->aim1.num_buffers <= 0))) + return 0; + + spin_lock_irqsave(&c->fifo_lock, flags); + empty = list_empty(&c->fifo); + spin_unlock_irqrestore(&c->fifo_lock, flags); + return !empty; +} +EXPORT_SYMBOL_GPL(channel_has_mbo); + +/** + * most_get_mbo - get pointer to an MBO of pool + * @iface: pointer to interface instance + * @id: channel ID + * + * This attempts to get a free buffer out of the channel fifo. + * Returns a pointer to MBO on success or NULL otherwise. + */ +struct mbo *most_get_mbo(struct most_interface *iface, int id, + struct most_aim *aim) +{ + struct mbo *mbo; + struct most_c_obj *c; + unsigned long flags; + int *num_buffers_ptr; + + c = get_channel_by_iface(iface, id); + if (unlikely(!c)) + return NULL; + + if (c->aim0.refs && c->aim1.refs && + ((aim == c->aim0.ptr && c->aim0.num_buffers <= 0) || + (aim == c->aim1.ptr && c->aim1.num_buffers <= 0))) + return NULL; + + if (aim == c->aim0.ptr) + num_buffers_ptr = &c->aim0.num_buffers; + else if (aim == c->aim1.ptr) + num_buffers_ptr = &c->aim1.num_buffers; + else + num_buffers_ptr = &dummy_num_buffers; + + spin_lock_irqsave(&c->fifo_lock, flags); + if (list_empty(&c->fifo)) { + spin_unlock_irqrestore(&c->fifo_lock, flags); + return NULL; + } + mbo = list_pop_mbo(&c->fifo); + --*num_buffers_ptr; + spin_unlock_irqrestore(&c->fifo_lock, flags); + + mbo->num_buffers_ptr = num_buffers_ptr; + mbo->buffer_length = c->cfg.buffer_size; + return mbo; +} +EXPORT_SYMBOL_GPL(most_get_mbo); + +/** + * most_put_mbo - return buffer to pool + * @mbo: buffer object + */ +void most_put_mbo(struct mbo *mbo) +{ + struct most_c_obj *c = mbo->context; + + if (c->cfg.direction == MOST_CH_TX) { + arm_mbo(mbo); + return; + } + nq_hdm_mbo(mbo); + atomic_inc(&c->mbo_nq_level); +} +EXPORT_SYMBOL_GPL(most_put_mbo); + +/** + * most_read_completion - read completion handler + * @mbo: pointer to MBO + * + * This function is called by the HDM when data has been received from the + * hardware and copied to the buffer of the MBO. + * + * In case the channel has been poisoned it puts the buffer in the trash queue. + * Otherwise, it passes the buffer to an AIM for further processing. + */ +static void most_read_completion(struct mbo *mbo) +{ + struct most_c_obj *c = mbo->context; + + if (unlikely(c->is_poisoned || (mbo->status == MBO_E_CLOSE))) { + trash_mbo(mbo); + return; + } + + if (mbo->status == MBO_E_INVAL) { + nq_hdm_mbo(mbo); + atomic_inc(&c->mbo_nq_level); + return; + } + + if (atomic_sub_and_test(1, &c->mbo_nq_level)) + c->is_starving = 1; + + if (c->aim0.refs && c->aim0.ptr->rx_completion && + c->aim0.ptr->rx_completion(mbo) == 0) + return; + + if (c->aim1.refs && c->aim1.ptr->rx_completion && + c->aim1.ptr->rx_completion(mbo) == 0) + return; + + most_put_mbo(mbo); +} + +/** + * most_start_channel - prepares a channel for communication + * @iface: pointer to interface instance + * @id: channel ID + * + * This prepares the channel for usage. Cross-checks whether the + * channel's been properly configured. + * + * Returns 0 on success or error code otherwise. + */ +int most_start_channel(struct most_interface *iface, int id, + struct most_aim *aim) +{ + int num_buffer; + int ret; + struct most_c_obj *c = get_channel_by_iface(iface, id); + + if (unlikely(!c)) + return -EINVAL; + + mutex_lock(&c->start_mutex); + if (c->aim0.refs + c->aim1.refs > 0) + goto out; /* already started by other aim */ + + if (!try_module_get(iface->mod)) { + pr_info("failed to acquire HDM lock\n"); + mutex_unlock(&c->start_mutex); + return -ENOLCK; + } + + c->cfg.extra_len = 0; + if (c->iface->configure(c->iface, c->channel_id, &c->cfg)) { + pr_info("channel configuration failed. Go check settings...\n"); + ret = -EINVAL; + goto error; + } + + init_waitqueue_head(&c->hdm_fifo_wq); + + if (c->cfg.direction == MOST_CH_RX) + num_buffer = arm_mbo_chain(c, c->cfg.direction, + most_read_completion); + else + num_buffer = arm_mbo_chain(c, c->cfg.direction, + most_write_completion); + if (unlikely(!num_buffer)) { + pr_info("failed to allocate memory\n"); + ret = -ENOMEM; + goto error; + } + + ret = run_enqueue_thread(c, id); + if (ret) + goto error; + + c->is_starving = 0; + c->aim0.num_buffers = c->cfg.num_buffers / 2; + c->aim1.num_buffers = c->cfg.num_buffers - c->aim0.num_buffers; + atomic_set(&c->mbo_ref, num_buffer); + +out: + if (aim == c->aim0.ptr) + c->aim0.refs++; + if (aim == c->aim1.ptr) + c->aim1.refs++; + mutex_unlock(&c->start_mutex); + return 0; + +error: + module_put(iface->mod); + mutex_unlock(&c->start_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(most_start_channel); + +/** + * most_stop_channel - stops a running channel + * @iface: pointer to interface instance + * @id: channel ID + */ +int most_stop_channel(struct most_interface *iface, int id, + struct most_aim *aim) +{ + struct most_c_obj *c; + + if (unlikely((!iface) || (id >= iface->num_channels) || (id < 0))) { + pr_err("Bad interface or index out of range\n"); + return -EINVAL; + } + c = get_channel_by_iface(iface, id); + if (unlikely(!c)) + return -EINVAL; + + mutex_lock(&c->start_mutex); + if (c->aim0.refs + c->aim1.refs >= 2) + goto out; + + if (c->hdm_enqueue_task) + kthread_stop(c->hdm_enqueue_task); + c->hdm_enqueue_task = NULL; + + if (iface->mod) + module_put(iface->mod); + + c->is_poisoned = true; + if (c->iface->poison_channel(c->iface, c->channel_id)) { + pr_err("Cannot stop channel %d of mdev %s\n", c->channel_id, + c->iface->description); + mutex_unlock(&c->start_mutex); + return -EAGAIN; + } + flush_trash_fifo(c); + flush_channel_fifos(c); + +#ifdef CMPL_INTERRUPTIBLE + if (wait_for_completion_interruptible(&c->cleanup)) { + pr_info("Interrupted while clean up ch %d\n", c->channel_id); + mutex_unlock(&c->start_mutex); + return -EINTR; + } +#else + wait_for_completion(&c->cleanup); +#endif + c->is_poisoned = false; + +out: + if (aim == c->aim0.ptr) + c->aim0.refs--; + if (aim == c->aim1.ptr) + c->aim1.refs--; + mutex_unlock(&c->start_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(most_stop_channel); + +/** + * most_register_aim - registers an AIM (driver) with the core + * @aim: instance of AIM to be registered + */ +int most_register_aim(struct most_aim *aim) +{ + struct most_aim_obj *aim_obj; + + if (!aim) { + pr_err("Bad driver\n"); + return -EINVAL; + } + aim_obj = create_most_aim_obj(aim->name); + if (!aim_obj) { + pr_info("failed to alloc driver object\n"); + return -ENOMEM; + } + aim_obj->driver = aim; + aim->context = aim_obj; + pr_info("registered new application interfacing module %s\n", + aim->name); + list_add_tail(&aim_obj->list, &aim_list); + return 0; +} +EXPORT_SYMBOL_GPL(most_register_aim); + +/** + * most_deregister_aim - deregisters an AIM (driver) with the core + * @aim: AIM to be removed + */ +int most_deregister_aim(struct most_aim *aim) +{ + struct most_aim_obj *aim_obj; + struct most_c_obj *c, *tmp; + struct most_inst_obj *i, *i_tmp; + + if (!aim) { + pr_err("Bad driver\n"); + return -EINVAL; + } + + aim_obj = aim->context; + if (!aim_obj) { + pr_info("driver not registered.\n"); + return -EINVAL; + } + list_for_each_entry_safe(i, i_tmp, &instance_list, list) { + list_for_each_entry_safe(c, tmp, &i->channel_list, list) { + if (c->aim0.ptr == aim || c->aim1.ptr == aim) + aim->disconnect_channel( + c->iface, c->channel_id); + if (c->aim0.ptr == aim) + c->aim0.ptr = NULL; + if (c->aim1.ptr == aim) + c->aim1.ptr = NULL; + } + } + list_del(&aim_obj->list); + destroy_most_aim_obj(aim_obj); + pr_info("deregistering application interfacing module %s\n", aim->name); + return 0; +} +EXPORT_SYMBOL_GPL(most_deregister_aim); + +/** + * most_register_interface - registers an interface with core + * @iface: pointer to the instance of the interface description. + * + * Allocates and initializes a new interface instance and all of its channels. + * Returns a pointer to kobject or an error pointer. + */ +struct kobject *most_register_interface(struct most_interface *iface) +{ + unsigned int i; + int id; + char name[STRING_SIZE]; + char channel_name[STRING_SIZE]; + struct most_c_obj *c; + struct most_inst_obj *inst; + + if (!iface || !iface->enqueue || !iface->configure || + !iface->poison_channel || (iface->num_channels > MAX_CHANNELS)) { + pr_err("Bad interface or channel overflow\n"); + return ERR_PTR(-EINVAL); + } + + id = ida_simple_get(&mdev_id, 0, 0, GFP_KERNEL); + if (id < 0) { + pr_info("Failed to alloc mdev ID\n"); + return ERR_PTR(id); + } + snprintf(name, STRING_SIZE, "mdev%d", id); + + inst = create_most_inst_obj(name); + if (!inst) { + pr_info("Failed to allocate interface instance\n"); + ida_simple_remove(&mdev_id, id); + return ERR_PTR(-ENOMEM); + } + + iface->priv = inst; + INIT_LIST_HEAD(&inst->channel_list); + inst->iface = iface; + inst->dev_id = id; + list_add_tail(&inst->list, &instance_list); + + for (i = 0; i < iface->num_channels; i++) { + const char *name_suffix = iface->channel_vector[i].name_suffix; + + if (!name_suffix) + snprintf(channel_name, STRING_SIZE, "ch%d", i); + else + snprintf(channel_name, STRING_SIZE, "%s", name_suffix); + + /* this increments the reference count of this instance */ + c = create_most_c_obj(channel_name, &inst->kobj); + if (!c) + goto free_instance; + inst->channel[i] = c; + c->is_starving = 0; + c->iface = iface; + c->inst = inst; + c->channel_id = i; + c->keep_mbo = false; + c->enqueue_halt = false; + c->is_poisoned = false; + c->cfg.direction = 0; + c->cfg.data_type = 0; + c->cfg.num_buffers = 0; + c->cfg.buffer_size = 0; + c->cfg.subbuffer_size = 0; + c->cfg.packets_per_xact = 0; + spin_lock_init(&c->fifo_lock); + INIT_LIST_HEAD(&c->fifo); + INIT_LIST_HEAD(&c->trash_fifo); + INIT_LIST_HEAD(&c->halt_fifo); + init_completion(&c->cleanup); + atomic_set(&c->mbo_ref, 0); + mutex_init(&c->start_mutex); + mutex_init(&c->nq_mutex); + list_add_tail(&c->list, &inst->channel_list); + } + pr_info("registered new MOST device mdev%d (%s)\n", + inst->dev_id, iface->description); + return &inst->kobj; + +free_instance: + pr_info("Failed allocate channel(s)\n"); + list_del(&inst->list); + ida_simple_remove(&mdev_id, id); + destroy_most_inst_obj(inst); + return ERR_PTR(-ENOMEM); +} +EXPORT_SYMBOL_GPL(most_register_interface); + +/** + * most_deregister_interface - deregisters an interface with core + * @iface: pointer to the interface instance description. + * + * Before removing an interface instance from the list, all running + * channels are stopped and poisoned. + */ +void most_deregister_interface(struct most_interface *iface) +{ + struct most_inst_obj *i = iface->priv; + struct most_c_obj *c; + + if (unlikely(!i)) { + pr_info("Bad Interface\n"); + return; + } + pr_info("deregistering MOST device %s (%s)\n", i->kobj.name, + iface->description); + + list_for_each_entry(c, &i->channel_list, list) { + if (c->aim0.ptr) + c->aim0.ptr->disconnect_channel(c->iface, + c->channel_id); + if (c->aim1.ptr) + c->aim1.ptr->disconnect_channel(c->iface, + c->channel_id); + c->aim0.ptr = NULL; + c->aim1.ptr = NULL; + } + + ida_simple_remove(&mdev_id, i->dev_id); + list_del(&i->list); + destroy_most_inst_obj(i); +} +EXPORT_SYMBOL_GPL(most_deregister_interface); + +/** + * most_stop_enqueue - prevents core from enqueueing MBOs + * @iface: pointer to interface + * @id: channel id + * + * This is called by an HDM that _cannot_ attend to its duties and + * is imminent to get run over by the core. The core is not going to + * enqueue any further packets unless the flagging HDM calls + * most_resume enqueue(). + */ +void most_stop_enqueue(struct most_interface *iface, int id) +{ + struct most_c_obj *c = get_channel_by_iface(iface, id); + + if (!c) + return; + + mutex_lock(&c->nq_mutex); + c->enqueue_halt = true; + mutex_unlock(&c->nq_mutex); +} +EXPORT_SYMBOL_GPL(most_stop_enqueue); + +/** + * most_resume_enqueue - allow core to enqueue MBOs again + * @iface: pointer to interface + * @id: channel id + * + * This clears the enqueue halt flag and enqueues all MBOs currently + * sitting in the wait fifo. + */ +void most_resume_enqueue(struct most_interface *iface, int id) +{ + struct most_c_obj *c = get_channel_by_iface(iface, id); + + if (!c) + return; + + mutex_lock(&c->nq_mutex); + c->enqueue_halt = false; + mutex_unlock(&c->nq_mutex); + + wake_up_interruptible(&c->hdm_fifo_wq); +} +EXPORT_SYMBOL_GPL(most_resume_enqueue); + +static int __init most_init(void) +{ + int err; + + pr_info("init()\n"); + INIT_LIST_HEAD(&instance_list); + INIT_LIST_HEAD(&aim_list); + ida_init(&mdev_id); + + err = bus_register(&most_bus); + if (err) { + pr_info("Cannot register most bus\n"); + return err; + } + + most_class = class_create(THIS_MODULE, "most"); + if (IS_ERR(most_class)) { + pr_info("No udev support.\n"); + err = PTR_ERR(most_class); + goto exit_bus; + } + + err = driver_register(&mostcore); + if (err) { + pr_info("Cannot register core driver\n"); + goto exit_class; + } + + core_dev = device_create(most_class, NULL, 0, NULL, "mostcore"); + if (IS_ERR(core_dev)) { + err = PTR_ERR(core_dev); + goto exit_driver; + } + + most_aim_kset = kset_create_and_add("aims", NULL, &core_dev->kobj); + if (!most_aim_kset) { + err = -ENOMEM; + goto exit_class_container; + } + + most_inst_kset = kset_create_and_add("devices", NULL, &core_dev->kobj); + if (!most_inst_kset) { + err = -ENOMEM; + goto exit_driver_kset; + } + + return 0; + +exit_driver_kset: + kset_unregister(most_aim_kset); +exit_class_container: + device_destroy(most_class, 0); +exit_driver: + driver_unregister(&mostcore); +exit_class: + class_destroy(most_class); +exit_bus: + bus_unregister(&most_bus); + return err; +} + +static void __exit most_exit(void) +{ + struct most_inst_obj *i, *i_tmp; + struct most_aim_obj *d, *d_tmp; + + pr_info("exit core module\n"); + list_for_each_entry_safe(d, d_tmp, &aim_list, list) { + destroy_most_aim_obj(d); + } + + list_for_each_entry_safe(i, i_tmp, &instance_list, list) { + list_del(&i->list); + destroy_most_inst_obj(i); + } + kset_unregister(most_inst_kset); + kset_unregister(most_aim_kset); + device_destroy(most_class, 0); + driver_unregister(&mostcore); + class_destroy(most_class); + bus_unregister(&most_bus); + ida_destroy(&mdev_id); +} + +module_init(most_init); +module_exit(most_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Christian Gromm "); +MODULE_DESCRIPTION("Core module of stacked MOST Linux driver"); diff --git a/drivers/staging/most/core.h b/drivers/staging/most/core.h new file mode 100644 index 000000000000..5577e8735196 --- /dev/null +++ b/drivers/staging/most/core.h @@ -0,0 +1,319 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * most.h - Interface between MostCore, + * Hardware Dependent Module (HDM) and Application Interface Module (AIM). + * + * Copyright (C) 2013-2015, Microchip Technology Germany II GmbH & Co. KG + */ + +/* + * Authors: + * Andrey Shvetsov + * Christian Gromm + * Sebastian Graf + */ + +#ifndef __MOST_CORE_H__ +#define __MOST_CORE_H__ + +#include + +struct kobject; +struct module; + +/** + * Interface type + */ +enum most_interface_type { + ITYPE_LOOPBACK = 1, + ITYPE_I2C, + ITYPE_I2S, + ITYPE_TSI, + ITYPE_HBI, + ITYPE_MEDIALB_DIM, + ITYPE_MEDIALB_DIM2, + ITYPE_USB, + ITYPE_PCIE +}; + +/** + * Channel direction. + */ +enum most_channel_direction { + MOST_CH_RX = 1 << 0, + MOST_CH_TX = 1 << 1, +}; + +/** + * Channel data type. + */ +enum most_channel_data_type { + MOST_CH_CONTROL = 1 << 0, + MOST_CH_ASYNC = 1 << 1, + MOST_CH_ISOC = 1 << 2, + MOST_CH_SYNC = 1 << 5, +}; + +enum mbo_status_flags { + /* MBO was processed successfully (data was send or received )*/ + MBO_SUCCESS = 0, + /* The MBO contains wrong or missing information. */ + MBO_E_INVAL, + /* MBO was completed as HDM Channel will be closed */ + MBO_E_CLOSE, +}; + +/** + * struct most_channel_capability - Channel capability + * @direction: Supported channel directions. + * The value is bitwise OR-combination of the values from the + * enumeration most_channel_direction. Zero is allowed value and means + * "channel may not be used". + * @data_type: Supported channel data types. + * The value is bitwise OR-combination of the values from the + * enumeration most_channel_data_type. Zero is allowed value and means + * "channel may not be used". + * @num_buffer_packet: Maximum number of buffers supported by this channel + * for packet data types (Async,Control,QoS) + * @buffer_size_packet: Maximum buffer size supported by this channel + * for packet data types (Async,Control,QoS) + * @num_buffer_streaming: Maximum number of buffers supported by this channel + * for streaming data types (Sync,AV Packetized) + * @buffer_size_streaming: Maximum buffer size supported by this channel + * for streaming data types (Sync,AV Packetized) + * @name_suffix: Optional suffix providean by an HDM that is attached to the + * regular channel name. + * + * Describes the capabilities of a MostCore channel like supported Data Types + * and directions. This information is provided by an HDM for the MostCore. + * + * The Core creates read only sysfs attribute files in + * /sys/devices/virtual/most/mostcore/devices/mdev-#/mdev#-ch#/ with the + * following attributes: + * -available_directions + * -available_datatypes + * -number_of_packet_buffers + * -number_of_stream_buffers + * -size_of_packet_buffer + * -size_of_stream_buffer + * where content of each file is a string with all supported properties of this + * very channel attribute. + */ +struct most_channel_capability { + u16 direction; + u16 data_type; + u16 num_buffers_packet; + u16 buffer_size_packet; + u16 num_buffers_streaming; + u16 buffer_size_streaming; + const char *name_suffix; +}; + +/** + * struct most_channel_config - stores channel configuration + * @direction: direction of the channel + * @data_type: data type travelling over this channel + * @num_buffers: number of buffers + * @buffer_size: size of a buffer for AIM. + * Buffer size may be cutted down by HDM in a configure callback + * to match to a given interface and channel type. + * @extra_len: additional buffer space for internal HDM purposes like padding. + * May be set by HDM in a configure callback if needed. + * @subbuffer_size: size of a subbuffer + * @packets_per_xact: number of MOST frames that are packet inside one USB + * packet. This is USB specific + * + * Describes the configuration for a MostCore channel. This information is + * provided from the MostCore to a HDM (like the Medusa PCIe Interface) as a + * parameter of the "configure" function call. + */ +struct most_channel_config { + enum most_channel_direction direction; + enum most_channel_data_type data_type; + u16 num_buffers; + u16 buffer_size; + u16 extra_len; + u16 subbuffer_size; + u16 packets_per_xact; +}; + +/* + * struct mbo - MOST Buffer Object. + * @context: context for core completion handler + * @priv: private data for HDM + * + * public: documented fields that are used for the communications + * between MostCore and HDMs + * + * @list: list head for use by the mbo's current owner + * @ifp: (in) associated interface instance + * @hdm_channel_id: (in) HDM channel instance + * @virt_address: (in) kernel virtual address of the buffer + * @bus_address: (in) bus address of the buffer + * @buffer_length: (in) buffer payload length + * @processed_length: (out) processed length + * @status: (out) transfer status + * @complete: (in) completion routine + * + * The MostCore allocates and initializes the MBO. + * + * The HDM receives MBO for transfer from MostCore with the call to enqueue(). + * The HDM copies the data to- or from the buffer depending on configured + * channel direction, set "processed_length" and "status" and completes + * the transfer procedure by calling the completion routine. + * + * At the end the MostCore deallocates the MBO or recycles it for further + * transfers for the same or different HDM. + * + * Directions of usage: + * The core driver should never access any MBO fields (even if marked + * as "public") while the MBO is owned by an HDM. The ownership starts with + * the call of enqueue() and ends with the call of its complete() routine. + * + * II. + * Every HDM attached to the core driver _must_ ensure that it returns any MBO + * it owns (due to a previous call to enqueue() by the core driver) before it + * de-registers an interface or gets unloaded from the kernel. If this direction + * is violated memory leaks will occur, since the core driver does _not_ track + * MBOs it is currently not in control of. + * + */ +struct mbo { + void *context; + void *priv; + struct list_head list; + struct most_interface *ifp; + int *num_buffers_ptr; + u16 hdm_channel_id; + void *virt_address; + dma_addr_t bus_address; + u16 buffer_length; + u16 processed_length; + enum mbo_status_flags status; + void (*complete)(struct mbo *); +}; + +/** + * Interface instance description. + * + * Describes one instance of an interface like Medusa PCIe or Vantage USB. + * This structure is allocated and initialized in the HDM. MostCore may not + * modify this structure. + * + * @interface Interface type. \sa most_interface_type. + * @description PRELIMINARY. + * Unique description of the device instance from point of view of the + * interface in free text form (ASCII). + * It may be a hexadecimal presentation of the memory address for the MediaLB + * IP or USB device ID with USB properties for USB interface, etc. + * @num_channels Number of channels and size of the channel_vector. + * @channel_vector Properties of the channels. + * Array index represents channel ID by the driver. + * @configure Callback to change data type for the channel of the + * interface instance. May be zero if the instance of the interface is not + * configurable. Parameter channel_config describes direction and data + * type for the channel, configured by the higher level. The content of + * @enqueue Delivers MBO to the HDM for processing. + * After HDM completes Rx- or Tx- operation the processed MBO shall + * be returned back to the MostCore using completion routine. + * The reason to get the MBO delivered from the MostCore after the channel + * is poisoned is the re-opening of the channel by the application. + * In this case the HDM shall hold MBOs and service the channel as usual. + * The HDM must be able to hold at least one MBO for each channel. + * The callback returns a negative value on error, otherwise 0. + * @poison_channel Informs HDM about closing the channel. The HDM shall + * cancel all transfers and synchronously or asynchronously return + * all enqueued for this channel MBOs using the completion routine. + * The callback returns a negative value on error, otherwise 0. + * @request_netinfo: triggers retrieving of network info from the HDM by + * means of "Message exchange over MDP/MEP" + * The call of the function request_netinfo with the parameter on_netinfo as + * NULL prohibits use of the previously obtained function pointer. + * @priv Private field used by mostcore to store context information. + */ +struct most_interface { + struct module *mod; + enum most_interface_type interface; + const char *description; + int num_channels; + struct most_channel_capability *channel_vector; + int (*configure)(struct most_interface *iface, int channel_idx, + struct most_channel_config *channel_config); + int (*enqueue)(struct most_interface *iface, int channel_idx, + struct mbo *mbo); + int (*poison_channel)(struct most_interface *iface, int channel_idx); + void (*request_netinfo)(struct most_interface *iface, int channel_idx, + void (*on_netinfo)(struct most_interface *iface, + unsigned char link_stat, + unsigned char *mac_addr)); + void *priv; +}; + +/** + * struct most_aim - identifies MOST device driver to mostcore + * @name: Driver name + * @probe_channel: function for core to notify driver about channel connection + * @disconnect_channel: callback function to disconnect a certain channel + * @rx_completion: completion handler for received packets + * @tx_completion: completion handler for transmitted packets + * @context: context pointer to be used by mostcore + */ +struct most_aim { + const char *name; + int (*probe_channel)(struct most_interface *iface, int channel_idx, + struct most_channel_config *cfg, + struct kobject *parent, char *name); + int (*disconnect_channel)(struct most_interface *iface, + int channel_idx); + int (*rx_completion)(struct mbo *mbo); + int (*tx_completion)(struct most_interface *iface, int channel_idx); + void *context; +}; + +/** + * most_register_interface - Registers instance of the interface. + * @iface: Pointer to the interface instance description. + * + * Returns a pointer to the kobject of the generated instance. + * + * Note: HDM has to ensure that any reference held on the kobj is + * released before deregistering the interface. + */ +struct kobject *most_register_interface(struct most_interface *iface); + +/** + * Deregisters instance of the interface. + * @intf_instance Pointer to the interface instance description. + */ +void most_deregister_interface(struct most_interface *iface); +void most_submit_mbo(struct mbo *mbo); + +/** + * most_stop_enqueue - prevents core from enqueing MBOs + * @iface: pointer to interface + * @channel_idx: channel index + */ +void most_stop_enqueue(struct most_interface *iface, int channel_idx); + +/** + * most_resume_enqueue - allow core to enqueue MBOs again + * @iface: pointer to interface + * @channel_idx: channel index + * + * This clears the enqueue halt flag and enqueues all MBOs currently + * in wait fifo. + */ +void most_resume_enqueue(struct most_interface *iface, int channel_idx); +int most_register_aim(struct most_aim *aim); +int most_deregister_aim(struct most_aim *aim); +struct mbo *most_get_mbo(struct most_interface *iface, int channel_idx, + struct most_aim *); +void most_put_mbo(struct mbo *mbo); +int channel_has_mbo(struct most_interface *iface, int channel_idx, + struct most_aim *aim); +int most_start_channel(struct most_interface *iface, int channel_idx, + struct most_aim *); +int most_stop_channel(struct most_interface *iface, int channel_idx, + struct most_aim *); + +#endif /* MOST_CORE_H_ */ diff --git a/drivers/staging/most/hdm-dim2/Makefile b/drivers/staging/most/hdm-dim2/Makefile index 6bbee879a8ea..b66492bf7674 100644 --- a/drivers/staging/most/hdm-dim2/Makefile +++ b/drivers/staging/most/hdm-dim2/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_HDM_DIM2) += hdm_dim2.o hdm_dim2-objs := dim2_hdm.o dim2_hal.o dim2_sysfs.o -ccflags-y += -Idrivers/staging/most/mostcore/ +ccflags-y += -Idrivers/staging/ ccflags-y += -Idrivers/staging/most/aim-network/ diff --git a/drivers/staging/most/hdm-dim2/dim2_hdm.c b/drivers/staging/most/hdm-dim2/dim2_hdm.c index 312f4f75ef06..fedd2d06742a 100644 --- a/drivers/staging/most/hdm-dim2/dim2_hdm.c +++ b/drivers/staging/most/hdm-dim2/dim2_hdm.c @@ -19,7 +19,7 @@ #include #include -#include +#include "most/core.h" #include "dim2_hal.h" #include "dim2_hdm.h" #include "dim2_errors.h" diff --git a/drivers/staging/most/hdm-i2c/Makefile b/drivers/staging/most/hdm-i2c/Makefile index 03a4a59b1f9f..6ddc78aae3d9 100644 --- a/drivers/staging/most/hdm-i2c/Makefile +++ b/drivers/staging/most/hdm-i2c/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_HDM_I2C) += hdm_i2c.o -ccflags-y += -Idrivers/staging/most/mostcore/ +ccflags-y += -Idrivers/staging/ diff --git a/drivers/staging/most/hdm-i2c/hdm_i2c.c b/drivers/staging/most/hdm-i2c/hdm_i2c.c index ed6793a1bc93..c73c76d0a6d5 100644 --- a/drivers/staging/most/hdm-i2c/hdm_i2c.c +++ b/drivers/staging/most/hdm-i2c/hdm_i2c.c @@ -15,7 +15,7 @@ #include #include -#include +#include "most/core.h" enum { CH_RX, CH_TX, NUM_CHANNELS }; diff --git a/drivers/staging/most/hdm-usb/Makefile b/drivers/staging/most/hdm-usb/Makefile index 6bbacb41e94b..4fea7c2a7755 100644 --- a/drivers/staging/most/hdm-usb/Makefile +++ b/drivers/staging/most/hdm-usb/Makefile @@ -1,4 +1,4 @@ obj-$(CONFIG_HDM_USB) += hdm_usb.o -ccflags-y += -Idrivers/staging/most/mostcore/ +ccflags-y += -Idrivers/staging/ ccflags-y += -Idrivers/staging/most/aim-network/ diff --git a/drivers/staging/most/hdm-usb/hdm_usb.c b/drivers/staging/most/hdm-usb/hdm_usb.c index c01b93bb134f..f036614a1da6 100644 --- a/drivers/staging/most/hdm-usb/hdm_usb.c +++ b/drivers/staging/most/hdm-usb/hdm_usb.c @@ -23,7 +23,7 @@ #include #include #include -#include "mostcore.h" +#include "most/core.h" #define USB_MTU 512 #define NO_ISOCHRONOUS_URB 0 diff --git a/drivers/staging/most/mostcore/Kconfig b/drivers/staging/most/mostcore/Kconfig deleted file mode 100644 index 47172546d728..000000000000 --- a/drivers/staging/most/mostcore/Kconfig +++ /dev/null @@ -1,14 +0,0 @@ -# -# MOSTCore configuration -# - -config MOSTCORE - tristate "MOST Core" - depends on HAS_DMA - - ---help--- - Say Y here if you want to enable MOST support. - This device driver needs at least an additional AIM and HDM to work. - - To compile this driver as a module, choose M here: the - module will be called mostcore. diff --git a/drivers/staging/most/mostcore/Makefile b/drivers/staging/most/mostcore/Makefile deleted file mode 100644 index a078f01cf7c2..000000000000 --- a/drivers/staging/most/mostcore/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-$(CONFIG_MOSTCORE) += mostcore.o - -mostcore-objs := core.o diff --git a/drivers/staging/most/mostcore/core.c b/drivers/staging/most/mostcore/core.c deleted file mode 100644 index cb70e1029a93..000000000000 --- a/drivers/staging/most/mostcore/core.c +++ /dev/null @@ -1,1943 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * core.c - Implementation of core module of MOST Linux driver stack - * - * Copyright (C) 2013-2015 Microchip Technology Germany II GmbH & Co. KG - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "mostcore.h" - -#define MAX_CHANNELS 64 -#define STRING_SIZE 80 - -static struct class *most_class; -static struct device *core_dev; -static struct ida mdev_id; -static int dummy_num_buffers; - -struct most_c_aim_obj { - struct most_aim *ptr; - int refs; - int num_buffers; -}; - -struct most_c_obj { - struct kobject kobj; - struct completion cleanup; - atomic_t mbo_ref; - atomic_t mbo_nq_level; - u16 channel_id; - bool is_poisoned; - struct mutex start_mutex; - struct mutex nq_mutex; /* nq thread synchronization */ - int is_starving; - struct most_interface *iface; - struct most_inst_obj *inst; - struct most_channel_config cfg; - bool keep_mbo; - bool enqueue_halt; - struct list_head fifo; - spinlock_t fifo_lock; - struct list_head halt_fifo; - struct list_head list; - struct most_c_aim_obj aim0; - struct most_c_aim_obj aim1; - struct list_head trash_fifo; - struct task_struct *hdm_enqueue_task; - wait_queue_head_t hdm_fifo_wq; -}; - -#define to_c_obj(d) container_of(d, struct most_c_obj, kobj) - -struct most_inst_obj { - int dev_id; - struct most_interface *iface; - struct list_head channel_list; - struct most_c_obj *channel[MAX_CHANNELS]; - struct kobject kobj; - struct list_head list; -}; - -static const struct { - int most_ch_data_type; - const char *name; -} ch_data_type[] = { - { MOST_CH_CONTROL, "control\n" }, - { MOST_CH_ASYNC, "async\n" }, - { MOST_CH_SYNC, "sync\n" }, - { MOST_CH_ISOC, "isoc\n"}, - { MOST_CH_ISOC, "isoc_avp\n"}, -}; - -#define to_inst_obj(d) container_of(d, struct most_inst_obj, kobj) - -/** - * list_pop_mbo - retrieves the first MBO of the list and removes it - * @ptr: the list head to grab the MBO from. - */ -#define list_pop_mbo(ptr) \ -({ \ - struct mbo *_mbo = list_first_entry(ptr, struct mbo, list); \ - list_del(&_mbo->list); \ - _mbo; \ -}) - -/* ___ ___ - * ___C H A N N E L___ - */ - -/** - * struct most_c_attr - to access the attributes of a channel object - * @attr: attributes of a channel - * @show: pointer to the show function - * @store: pointer to the store function - */ -struct most_c_attr { - struct attribute attr; - ssize_t (*show)(struct most_c_obj *d, - struct most_c_attr *attr, - char *buf); - ssize_t (*store)(struct most_c_obj *d, - struct most_c_attr *attr, - const char *buf, - size_t count); -}; - -#define to_channel_attr(a) container_of(a, struct most_c_attr, attr) - -/** - * channel_attr_show - show function of channel object - * @kobj: pointer to its kobject - * @attr: pointer to its attributes - * @buf: buffer - */ -static ssize_t channel_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - struct most_c_attr *channel_attr = to_channel_attr(attr); - struct most_c_obj *c_obj = to_c_obj(kobj); - - if (!channel_attr->show) - return -EIO; - - return channel_attr->show(c_obj, channel_attr, buf); -} - -/** - * channel_attr_store - store function of channel object - * @kobj: pointer to its kobject - * @attr: pointer to its attributes - * @buf: buffer - * @len: length of buffer - */ -static ssize_t channel_attr_store(struct kobject *kobj, - struct attribute *attr, - const char *buf, - size_t len) -{ - struct most_c_attr *channel_attr = to_channel_attr(attr); - struct most_c_obj *c_obj = to_c_obj(kobj); - - if (!channel_attr->store) - return -EIO; - return channel_attr->store(c_obj, channel_attr, buf, len); -} - -static const struct sysfs_ops most_channel_sysfs_ops = { - .show = channel_attr_show, - .store = channel_attr_store, -}; - -/** - * most_free_mbo_coherent - free an MBO and its coherent buffer - * @mbo: buffer to be released - * - */ -static void most_free_mbo_coherent(struct mbo *mbo) -{ - struct most_c_obj *c = mbo->context; - u16 const coherent_buf_size = c->cfg.buffer_size + c->cfg.extra_len; - - dma_free_coherent(NULL, coherent_buf_size, mbo->virt_address, - mbo->bus_address); - kfree(mbo); - if (atomic_sub_and_test(1, &c->mbo_ref)) - complete(&c->cleanup); -} - -/** - * flush_channel_fifos - clear the channel fifos - * @c: pointer to channel object - */ -static void flush_channel_fifos(struct most_c_obj *c) -{ - unsigned long flags, hf_flags; - struct mbo *mbo, *tmp; - - if (list_empty(&c->fifo) && list_empty(&c->halt_fifo)) - return; - - spin_lock_irqsave(&c->fifo_lock, flags); - list_for_each_entry_safe(mbo, tmp, &c->fifo, list) { - list_del(&mbo->list); - spin_unlock_irqrestore(&c->fifo_lock, flags); - most_free_mbo_coherent(mbo); - spin_lock_irqsave(&c->fifo_lock, flags); - } - spin_unlock_irqrestore(&c->fifo_lock, flags); - - spin_lock_irqsave(&c->fifo_lock, hf_flags); - list_for_each_entry_safe(mbo, tmp, &c->halt_fifo, list) { - list_del(&mbo->list); - spin_unlock_irqrestore(&c->fifo_lock, hf_flags); - most_free_mbo_coherent(mbo); - spin_lock_irqsave(&c->fifo_lock, hf_flags); - } - spin_unlock_irqrestore(&c->fifo_lock, hf_flags); - - if (unlikely((!list_empty(&c->fifo) || !list_empty(&c->halt_fifo)))) - pr_info("WARN: fifo | trash fifo not empty\n"); -} - -/** - * flush_trash_fifo - clear the trash fifo - * @c: pointer to channel object - */ -static int flush_trash_fifo(struct most_c_obj *c) -{ - struct mbo *mbo, *tmp; - unsigned long flags; - - spin_lock_irqsave(&c->fifo_lock, flags); - list_for_each_entry_safe(mbo, tmp, &c->trash_fifo, list) { - list_del(&mbo->list); - spin_unlock_irqrestore(&c->fifo_lock, flags); - most_free_mbo_coherent(mbo); - spin_lock_irqsave(&c->fifo_lock, flags); - } - spin_unlock_irqrestore(&c->fifo_lock, flags); - return 0; -} - -/** - * most_channel_release - release function of channel object - * @kobj: pointer to channel's kobject - */ -static void most_channel_release(struct kobject *kobj) -{ - struct most_c_obj *c = to_c_obj(kobj); - - kfree(c); -} - -static ssize_t available_directions_show(struct most_c_obj *c, - struct most_c_attr *attr, - char *buf) -{ - unsigned int i = c->channel_id; - - strcpy(buf, ""); - if (c->iface->channel_vector[i].direction & MOST_CH_RX) - strcat(buf, "rx "); - if (c->iface->channel_vector[i].direction & MOST_CH_TX) - strcat(buf, "tx "); - strcat(buf, "\n"); - return strlen(buf); -} - -static ssize_t available_datatypes_show(struct most_c_obj *c, - struct most_c_attr *attr, - char *buf) -{ - unsigned int i = c->channel_id; - - strcpy(buf, ""); - if (c->iface->channel_vector[i].data_type & MOST_CH_CONTROL) - strcat(buf, "control "); - if (c->iface->channel_vector[i].data_type & MOST_CH_ASYNC) - strcat(buf, "async "); - if (c->iface->channel_vector[i].data_type & MOST_CH_SYNC) - strcat(buf, "sync "); - if (c->iface->channel_vector[i].data_type & MOST_CH_ISOC) - strcat(buf, "isoc "); - strcat(buf, "\n"); - return strlen(buf); -} - -static ssize_t number_of_packet_buffers_show(struct most_c_obj *c, - struct most_c_attr *attr, - char *buf) -{ - unsigned int i = c->channel_id; - - return snprintf(buf, PAGE_SIZE, "%d\n", - c->iface->channel_vector[i].num_buffers_packet); -} - -static ssize_t number_of_stream_buffers_show(struct most_c_obj *c, - struct most_c_attr *attr, - char *buf) -{ - unsigned int i = c->channel_id; - - return snprintf(buf, PAGE_SIZE, "%d\n", - c->iface->channel_vector[i].num_buffers_streaming); -} - -static ssize_t size_of_packet_buffer_show(struct most_c_obj *c, - struct most_c_attr *attr, - char *buf) -{ - unsigned int i = c->channel_id; - - return snprintf(buf, PAGE_SIZE, "%d\n", - c->iface->channel_vector[i].buffer_size_packet); -} - -static ssize_t size_of_stream_buffer_show(struct most_c_obj *c, - struct most_c_attr *attr, - char *buf) -{ - unsigned int i = c->channel_id; - - return snprintf(buf, PAGE_SIZE, "%d\n", - c->iface->channel_vector[i].buffer_size_streaming); -} - -static ssize_t channel_starving_show(struct most_c_obj *c, - struct most_c_attr *attr, - char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d\n", c->is_starving); -} - -static ssize_t set_number_of_buffers_show(struct most_c_obj *c, - struct most_c_attr *attr, - char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.num_buffers); -} - -static ssize_t set_number_of_buffers_store(struct most_c_obj *c, - struct most_c_attr *attr, - const char *buf, - size_t count) -{ - int ret = kstrtou16(buf, 0, &c->cfg.num_buffers); - - if (ret) - return ret; - return count; -} - -static ssize_t set_buffer_size_show(struct most_c_obj *c, - struct most_c_attr *attr, - char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.buffer_size); -} - -static ssize_t set_buffer_size_store(struct most_c_obj *c, - struct most_c_attr *attr, - const char *buf, - size_t count) -{ - int ret = kstrtou16(buf, 0, &c->cfg.buffer_size); - - if (ret) - return ret; - return count; -} - -static ssize_t set_direction_show(struct most_c_obj *c, - struct most_c_attr *attr, - char *buf) -{ - if (c->cfg.direction & MOST_CH_TX) - return snprintf(buf, PAGE_SIZE, "tx\n"); - else if (c->cfg.direction & MOST_CH_RX) - return snprintf(buf, PAGE_SIZE, "rx\n"); - return snprintf(buf, PAGE_SIZE, "unconfigured\n"); -} - -static ssize_t set_direction_store(struct most_c_obj *c, - struct most_c_attr *attr, - const char *buf, - size_t count) -{ - if (!strcmp(buf, "dir_rx\n")) { - c->cfg.direction = MOST_CH_RX; - } else if (!strcmp(buf, "rx\n")) { - c->cfg.direction = MOST_CH_RX; - } else if (!strcmp(buf, "dir_tx\n")) { - c->cfg.direction = MOST_CH_TX; - } else if (!strcmp(buf, "tx\n")) { - c->cfg.direction = MOST_CH_TX; - } else { - pr_info("WARN: invalid attribute settings\n"); - return -EINVAL; - } - return count; -} - -static ssize_t set_datatype_show(struct most_c_obj *c, - struct most_c_attr *attr, - char *buf) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ch_data_type); i++) { - if (c->cfg.data_type & ch_data_type[i].most_ch_data_type) - return snprintf(buf, PAGE_SIZE, ch_data_type[i].name); - } - return snprintf(buf, PAGE_SIZE, "unconfigured\n"); -} - -static ssize_t set_datatype_store(struct most_c_obj *c, - struct most_c_attr *attr, - const char *buf, - size_t count) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ch_data_type); i++) { - if (!strcmp(buf, ch_data_type[i].name)) { - c->cfg.data_type = ch_data_type[i].most_ch_data_type; - break; - } - } - - if (i == ARRAY_SIZE(ch_data_type)) { - pr_info("WARN: invalid attribute settings\n"); - return -EINVAL; - } - return count; -} - -static ssize_t set_subbuffer_size_show(struct most_c_obj *c, - struct most_c_attr *attr, - char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.subbuffer_size); -} - -static ssize_t set_subbuffer_size_store(struct most_c_obj *c, - struct most_c_attr *attr, - const char *buf, - size_t count) -{ - int ret = kstrtou16(buf, 0, &c->cfg.subbuffer_size); - - if (ret) - return ret; - return count; -} - -static ssize_t set_packets_per_xact_show(struct most_c_obj *c, - struct most_c_attr *attr, - char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.packets_per_xact); -} - -static ssize_t set_packets_per_xact_store(struct most_c_obj *c, - struct most_c_attr *attr, - const char *buf, - size_t count) -{ - int ret = kstrtou16(buf, 0, &c->cfg.packets_per_xact); - - if (ret) - return ret; - return count; -} - -static struct most_c_attr most_c_attrs[] = { - __ATTR_RO(available_directions), - __ATTR_RO(available_datatypes), - __ATTR_RO(number_of_packet_buffers), - __ATTR_RO(number_of_stream_buffers), - __ATTR_RO(size_of_stream_buffer), - __ATTR_RO(size_of_packet_buffer), - __ATTR_RO(channel_starving), - __ATTR_RW(set_buffer_size), - __ATTR_RW(set_number_of_buffers), - __ATTR_RW(set_direction), - __ATTR_RW(set_datatype), - __ATTR_RW(set_subbuffer_size), - __ATTR_RW(set_packets_per_xact), -}; - -/** - * most_channel_def_attrs - array of default attributes of channel object - */ -static struct attribute *most_channel_def_attrs[] = { - &most_c_attrs[0].attr, - &most_c_attrs[1].attr, - &most_c_attrs[2].attr, - &most_c_attrs[3].attr, - &most_c_attrs[4].attr, - &most_c_attrs[5].attr, - &most_c_attrs[6].attr, - &most_c_attrs[7].attr, - &most_c_attrs[8].attr, - &most_c_attrs[9].attr, - &most_c_attrs[10].attr, - &most_c_attrs[11].attr, - &most_c_attrs[12].attr, - NULL, -}; - -static struct kobj_type most_channel_ktype = { - .sysfs_ops = &most_channel_sysfs_ops, - .release = most_channel_release, - .default_attrs = most_channel_def_attrs, -}; - -static struct kset *most_channel_kset; - -/** - * create_most_c_obj - allocates a channel object - * @name: name of the channel object - * @parent: parent kobject - * - * This create a channel object and registers it with sysfs. - * Returns a pointer to the object or NULL when something went wrong. - */ -static struct most_c_obj * -create_most_c_obj(const char *name, struct kobject *parent) -{ - struct most_c_obj *c; - int retval; - - c = kzalloc(sizeof(*c), GFP_KERNEL); - if (!c) - return NULL; - c->kobj.kset = most_channel_kset; - retval = kobject_init_and_add(&c->kobj, &most_channel_ktype, parent, - "%s", name); - if (retval) { - kobject_put(&c->kobj); - return NULL; - } - kobject_uevent(&c->kobj, KOBJ_ADD); - return c; -} - -/* ___ ___ - * ___I N S T A N C E___ - */ - -static struct list_head instance_list; - -/** - * struct most_inst_attribute - to access the attributes of instance object - * @attr: attributes of an instance - * @show: pointer to the show function - * @store: pointer to the store function - */ -struct most_inst_attribute { - struct attribute attr; - ssize_t (*show)(struct most_inst_obj *d, - struct most_inst_attribute *attr, - char *buf); - ssize_t (*store)(struct most_inst_obj *d, - struct most_inst_attribute *attr, - const char *buf, - size_t count); -}; - -#define to_instance_attr(a) \ - container_of(a, struct most_inst_attribute, attr) - -/** - * instance_attr_show - show function for an instance object - * @kobj: pointer to kobject - * @attr: pointer to attribute struct - * @buf: buffer - */ -static ssize_t instance_attr_show(struct kobject *kobj, - struct attribute *attr, - char *buf) -{ - struct most_inst_attribute *instance_attr; - struct most_inst_obj *instance_obj; - - instance_attr = to_instance_attr(attr); - instance_obj = to_inst_obj(kobj); - - if (!instance_attr->show) - return -EIO; - - return instance_attr->show(instance_obj, instance_attr, buf); -} - -/** - * instance_attr_store - store function for an instance object - * @kobj: pointer to kobject - * @attr: pointer to attribute struct - * @buf: buffer - * @len: length of buffer - */ -static ssize_t instance_attr_store(struct kobject *kobj, - struct attribute *attr, - const char *buf, - size_t len) -{ - struct most_inst_attribute *instance_attr; - struct most_inst_obj *instance_obj; - - instance_attr = to_instance_attr(attr); - instance_obj = to_inst_obj(kobj); - - if (!instance_attr->store) - return -EIO; - - return instance_attr->store(instance_obj, instance_attr, buf, len); -} - -static const struct sysfs_ops most_inst_sysfs_ops = { - .show = instance_attr_show, - .store = instance_attr_store, -}; - -/** - * most_inst_release - release function for instance object - * @kobj: pointer to instance's kobject - * - * This frees the allocated memory for the instance object - */ -static void most_inst_release(struct kobject *kobj) -{ - struct most_inst_obj *inst = to_inst_obj(kobj); - - kfree(inst); -} - -static ssize_t description_show(struct most_inst_obj *instance_obj, - struct most_inst_attribute *attr, - char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%s\n", - instance_obj->iface->description); -} - -static ssize_t interface_show(struct most_inst_obj *instance_obj, - struct most_inst_attribute *attr, - char *buf) -{ - switch (instance_obj->iface->interface) { - case ITYPE_LOOPBACK: - return snprintf(buf, PAGE_SIZE, "loopback\n"); - case ITYPE_I2C: - return snprintf(buf, PAGE_SIZE, "i2c\n"); - case ITYPE_I2S: - return snprintf(buf, PAGE_SIZE, "i2s\n"); - case ITYPE_TSI: - return snprintf(buf, PAGE_SIZE, "tsi\n"); - case ITYPE_HBI: - return snprintf(buf, PAGE_SIZE, "hbi\n"); - case ITYPE_MEDIALB_DIM: - return snprintf(buf, PAGE_SIZE, "mlb_dim\n"); - case ITYPE_MEDIALB_DIM2: - return snprintf(buf, PAGE_SIZE, "mlb_dim2\n"); - case ITYPE_USB: - return snprintf(buf, PAGE_SIZE, "usb\n"); - case ITYPE_PCIE: - return snprintf(buf, PAGE_SIZE, "pcie\n"); - } - return snprintf(buf, PAGE_SIZE, "unknown\n"); -} - -static struct most_inst_attribute most_inst_attr_description = - __ATTR_RO(description); - -static struct most_inst_attribute most_inst_attr_interface = - __ATTR_RO(interface); - -static struct attribute *most_inst_def_attrs[] = { - &most_inst_attr_description.attr, - &most_inst_attr_interface.attr, - NULL, -}; - -static struct kobj_type most_inst_ktype = { - .sysfs_ops = &most_inst_sysfs_ops, - .release = most_inst_release, - .default_attrs = most_inst_def_attrs, -}; - -static struct kset *most_inst_kset; - -/** - * create_most_inst_obj - creates an instance object - * @name: name of the object to be created - * - * This allocates memory for an instance structure, assigns the proper kset - * and registers it with sysfs. - * - * Returns a pointer to the instance object or NULL when something went wrong. - */ -static struct most_inst_obj *create_most_inst_obj(const char *name) -{ - struct most_inst_obj *inst; - int retval; - - inst = kzalloc(sizeof(*inst), GFP_KERNEL); - if (!inst) - return NULL; - inst->kobj.kset = most_inst_kset; - retval = kobject_init_and_add(&inst->kobj, &most_inst_ktype, NULL, - "%s", name); - if (retval) { - kobject_put(&inst->kobj); - return NULL; - } - kobject_uevent(&inst->kobj, KOBJ_ADD); - return inst; -} - -/** - * destroy_most_inst_obj - MOST instance release function - * @inst: pointer to the instance object - * - * This decrements the reference counter of the instance object. - * If the reference count turns zero, its release function is called - */ -static void destroy_most_inst_obj(struct most_inst_obj *inst) -{ - struct most_c_obj *c, *tmp; - - list_for_each_entry_safe(c, tmp, &inst->channel_list, list) { - flush_trash_fifo(c); - flush_channel_fifos(c); - kobject_put(&c->kobj); - } - kobject_put(&inst->kobj); -} - -/* ___ ___ - * ___A I M___ - */ -struct most_aim_obj { - struct kobject kobj; - struct list_head list; - struct most_aim *driver; -}; - -#define to_aim_obj(d) container_of(d, struct most_aim_obj, kobj) - -static struct list_head aim_list; - -/** - * struct most_aim_attribute - to access the attributes of AIM object - * @attr: attributes of an AIM - * @show: pointer to the show function - * @store: pointer to the store function - */ -struct most_aim_attribute { - struct attribute attr; - ssize_t (*show)(struct most_aim_obj *d, - struct most_aim_attribute *attr, - char *buf); - ssize_t (*store)(struct most_aim_obj *d, - struct most_aim_attribute *attr, - const char *buf, - size_t count); -}; - -#define to_aim_attr(a) container_of(a, struct most_aim_attribute, attr) - -/** - * aim_attr_show - show function of an AIM object - * @kobj: pointer to kobject - * @attr: pointer to attribute struct - * @buf: buffer - */ -static ssize_t aim_attr_show(struct kobject *kobj, - struct attribute *attr, - char *buf) -{ - struct most_aim_attribute *aim_attr; - struct most_aim_obj *aim_obj; - - aim_attr = to_aim_attr(attr); - aim_obj = to_aim_obj(kobj); - - if (!aim_attr->show) - return -EIO; - - return aim_attr->show(aim_obj, aim_attr, buf); -} - -/** - * aim_attr_store - store function of an AIM object - * @kobj: pointer to kobject - * @attr: pointer to attribute struct - * @buf: buffer - * @len: length of buffer - */ -static ssize_t aim_attr_store(struct kobject *kobj, - struct attribute *attr, - const char *buf, - size_t len) -{ - struct most_aim_attribute *aim_attr; - struct most_aim_obj *aim_obj; - - aim_attr = to_aim_attr(attr); - aim_obj = to_aim_obj(kobj); - - if (!aim_attr->store) - return -EIO; - return aim_attr->store(aim_obj, aim_attr, buf, len); -} - -static const struct sysfs_ops most_aim_sysfs_ops = { - .show = aim_attr_show, - .store = aim_attr_store, -}; - -/** - * most_aim_release - AIM release function - * @kobj: pointer to AIM's kobject - */ -static void most_aim_release(struct kobject *kobj) -{ - struct most_aim_obj *aim_obj = to_aim_obj(kobj); - - kfree(aim_obj); -} - -static ssize_t links_show(struct most_aim_obj *aim_obj, - struct most_aim_attribute *attr, - char *buf) -{ - struct most_c_obj *c; - struct most_inst_obj *i; - int offs = 0; - - list_for_each_entry(i, &instance_list, list) { - list_for_each_entry(c, &i->channel_list, list) { - if (c->aim0.ptr == aim_obj->driver || - c->aim1.ptr == aim_obj->driver) { - offs += snprintf(buf + offs, PAGE_SIZE - offs, - "%s:%s\n", - kobject_name(&i->kobj), - kobject_name(&c->kobj)); - } - } - } - - return offs; -} - -/** - * split_string - parses and changes string in the buffer buf and - * splits it into two mandatory and one optional substrings. - * - * @buf: complete string from attribute 'add_channel' - * @a: address of pointer to 1st substring (=instance name) - * @b: address of pointer to 2nd substring (=channel name) - * @c: optional address of pointer to 3rd substring (=user defined name) - * - * Examples: - * - * Input: "mdev0:ch6:my_channel\n" or - * "mdev0:ch6:my_channel" - * - * Output: *a -> "mdev0", *b -> "ch6", *c -> "my_channel" - * - * Input: "mdev1:ep81\n" - * Output: *a -> "mdev1", *b -> "ep81", *c -> "" - * - * Input: "mdev1:ep81" - * Output: *a -> "mdev1", *b -> "ep81", *c == NULL - */ -static int split_string(char *buf, char **a, char **b, char **c) -{ - *a = strsep(&buf, ":"); - if (!*a) - return -EIO; - - *b = strsep(&buf, ":\n"); - if (!*b) - return -EIO; - - if (c) - *c = strsep(&buf, ":\n"); - - return 0; -} - -/** - * get_channel_by_name - get pointer to channel object - * @mdev: name of the device instance - * @mdev_ch: name of the respective channel - * - * This retrieves the pointer to a channel object. - */ -static struct -most_c_obj *get_channel_by_name(char *mdev, char *mdev_ch) -{ - struct most_c_obj *c, *tmp; - struct most_inst_obj *i, *i_tmp; - int found = 0; - - list_for_each_entry_safe(i, i_tmp, &instance_list, list) { - if (!strcmp(kobject_name(&i->kobj), mdev)) { - found++; - break; - } - } - if (unlikely(!found)) - return ERR_PTR(-EIO); - - list_for_each_entry_safe(c, tmp, &i->channel_list, list) { - if (!strcmp(kobject_name(&c->kobj), mdev_ch)) { - found++; - break; - } - } - if (unlikely(found < 2)) - return ERR_PTR(-EIO); - return c; -} - -/** - * add_link_store - store() function for add_link attribute - * @aim_obj: pointer to AIM object - * @attr: its attributes - * @buf: buffer - * @len: buffer length - * - * This parses the string given by buf and splits it into - * three substrings. Note: third substring is optional. In case a cdev - * AIM is loaded the optional 3rd substring will make up the name of - * device node in the /dev directory. If omitted, the device node will - * inherit the channel's name within sysfs. - * - * Searches for a pair of device and channel and probes the AIM - * - * Example: - * (1) echo "mdev0:ch6:my_rxchannel" >add_link - * (2) echo "mdev1:ep81" >add_link - * - * (1) would create the device node /dev/my_rxchannel - * (2) would create the device node /dev/mdev1-ep81 - */ -static ssize_t add_link_store(struct most_aim_obj *aim_obj, - struct most_aim_attribute *attr, - const char *buf, - size_t len) -{ - struct most_c_obj *c; - struct most_aim **aim_ptr; - char buffer[STRING_SIZE]; - char *mdev; - char *mdev_ch; - char *mdev_devnod; - char devnod_buf[STRING_SIZE]; - int ret; - size_t max_len = min_t(size_t, len + 1, STRING_SIZE); - - strlcpy(buffer, buf, max_len); - - ret = split_string(buffer, &mdev, &mdev_ch, &mdev_devnod); - if (ret) - return ret; - - if (!mdev_devnod || *mdev_devnod == 0) { - snprintf(devnod_buf, sizeof(devnod_buf), "%s-%s", mdev, - mdev_ch); - mdev_devnod = devnod_buf; - } - - c = get_channel_by_name(mdev, mdev_ch); - if (IS_ERR(c)) - return -ENODEV; - - if (!c->aim0.ptr) - aim_ptr = &c->aim0.ptr; - else if (!c->aim1.ptr) - aim_ptr = &c->aim1.ptr; - else - return -ENOSPC; - - *aim_ptr = aim_obj->driver; - ret = aim_obj->driver->probe_channel(c->iface, c->channel_id, - &c->cfg, &c->kobj, mdev_devnod); - if (ret) { - *aim_ptr = NULL; - return ret; - } - - return len; -} - -/** - * remove_link_store - store function for remove_link attribute - * @aim_obj: pointer to AIM object - * @attr: its attributes - * @buf: buffer - * @len: buffer length - * - * Example: - * echo "mdev0:ep81" >remove_link - */ -static ssize_t remove_link_store(struct most_aim_obj *aim_obj, - struct most_aim_attribute *attr, - const char *buf, - size_t len) -{ - struct most_c_obj *c; - char buffer[STRING_SIZE]; - char *mdev; - char *mdev_ch; - int ret; - size_t max_len = min_t(size_t, len + 1, STRING_SIZE); - - strlcpy(buffer, buf, max_len); - ret = split_string(buffer, &mdev, &mdev_ch, NULL); - if (ret) - return ret; - - c = get_channel_by_name(mdev, mdev_ch); - if (IS_ERR(c)) - return -ENODEV; - - if (aim_obj->driver->disconnect_channel(c->iface, c->channel_id)) - return -EIO; - if (c->aim0.ptr == aim_obj->driver) - c->aim0.ptr = NULL; - if (c->aim1.ptr == aim_obj->driver) - c->aim1.ptr = NULL; - return len; -} - -static struct most_aim_attribute most_aim_attrs[] = { - __ATTR_RO(links), - __ATTR_WO(add_link), - __ATTR_WO(remove_link), -}; - -static struct attribute *most_aim_def_attrs[] = { - &most_aim_attrs[0].attr, - &most_aim_attrs[1].attr, - &most_aim_attrs[2].attr, - NULL, -}; - -static struct kobj_type most_aim_ktype = { - .sysfs_ops = &most_aim_sysfs_ops, - .release = most_aim_release, - .default_attrs = most_aim_def_attrs, -}; - -static struct kset *most_aim_kset; - -/** - * create_most_aim_obj - creates an AIM object - * @name: name of the AIM - * - * This creates an AIM object assigns the proper kset and registers - * it with sysfs. - * Returns a pointer to the object or NULL if something went wrong. - */ -static struct most_aim_obj *create_most_aim_obj(const char *name) -{ - struct most_aim_obj *most_aim; - int retval; - - most_aim = kzalloc(sizeof(*most_aim), GFP_KERNEL); - if (!most_aim) - return NULL; - most_aim->kobj.kset = most_aim_kset; - retval = kobject_init_and_add(&most_aim->kobj, &most_aim_ktype, - NULL, "%s", name); - if (retval) { - kobject_put(&most_aim->kobj); - return NULL; - } - kobject_uevent(&most_aim->kobj, KOBJ_ADD); - return most_aim; -} - -/** - * destroy_most_aim_obj - AIM release function - * @p: pointer to AIM object - * - * This decrements the reference counter of the AIM object. If the - * reference count turns zero, its release function will be called. - */ -static void destroy_most_aim_obj(struct most_aim_obj *p) -{ - kobject_put(&p->kobj); -} - -/* ___ ___ - * ___C O R E___ - */ - -/** - * Instantiation of the MOST bus - */ -static struct bus_type most_bus = { - .name = "most", -}; - -/** - * Instantiation of the core driver - */ -static struct device_driver mostcore = { - .name = "mostcore", - .bus = &most_bus, -}; - -static inline void trash_mbo(struct mbo *mbo) -{ - unsigned long flags; - struct most_c_obj *c = mbo->context; - - spin_lock_irqsave(&c->fifo_lock, flags); - list_add(&mbo->list, &c->trash_fifo); - spin_unlock_irqrestore(&c->fifo_lock, flags); -} - -static bool hdm_mbo_ready(struct most_c_obj *c) -{ - bool empty; - - if (c->enqueue_halt) - return false; - - spin_lock_irq(&c->fifo_lock); - empty = list_empty(&c->halt_fifo); - spin_unlock_irq(&c->fifo_lock); - - return !empty; -} - -static void nq_hdm_mbo(struct mbo *mbo) -{ - unsigned long flags; - struct most_c_obj *c = mbo->context; - - spin_lock_irqsave(&c->fifo_lock, flags); - list_add_tail(&mbo->list, &c->halt_fifo); - spin_unlock_irqrestore(&c->fifo_lock, flags); - wake_up_interruptible(&c->hdm_fifo_wq); -} - -static int hdm_enqueue_thread(void *data) -{ - struct most_c_obj *c = data; - struct mbo *mbo; - int ret; - typeof(c->iface->enqueue) enqueue = c->iface->enqueue; - - while (likely(!kthread_should_stop())) { - wait_event_interruptible(c->hdm_fifo_wq, - hdm_mbo_ready(c) || - kthread_should_stop()); - - mutex_lock(&c->nq_mutex); - spin_lock_irq(&c->fifo_lock); - if (unlikely(c->enqueue_halt || list_empty(&c->halt_fifo))) { - spin_unlock_irq(&c->fifo_lock); - mutex_unlock(&c->nq_mutex); - continue; - } - - mbo = list_pop_mbo(&c->halt_fifo); - spin_unlock_irq(&c->fifo_lock); - - if (c->cfg.direction == MOST_CH_RX) - mbo->buffer_length = c->cfg.buffer_size; - - ret = enqueue(mbo->ifp, mbo->hdm_channel_id, mbo); - mutex_unlock(&c->nq_mutex); - - if (unlikely(ret)) { - pr_err("hdm enqueue failed\n"); - nq_hdm_mbo(mbo); - c->hdm_enqueue_task = NULL; - return 0; - } - } - - return 0; -} - -static int run_enqueue_thread(struct most_c_obj *c, int channel_id) -{ - struct task_struct *task = - kthread_run(hdm_enqueue_thread, c, "hdm_fifo_%d", - channel_id); - - if (IS_ERR(task)) - return PTR_ERR(task); - - c->hdm_enqueue_task = task; - return 0; -} - -/** - * arm_mbo - recycle MBO for further usage - * @mbo: buffer object - * - * This puts an MBO back to the list to have it ready for up coming - * tx transactions. - * - * In case the MBO belongs to a channel that recently has been - * poisoned, the MBO is scheduled to be trashed. - * Calls the completion handler of an attached AIM. - */ -static void arm_mbo(struct mbo *mbo) -{ - unsigned long flags; - struct most_c_obj *c; - - BUG_ON((!mbo) || (!mbo->context)); - c = mbo->context; - - if (c->is_poisoned) { - trash_mbo(mbo); - return; - } - - spin_lock_irqsave(&c->fifo_lock, flags); - ++*mbo->num_buffers_ptr; - list_add_tail(&mbo->list, &c->fifo); - spin_unlock_irqrestore(&c->fifo_lock, flags); - - if (c->aim0.refs && c->aim0.ptr->tx_completion) - c->aim0.ptr->tx_completion(c->iface, c->channel_id); - - if (c->aim1.refs && c->aim1.ptr->tx_completion) - c->aim1.ptr->tx_completion(c->iface, c->channel_id); -} - -/** - * arm_mbo_chain - helper function that arms an MBO chain for the HDM - * @c: pointer to interface channel - * @dir: direction of the channel - * @compl: pointer to completion function - * - * This allocates buffer objects including the containing DMA coherent - * buffer and puts them in the fifo. - * Buffers of Rx channels are put in the kthread fifo, hence immediately - * submitted to the HDM. - * - * Returns the number of allocated and enqueued MBOs. - */ -static int arm_mbo_chain(struct most_c_obj *c, int dir, - void (*compl)(struct mbo *)) -{ - unsigned int i; - int retval; - struct mbo *mbo; - u32 coherent_buf_size = c->cfg.buffer_size + c->cfg.extra_len; - - atomic_set(&c->mbo_nq_level, 0); - - for (i = 0; i < c->cfg.num_buffers; i++) { - mbo = kzalloc(sizeof(*mbo), GFP_KERNEL); - if (!mbo) { - retval = i; - goto _exit; - } - mbo->context = c; - mbo->ifp = c->iface; - mbo->hdm_channel_id = c->channel_id; - mbo->virt_address = dma_alloc_coherent(NULL, - coherent_buf_size, - &mbo->bus_address, - GFP_KERNEL); - if (!mbo->virt_address) { - pr_info("WARN: No DMA coherent buffer.\n"); - retval = i; - goto _error1; - } - mbo->complete = compl; - mbo->num_buffers_ptr = &dummy_num_buffers; - if (dir == MOST_CH_RX) { - nq_hdm_mbo(mbo); - atomic_inc(&c->mbo_nq_level); - } else { - arm_mbo(mbo); - } - } - return i; - -_error1: - kfree(mbo); -_exit: - return retval; -} - -/** - * most_submit_mbo - submits an MBO to fifo - * @mbo: pointer to the MBO - */ -void most_submit_mbo(struct mbo *mbo) -{ - if (WARN_ONCE(!mbo || !mbo->context, - "bad mbo or missing channel reference\n")) - return; - - nq_hdm_mbo(mbo); -} -EXPORT_SYMBOL_GPL(most_submit_mbo); - -/** - * most_write_completion - write completion handler - * @mbo: pointer to MBO - * - * This recycles the MBO for further usage. In case the channel has been - * poisoned, the MBO is scheduled to be trashed. - */ -static void most_write_completion(struct mbo *mbo) -{ - struct most_c_obj *c; - - BUG_ON((!mbo) || (!mbo->context)); - - c = mbo->context; - if (mbo->status == MBO_E_INVAL) - pr_info("WARN: Tx MBO status: invalid\n"); - if (unlikely(c->is_poisoned || (mbo->status == MBO_E_CLOSE))) - trash_mbo(mbo); - else - arm_mbo(mbo); -} - -/** - * get_channel_by_iface - get pointer to channel object - * @iface: pointer to interface instance - * @id: channel ID - * - * This retrieves a pointer to a channel of the given interface and channel ID. - */ -static struct -most_c_obj *get_channel_by_iface(struct most_interface *iface, int id) -{ - struct most_inst_obj *i; - - if (unlikely(!iface)) { - pr_err("Bad interface\n"); - return NULL; - } - if (unlikely((id < 0) || (id >= iface->num_channels))) { - pr_err("Channel index (%d) out of range\n", id); - return NULL; - } - i = iface->priv; - if (unlikely(!i)) { - pr_err("interface is not registered\n"); - return NULL; - } - return i->channel[id]; -} - -int channel_has_mbo(struct most_interface *iface, int id, struct most_aim *aim) -{ - struct most_c_obj *c = get_channel_by_iface(iface, id); - unsigned long flags; - int empty; - - if (unlikely(!c)) - return -EINVAL; - - if (c->aim0.refs && c->aim1.refs && - ((aim == c->aim0.ptr && c->aim0.num_buffers <= 0) || - (aim == c->aim1.ptr && c->aim1.num_buffers <= 0))) - return 0; - - spin_lock_irqsave(&c->fifo_lock, flags); - empty = list_empty(&c->fifo); - spin_unlock_irqrestore(&c->fifo_lock, flags); - return !empty; -} -EXPORT_SYMBOL_GPL(channel_has_mbo); - -/** - * most_get_mbo - get pointer to an MBO of pool - * @iface: pointer to interface instance - * @id: channel ID - * - * This attempts to get a free buffer out of the channel fifo. - * Returns a pointer to MBO on success or NULL otherwise. - */ -struct mbo *most_get_mbo(struct most_interface *iface, int id, - struct most_aim *aim) -{ - struct mbo *mbo; - struct most_c_obj *c; - unsigned long flags; - int *num_buffers_ptr; - - c = get_channel_by_iface(iface, id); - if (unlikely(!c)) - return NULL; - - if (c->aim0.refs && c->aim1.refs && - ((aim == c->aim0.ptr && c->aim0.num_buffers <= 0) || - (aim == c->aim1.ptr && c->aim1.num_buffers <= 0))) - return NULL; - - if (aim == c->aim0.ptr) - num_buffers_ptr = &c->aim0.num_buffers; - else if (aim == c->aim1.ptr) - num_buffers_ptr = &c->aim1.num_buffers; - else - num_buffers_ptr = &dummy_num_buffers; - - spin_lock_irqsave(&c->fifo_lock, flags); - if (list_empty(&c->fifo)) { - spin_unlock_irqrestore(&c->fifo_lock, flags); - return NULL; - } - mbo = list_pop_mbo(&c->fifo); - --*num_buffers_ptr; - spin_unlock_irqrestore(&c->fifo_lock, flags); - - mbo->num_buffers_ptr = num_buffers_ptr; - mbo->buffer_length = c->cfg.buffer_size; - return mbo; -} -EXPORT_SYMBOL_GPL(most_get_mbo); - -/** - * most_put_mbo - return buffer to pool - * @mbo: buffer object - */ -void most_put_mbo(struct mbo *mbo) -{ - struct most_c_obj *c = mbo->context; - - if (c->cfg.direction == MOST_CH_TX) { - arm_mbo(mbo); - return; - } - nq_hdm_mbo(mbo); - atomic_inc(&c->mbo_nq_level); -} -EXPORT_SYMBOL_GPL(most_put_mbo); - -/** - * most_read_completion - read completion handler - * @mbo: pointer to MBO - * - * This function is called by the HDM when data has been received from the - * hardware and copied to the buffer of the MBO. - * - * In case the channel has been poisoned it puts the buffer in the trash queue. - * Otherwise, it passes the buffer to an AIM for further processing. - */ -static void most_read_completion(struct mbo *mbo) -{ - struct most_c_obj *c = mbo->context; - - if (unlikely(c->is_poisoned || (mbo->status == MBO_E_CLOSE))) { - trash_mbo(mbo); - return; - } - - if (mbo->status == MBO_E_INVAL) { - nq_hdm_mbo(mbo); - atomic_inc(&c->mbo_nq_level); - return; - } - - if (atomic_sub_and_test(1, &c->mbo_nq_level)) - c->is_starving = 1; - - if (c->aim0.refs && c->aim0.ptr->rx_completion && - c->aim0.ptr->rx_completion(mbo) == 0) - return; - - if (c->aim1.refs && c->aim1.ptr->rx_completion && - c->aim1.ptr->rx_completion(mbo) == 0) - return; - - most_put_mbo(mbo); -} - -/** - * most_start_channel - prepares a channel for communication - * @iface: pointer to interface instance - * @id: channel ID - * - * This prepares the channel for usage. Cross-checks whether the - * channel's been properly configured. - * - * Returns 0 on success or error code otherwise. - */ -int most_start_channel(struct most_interface *iface, int id, - struct most_aim *aim) -{ - int num_buffer; - int ret; - struct most_c_obj *c = get_channel_by_iface(iface, id); - - if (unlikely(!c)) - return -EINVAL; - - mutex_lock(&c->start_mutex); - if (c->aim0.refs + c->aim1.refs > 0) - goto out; /* already started by other aim */ - - if (!try_module_get(iface->mod)) { - pr_info("failed to acquire HDM lock\n"); - mutex_unlock(&c->start_mutex); - return -ENOLCK; - } - - c->cfg.extra_len = 0; - if (c->iface->configure(c->iface, c->channel_id, &c->cfg)) { - pr_info("channel configuration failed. Go check settings...\n"); - ret = -EINVAL; - goto error; - } - - init_waitqueue_head(&c->hdm_fifo_wq); - - if (c->cfg.direction == MOST_CH_RX) - num_buffer = arm_mbo_chain(c, c->cfg.direction, - most_read_completion); - else - num_buffer = arm_mbo_chain(c, c->cfg.direction, - most_write_completion); - if (unlikely(!num_buffer)) { - pr_info("failed to allocate memory\n"); - ret = -ENOMEM; - goto error; - } - - ret = run_enqueue_thread(c, id); - if (ret) - goto error; - - c->is_starving = 0; - c->aim0.num_buffers = c->cfg.num_buffers / 2; - c->aim1.num_buffers = c->cfg.num_buffers - c->aim0.num_buffers; - atomic_set(&c->mbo_ref, num_buffer); - -out: - if (aim == c->aim0.ptr) - c->aim0.refs++; - if (aim == c->aim1.ptr) - c->aim1.refs++; - mutex_unlock(&c->start_mutex); - return 0; - -error: - module_put(iface->mod); - mutex_unlock(&c->start_mutex); - return ret; -} -EXPORT_SYMBOL_GPL(most_start_channel); - -/** - * most_stop_channel - stops a running channel - * @iface: pointer to interface instance - * @id: channel ID - */ -int most_stop_channel(struct most_interface *iface, int id, - struct most_aim *aim) -{ - struct most_c_obj *c; - - if (unlikely((!iface) || (id >= iface->num_channels) || (id < 0))) { - pr_err("Bad interface or index out of range\n"); - return -EINVAL; - } - c = get_channel_by_iface(iface, id); - if (unlikely(!c)) - return -EINVAL; - - mutex_lock(&c->start_mutex); - if (c->aim0.refs + c->aim1.refs >= 2) - goto out; - - if (c->hdm_enqueue_task) - kthread_stop(c->hdm_enqueue_task); - c->hdm_enqueue_task = NULL; - - if (iface->mod) - module_put(iface->mod); - - c->is_poisoned = true; - if (c->iface->poison_channel(c->iface, c->channel_id)) { - pr_err("Cannot stop channel %d of mdev %s\n", c->channel_id, - c->iface->description); - mutex_unlock(&c->start_mutex); - return -EAGAIN; - } - flush_trash_fifo(c); - flush_channel_fifos(c); - -#ifdef CMPL_INTERRUPTIBLE - if (wait_for_completion_interruptible(&c->cleanup)) { - pr_info("Interrupted while clean up ch %d\n", c->channel_id); - mutex_unlock(&c->start_mutex); - return -EINTR; - } -#else - wait_for_completion(&c->cleanup); -#endif - c->is_poisoned = false; - -out: - if (aim == c->aim0.ptr) - c->aim0.refs--; - if (aim == c->aim1.ptr) - c->aim1.refs--; - mutex_unlock(&c->start_mutex); - return 0; -} -EXPORT_SYMBOL_GPL(most_stop_channel); - -/** - * most_register_aim - registers an AIM (driver) with the core - * @aim: instance of AIM to be registered - */ -int most_register_aim(struct most_aim *aim) -{ - struct most_aim_obj *aim_obj; - - if (!aim) { - pr_err("Bad driver\n"); - return -EINVAL; - } - aim_obj = create_most_aim_obj(aim->name); - if (!aim_obj) { - pr_info("failed to alloc driver object\n"); - return -ENOMEM; - } - aim_obj->driver = aim; - aim->context = aim_obj; - pr_info("registered new application interfacing module %s\n", - aim->name); - list_add_tail(&aim_obj->list, &aim_list); - return 0; -} -EXPORT_SYMBOL_GPL(most_register_aim); - -/** - * most_deregister_aim - deregisters an AIM (driver) with the core - * @aim: AIM to be removed - */ -int most_deregister_aim(struct most_aim *aim) -{ - struct most_aim_obj *aim_obj; - struct most_c_obj *c, *tmp; - struct most_inst_obj *i, *i_tmp; - - if (!aim) { - pr_err("Bad driver\n"); - return -EINVAL; - } - - aim_obj = aim->context; - if (!aim_obj) { - pr_info("driver not registered.\n"); - return -EINVAL; - } - list_for_each_entry_safe(i, i_tmp, &instance_list, list) { - list_for_each_entry_safe(c, tmp, &i->channel_list, list) { - if (c->aim0.ptr == aim || c->aim1.ptr == aim) - aim->disconnect_channel( - c->iface, c->channel_id); - if (c->aim0.ptr == aim) - c->aim0.ptr = NULL; - if (c->aim1.ptr == aim) - c->aim1.ptr = NULL; - } - } - list_del(&aim_obj->list); - destroy_most_aim_obj(aim_obj); - pr_info("deregistering application interfacing module %s\n", aim->name); - return 0; -} -EXPORT_SYMBOL_GPL(most_deregister_aim); - -/** - * most_register_interface - registers an interface with core - * @iface: pointer to the instance of the interface description. - * - * Allocates and initializes a new interface instance and all of its channels. - * Returns a pointer to kobject or an error pointer. - */ -struct kobject *most_register_interface(struct most_interface *iface) -{ - unsigned int i; - int id; - char name[STRING_SIZE]; - char channel_name[STRING_SIZE]; - struct most_c_obj *c; - struct most_inst_obj *inst; - - if (!iface || !iface->enqueue || !iface->configure || - !iface->poison_channel || (iface->num_channels > MAX_CHANNELS)) { - pr_err("Bad interface or channel overflow\n"); - return ERR_PTR(-EINVAL); - } - - id = ida_simple_get(&mdev_id, 0, 0, GFP_KERNEL); - if (id < 0) { - pr_info("Failed to alloc mdev ID\n"); - return ERR_PTR(id); - } - snprintf(name, STRING_SIZE, "mdev%d", id); - - inst = create_most_inst_obj(name); - if (!inst) { - pr_info("Failed to allocate interface instance\n"); - ida_simple_remove(&mdev_id, id); - return ERR_PTR(-ENOMEM); - } - - iface->priv = inst; - INIT_LIST_HEAD(&inst->channel_list); - inst->iface = iface; - inst->dev_id = id; - list_add_tail(&inst->list, &instance_list); - - for (i = 0; i < iface->num_channels; i++) { - const char *name_suffix = iface->channel_vector[i].name_suffix; - - if (!name_suffix) - snprintf(channel_name, STRING_SIZE, "ch%d", i); - else - snprintf(channel_name, STRING_SIZE, "%s", name_suffix); - - /* this increments the reference count of this instance */ - c = create_most_c_obj(channel_name, &inst->kobj); - if (!c) - goto free_instance; - inst->channel[i] = c; - c->is_starving = 0; - c->iface = iface; - c->inst = inst; - c->channel_id = i; - c->keep_mbo = false; - c->enqueue_halt = false; - c->is_poisoned = false; - c->cfg.direction = 0; - c->cfg.data_type = 0; - c->cfg.num_buffers = 0; - c->cfg.buffer_size = 0; - c->cfg.subbuffer_size = 0; - c->cfg.packets_per_xact = 0; - spin_lock_init(&c->fifo_lock); - INIT_LIST_HEAD(&c->fifo); - INIT_LIST_HEAD(&c->trash_fifo); - INIT_LIST_HEAD(&c->halt_fifo); - init_completion(&c->cleanup); - atomic_set(&c->mbo_ref, 0); - mutex_init(&c->start_mutex); - mutex_init(&c->nq_mutex); - list_add_tail(&c->list, &inst->channel_list); - } - pr_info("registered new MOST device mdev%d (%s)\n", - inst->dev_id, iface->description); - return &inst->kobj; - -free_instance: - pr_info("Failed allocate channel(s)\n"); - list_del(&inst->list); - ida_simple_remove(&mdev_id, id); - destroy_most_inst_obj(inst); - return ERR_PTR(-ENOMEM); -} -EXPORT_SYMBOL_GPL(most_register_interface); - -/** - * most_deregister_interface - deregisters an interface with core - * @iface: pointer to the interface instance description. - * - * Before removing an interface instance from the list, all running - * channels are stopped and poisoned. - */ -void most_deregister_interface(struct most_interface *iface) -{ - struct most_inst_obj *i = iface->priv; - struct most_c_obj *c; - - if (unlikely(!i)) { - pr_info("Bad Interface\n"); - return; - } - pr_info("deregistering MOST device %s (%s)\n", i->kobj.name, - iface->description); - - list_for_each_entry(c, &i->channel_list, list) { - if (c->aim0.ptr) - c->aim0.ptr->disconnect_channel(c->iface, - c->channel_id); - if (c->aim1.ptr) - c->aim1.ptr->disconnect_channel(c->iface, - c->channel_id); - c->aim0.ptr = NULL; - c->aim1.ptr = NULL; - } - - ida_simple_remove(&mdev_id, i->dev_id); - list_del(&i->list); - destroy_most_inst_obj(i); -} -EXPORT_SYMBOL_GPL(most_deregister_interface); - -/** - * most_stop_enqueue - prevents core from enqueueing MBOs - * @iface: pointer to interface - * @id: channel id - * - * This is called by an HDM that _cannot_ attend to its duties and - * is imminent to get run over by the core. The core is not going to - * enqueue any further packets unless the flagging HDM calls - * most_resume enqueue(). - */ -void most_stop_enqueue(struct most_interface *iface, int id) -{ - struct most_c_obj *c = get_channel_by_iface(iface, id); - - if (!c) - return; - - mutex_lock(&c->nq_mutex); - c->enqueue_halt = true; - mutex_unlock(&c->nq_mutex); -} -EXPORT_SYMBOL_GPL(most_stop_enqueue); - -/** - * most_resume_enqueue - allow core to enqueue MBOs again - * @iface: pointer to interface - * @id: channel id - * - * This clears the enqueue halt flag and enqueues all MBOs currently - * sitting in the wait fifo. - */ -void most_resume_enqueue(struct most_interface *iface, int id) -{ - struct most_c_obj *c = get_channel_by_iface(iface, id); - - if (!c) - return; - - mutex_lock(&c->nq_mutex); - c->enqueue_halt = false; - mutex_unlock(&c->nq_mutex); - - wake_up_interruptible(&c->hdm_fifo_wq); -} -EXPORT_SYMBOL_GPL(most_resume_enqueue); - -static int __init most_init(void) -{ - int err; - - pr_info("init()\n"); - INIT_LIST_HEAD(&instance_list); - INIT_LIST_HEAD(&aim_list); - ida_init(&mdev_id); - - err = bus_register(&most_bus); - if (err) { - pr_info("Cannot register most bus\n"); - return err; - } - - most_class = class_create(THIS_MODULE, "most"); - if (IS_ERR(most_class)) { - pr_info("No udev support.\n"); - err = PTR_ERR(most_class); - goto exit_bus; - } - - err = driver_register(&mostcore); - if (err) { - pr_info("Cannot register core driver\n"); - goto exit_class; - } - - core_dev = device_create(most_class, NULL, 0, NULL, "mostcore"); - if (IS_ERR(core_dev)) { - err = PTR_ERR(core_dev); - goto exit_driver; - } - - most_aim_kset = kset_create_and_add("aims", NULL, &core_dev->kobj); - if (!most_aim_kset) { - err = -ENOMEM; - goto exit_class_container; - } - - most_inst_kset = kset_create_and_add("devices", NULL, &core_dev->kobj); - if (!most_inst_kset) { - err = -ENOMEM; - goto exit_driver_kset; - } - - return 0; - -exit_driver_kset: - kset_unregister(most_aim_kset); -exit_class_container: - device_destroy(most_class, 0); -exit_driver: - driver_unregister(&mostcore); -exit_class: - class_destroy(most_class); -exit_bus: - bus_unregister(&most_bus); - return err; -} - -static void __exit most_exit(void) -{ - struct most_inst_obj *i, *i_tmp; - struct most_aim_obj *d, *d_tmp; - - pr_info("exit core module\n"); - list_for_each_entry_safe(d, d_tmp, &aim_list, list) { - destroy_most_aim_obj(d); - } - - list_for_each_entry_safe(i, i_tmp, &instance_list, list) { - list_del(&i->list); - destroy_most_inst_obj(i); - } - kset_unregister(most_inst_kset); - kset_unregister(most_aim_kset); - device_destroy(most_class, 0); - driver_unregister(&mostcore); - class_destroy(most_class); - bus_unregister(&most_bus); - ida_destroy(&mdev_id); -} - -module_init(most_init); -module_exit(most_exit); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Christian Gromm "); -MODULE_DESCRIPTION("Core module of stacked MOST Linux driver"); diff --git a/drivers/staging/most/mostcore/mostcore.h b/drivers/staging/most/mostcore/mostcore.h deleted file mode 100644 index f6fdba0c5eaf..000000000000 --- a/drivers/staging/most/mostcore/mostcore.h +++ /dev/null @@ -1,319 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * mostcore.h - Interface between MostCore, - * Hardware Dependent Module (HDM) and Application Interface Module (AIM). - * - * Copyright (C) 2013-2015, Microchip Technology Germany II GmbH & Co. KG - */ - -/* - * Authors: - * Andrey Shvetsov - * Christian Gromm - * Sebastian Graf - */ - -#ifndef __MOST_CORE_H__ -#define __MOST_CORE_H__ - -#include - -struct kobject; -struct module; - -/** - * Interface type - */ -enum most_interface_type { - ITYPE_LOOPBACK = 1, - ITYPE_I2C, - ITYPE_I2S, - ITYPE_TSI, - ITYPE_HBI, - ITYPE_MEDIALB_DIM, - ITYPE_MEDIALB_DIM2, - ITYPE_USB, - ITYPE_PCIE -}; - -/** - * Channel direction. - */ -enum most_channel_direction { - MOST_CH_RX = 1 << 0, - MOST_CH_TX = 1 << 1, -}; - -/** - * Channel data type. - */ -enum most_channel_data_type { - MOST_CH_CONTROL = 1 << 0, - MOST_CH_ASYNC = 1 << 1, - MOST_CH_ISOC = 1 << 2, - MOST_CH_SYNC = 1 << 5, -}; - -enum mbo_status_flags { - /* MBO was processed successfully (data was send or received )*/ - MBO_SUCCESS = 0, - /* The MBO contains wrong or missing information. */ - MBO_E_INVAL, - /* MBO was completed as HDM Channel will be closed */ - MBO_E_CLOSE, -}; - -/** - * struct most_channel_capability - Channel capability - * @direction: Supported channel directions. - * The value is bitwise OR-combination of the values from the - * enumeration most_channel_direction. Zero is allowed value and means - * "channel may not be used". - * @data_type: Supported channel data types. - * The value is bitwise OR-combination of the values from the - * enumeration most_channel_data_type. Zero is allowed value and means - * "channel may not be used". - * @num_buffer_packet: Maximum number of buffers supported by this channel - * for packet data types (Async,Control,QoS) - * @buffer_size_packet: Maximum buffer size supported by this channel - * for packet data types (Async,Control,QoS) - * @num_buffer_streaming: Maximum number of buffers supported by this channel - * for streaming data types (Sync,AV Packetized) - * @buffer_size_streaming: Maximum buffer size supported by this channel - * for streaming data types (Sync,AV Packetized) - * @name_suffix: Optional suffix providean by an HDM that is attached to the - * regular channel name. - * - * Describes the capabilities of a MostCore channel like supported Data Types - * and directions. This information is provided by an HDM for the MostCore. - * - * The Core creates read only sysfs attribute files in - * /sys/devices/virtual/most/mostcore/devices/mdev-#/mdev#-ch#/ with the - * following attributes: - * -available_directions - * -available_datatypes - * -number_of_packet_buffers - * -number_of_stream_buffers - * -size_of_packet_buffer - * -size_of_stream_buffer - * where content of each file is a string with all supported properties of this - * very channel attribute. - */ -struct most_channel_capability { - u16 direction; - u16 data_type; - u16 num_buffers_packet; - u16 buffer_size_packet; - u16 num_buffers_streaming; - u16 buffer_size_streaming; - const char *name_suffix; -}; - -/** - * struct most_channel_config - stores channel configuration - * @direction: direction of the channel - * @data_type: data type travelling over this channel - * @num_buffers: number of buffers - * @buffer_size: size of a buffer for AIM. - * Buffer size may be cutted down by HDM in a configure callback - * to match to a given interface and channel type. - * @extra_len: additional buffer space for internal HDM purposes like padding. - * May be set by HDM in a configure callback if needed. - * @subbuffer_size: size of a subbuffer - * @packets_per_xact: number of MOST frames that are packet inside one USB - * packet. This is USB specific - * - * Describes the configuration for a MostCore channel. This information is - * provided from the MostCore to a HDM (like the Medusa PCIe Interface) as a - * parameter of the "configure" function call. - */ -struct most_channel_config { - enum most_channel_direction direction; - enum most_channel_data_type data_type; - u16 num_buffers; - u16 buffer_size; - u16 extra_len; - u16 subbuffer_size; - u16 packets_per_xact; -}; - -/* - * struct mbo - MOST Buffer Object. - * @context: context for core completion handler - * @priv: private data for HDM - * - * public: documented fields that are used for the communications - * between MostCore and HDMs - * - * @list: list head for use by the mbo's current owner - * @ifp: (in) associated interface instance - * @hdm_channel_id: (in) HDM channel instance - * @virt_address: (in) kernel virtual address of the buffer - * @bus_address: (in) bus address of the buffer - * @buffer_length: (in) buffer payload length - * @processed_length: (out) processed length - * @status: (out) transfer status - * @complete: (in) completion routine - * - * The MostCore allocates and initializes the MBO. - * - * The HDM receives MBO for transfer from MostCore with the call to enqueue(). - * The HDM copies the data to- or from the buffer depending on configured - * channel direction, set "processed_length" and "status" and completes - * the transfer procedure by calling the completion routine. - * - * At the end the MostCore deallocates the MBO or recycles it for further - * transfers for the same or different HDM. - * - * Directions of usage: - * The core driver should never access any MBO fields (even if marked - * as "public") while the MBO is owned by an HDM. The ownership starts with - * the call of enqueue() and ends with the call of its complete() routine. - * - * II. - * Every HDM attached to the core driver _must_ ensure that it returns any MBO - * it owns (due to a previous call to enqueue() by the core driver) before it - * de-registers an interface or gets unloaded from the kernel. If this direction - * is violated memory leaks will occur, since the core driver does _not_ track - * MBOs it is currently not in control of. - * - */ -struct mbo { - void *context; - void *priv; - struct list_head list; - struct most_interface *ifp; - int *num_buffers_ptr; - u16 hdm_channel_id; - void *virt_address; - dma_addr_t bus_address; - u16 buffer_length; - u16 processed_length; - enum mbo_status_flags status; - void (*complete)(struct mbo *); -}; - -/** - * Interface instance description. - * - * Describes one instance of an interface like Medusa PCIe or Vantage USB. - * This structure is allocated and initialized in the HDM. MostCore may not - * modify this structure. - * - * @interface Interface type. \sa most_interface_type. - * @description PRELIMINARY. - * Unique description of the device instance from point of view of the - * interface in free text form (ASCII). - * It may be a hexadecimal presentation of the memory address for the MediaLB - * IP or USB device ID with USB properties for USB interface, etc. - * @num_channels Number of channels and size of the channel_vector. - * @channel_vector Properties of the channels. - * Array index represents channel ID by the driver. - * @configure Callback to change data type for the channel of the - * interface instance. May be zero if the instance of the interface is not - * configurable. Parameter channel_config describes direction and data - * type for the channel, configured by the higher level. The content of - * @enqueue Delivers MBO to the HDM for processing. - * After HDM completes Rx- or Tx- operation the processed MBO shall - * be returned back to the MostCore using completion routine. - * The reason to get the MBO delivered from the MostCore after the channel - * is poisoned is the re-opening of the channel by the application. - * In this case the HDM shall hold MBOs and service the channel as usual. - * The HDM must be able to hold at least one MBO for each channel. - * The callback returns a negative value on error, otherwise 0. - * @poison_channel Informs HDM about closing the channel. The HDM shall - * cancel all transfers and synchronously or asynchronously return - * all enqueued for this channel MBOs using the completion routine. - * The callback returns a negative value on error, otherwise 0. - * @request_netinfo: triggers retrieving of network info from the HDM by - * means of "Message exchange over MDP/MEP" - * The call of the function request_netinfo with the parameter on_netinfo as - * NULL prohibits use of the previously obtained function pointer. - * @priv Private field used by mostcore to store context information. - */ -struct most_interface { - struct module *mod; - enum most_interface_type interface; - const char *description; - int num_channels; - struct most_channel_capability *channel_vector; - int (*configure)(struct most_interface *iface, int channel_idx, - struct most_channel_config *channel_config); - int (*enqueue)(struct most_interface *iface, int channel_idx, - struct mbo *mbo); - int (*poison_channel)(struct most_interface *iface, int channel_idx); - void (*request_netinfo)(struct most_interface *iface, int channel_idx, - void (*on_netinfo)(struct most_interface *iface, - unsigned char link_stat, - unsigned char *mac_addr)); - void *priv; -}; - -/** - * struct most_aim - identifies MOST device driver to mostcore - * @name: Driver name - * @probe_channel: function for core to notify driver about channel connection - * @disconnect_channel: callback function to disconnect a certain channel - * @rx_completion: completion handler for received packets - * @tx_completion: completion handler for transmitted packets - * @context: context pointer to be used by mostcore - */ -struct most_aim { - const char *name; - int (*probe_channel)(struct most_interface *iface, int channel_idx, - struct most_channel_config *cfg, - struct kobject *parent, char *name); - int (*disconnect_channel)(struct most_interface *iface, - int channel_idx); - int (*rx_completion)(struct mbo *mbo); - int (*tx_completion)(struct most_interface *iface, int channel_idx); - void *context; -}; - -/** - * most_register_interface - Registers instance of the interface. - * @iface: Pointer to the interface instance description. - * - * Returns a pointer to the kobject of the generated instance. - * - * Note: HDM has to ensure that any reference held on the kobj is - * released before deregistering the interface. - */ -struct kobject *most_register_interface(struct most_interface *iface); - -/** - * Deregisters instance of the interface. - * @intf_instance Pointer to the interface instance description. - */ -void most_deregister_interface(struct most_interface *iface); -void most_submit_mbo(struct mbo *mbo); - -/** - * most_stop_enqueue - prevents core from enqueing MBOs - * @iface: pointer to interface - * @channel_idx: channel index - */ -void most_stop_enqueue(struct most_interface *iface, int channel_idx); - -/** - * most_resume_enqueue - allow core to enqueue MBOs again - * @iface: pointer to interface - * @channel_idx: channel index - * - * This clears the enqueue halt flag and enqueues all MBOs currently - * in wait fifo. - */ -void most_resume_enqueue(struct most_interface *iface, int channel_idx); -int most_register_aim(struct most_aim *aim); -int most_deregister_aim(struct most_aim *aim); -struct mbo *most_get_mbo(struct most_interface *iface, int channel_idx, - struct most_aim *); -void most_put_mbo(struct mbo *mbo); -int channel_has_mbo(struct most_interface *iface, int channel_idx, - struct most_aim *aim); -int most_start_channel(struct most_interface *iface, int channel_idx, - struct most_aim *); -int most_stop_channel(struct most_interface *iface, int channel_idx, - struct most_aim *); - -#endif /* MOST_CORE_H_ */ -- cgit From 7b9cdcf6ea8062b4a003b0a06601d94a52f95eeb Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:36 +0100 Subject: staging: most: cdev: rename module This patch renames the folder of the cdev module. This is needed to clear the directory layout. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/Kconfig | 2 +- drivers/staging/most/aim-cdev/Kconfig | 12 - drivers/staging/most/aim-cdev/cdev.c | 558 ---------------------------------- drivers/staging/most/cdev/Kconfig | 12 + drivers/staging/most/cdev/Makefile | 4 + drivers/staging/most/cdev/cdev.c | 558 ++++++++++++++++++++++++++++++++++ 6 files changed, 575 insertions(+), 571 deletions(-) delete mode 100644 drivers/staging/most/aim-cdev/Kconfig delete mode 100644 drivers/staging/most/aim-cdev/cdev.c create mode 100644 drivers/staging/most/cdev/Kconfig create mode 100644 drivers/staging/most/cdev/Makefile create mode 100644 drivers/staging/most/cdev/cdev.c (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/Kconfig b/drivers/staging/most/Kconfig index 2045f7ac5081..60a2825e6076 100644 --- a/drivers/staging/most/Kconfig +++ b/drivers/staging/most/Kconfig @@ -17,7 +17,7 @@ menuconfig MOST if MOST -source "drivers/staging/most/aim-cdev/Kconfig" +source "drivers/staging/most/cdev/Kconfig" source "drivers/staging/most/aim-network/Kconfig" diff --git a/drivers/staging/most/aim-cdev/Kconfig b/drivers/staging/most/aim-cdev/Kconfig deleted file mode 100644 index 3c59f1bac127..000000000000 --- a/drivers/staging/most/aim-cdev/Kconfig +++ /dev/null @@ -1,12 +0,0 @@ -# -# MOST Cdev configuration -# - -config AIM_CDEV - tristate "Cdev AIM" - - ---help--- - Say Y here if you want to commumicate via character devices. - - To compile this driver as a module, choose M here: the - module will be called aim_cdev. \ No newline at end of file diff --git a/drivers/staging/most/aim-cdev/cdev.c b/drivers/staging/most/aim-cdev/cdev.c deleted file mode 100644 index 3ae9246c0c5c..000000000000 --- a/drivers/staging/most/aim-cdev/cdev.c +++ /dev/null @@ -1,558 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * cdev.c - Application interfacing module for character devices - * - * Copyright (C) 2013-2015 Microchip Technology Germany II GmbH & Co. KG - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "most/core.h" - -static dev_t aim_devno; -static struct class *aim_class; -static struct ida minor_id; -static unsigned int major; -static struct most_aim cdev_aim; - -struct aim_channel { - wait_queue_head_t wq; - spinlock_t unlink; /* synchronization lock to unlink channels */ - struct cdev cdev; - struct device *dev; - struct mutex io_mutex; - struct most_interface *iface; - struct most_channel_config *cfg; - unsigned int channel_id; - dev_t devno; - size_t mbo_offs; - DECLARE_KFIFO_PTR(fifo, typeof(struct mbo *)); - int access_ref; - struct list_head list; -}; - -#define to_channel(d) container_of(d, struct aim_channel, cdev) -static struct list_head channel_list; -static spinlock_t ch_list_lock; - -static inline bool ch_has_mbo(struct aim_channel *c) -{ - return channel_has_mbo(c->iface, c->channel_id, &cdev_aim) > 0; -} - -static inline bool ch_get_mbo(struct aim_channel *c, struct mbo **mbo) -{ - if (!kfifo_peek(&c->fifo, mbo)) { - *mbo = most_get_mbo(c->iface, c->channel_id, &cdev_aim); - if (*mbo) - kfifo_in(&c->fifo, mbo, 1); - } - return *mbo; -} - -static struct aim_channel *get_channel(struct most_interface *iface, int id) -{ - struct aim_channel *c, *tmp; - unsigned long flags; - int found_channel = 0; - - spin_lock_irqsave(&ch_list_lock, flags); - list_for_each_entry_safe(c, tmp, &channel_list, list) { - if ((c->iface == iface) && (c->channel_id == id)) { - found_channel = 1; - break; - } - } - spin_unlock_irqrestore(&ch_list_lock, flags); - if (!found_channel) - return NULL; - return c; -} - -static void stop_channel(struct aim_channel *c) -{ - struct mbo *mbo; - - while (kfifo_out((struct kfifo *)&c->fifo, &mbo, 1)) - most_put_mbo(mbo); - most_stop_channel(c->iface, c->channel_id, &cdev_aim); -} - -static void destroy_cdev(struct aim_channel *c) -{ - unsigned long flags; - - device_destroy(aim_class, c->devno); - cdev_del(&c->cdev); - spin_lock_irqsave(&ch_list_lock, flags); - list_del(&c->list); - spin_unlock_irqrestore(&ch_list_lock, flags); -} - -static void destroy_channel(struct aim_channel *c) -{ - ida_simple_remove(&minor_id, MINOR(c->devno)); - kfifo_free(&c->fifo); - kfree(c); -} - -/** - * aim_open - implements the syscall to open the device - * @inode: inode pointer - * @filp: file pointer - * - * This stores the channel pointer in the private data field of - * the file structure and activates the channel within the core. - */ -static int aim_open(struct inode *inode, struct file *filp) -{ - struct aim_channel *c; - int ret; - - c = to_channel(inode->i_cdev); - filp->private_data = c; - - if (((c->cfg->direction == MOST_CH_RX) && - ((filp->f_flags & O_ACCMODE) != O_RDONLY)) || - ((c->cfg->direction == MOST_CH_TX) && - ((filp->f_flags & O_ACCMODE) != O_WRONLY))) { - pr_info("WARN: Access flags mismatch\n"); - return -EACCES; - } - - mutex_lock(&c->io_mutex); - if (!c->dev) { - pr_info("WARN: Device is destroyed\n"); - mutex_unlock(&c->io_mutex); - return -ENODEV; - } - - if (c->access_ref) { - pr_info("WARN: Device is busy\n"); - mutex_unlock(&c->io_mutex); - return -EBUSY; - } - - c->mbo_offs = 0; - ret = most_start_channel(c->iface, c->channel_id, &cdev_aim); - if (!ret) - c->access_ref = 1; - mutex_unlock(&c->io_mutex); - return ret; -} - -/** - * aim_close - implements the syscall to close the device - * @inode: inode pointer - * @filp: file pointer - * - * This stops the channel within the core. - */ -static int aim_close(struct inode *inode, struct file *filp) -{ - struct aim_channel *c = to_channel(inode->i_cdev); - - mutex_lock(&c->io_mutex); - spin_lock(&c->unlink); - c->access_ref = 0; - spin_unlock(&c->unlink); - if (c->dev) { - stop_channel(c); - mutex_unlock(&c->io_mutex); - } else { - mutex_unlock(&c->io_mutex); - destroy_channel(c); - } - return 0; -} - -/** - * aim_write - implements the syscall to write to the device - * @filp: file pointer - * @buf: pointer to user buffer - * @count: number of bytes to write - * @offset: offset from where to start writing - */ -static ssize_t aim_write(struct file *filp, const char __user *buf, - size_t count, loff_t *offset) -{ - int ret; - size_t to_copy, left; - struct mbo *mbo = NULL; - struct aim_channel *c = filp->private_data; - - mutex_lock(&c->io_mutex); - while (c->dev && !ch_get_mbo(c, &mbo)) { - mutex_unlock(&c->io_mutex); - - if ((filp->f_flags & O_NONBLOCK)) - return -EAGAIN; - if (wait_event_interruptible(c->wq, ch_has_mbo(c) || !c->dev)) - return -ERESTARTSYS; - mutex_lock(&c->io_mutex); - } - - if (unlikely(!c->dev)) { - ret = -ENODEV; - goto unlock; - } - - to_copy = min(count, c->cfg->buffer_size - c->mbo_offs); - left = copy_from_user(mbo->virt_address + c->mbo_offs, buf, to_copy); - if (left == to_copy) { - ret = -EFAULT; - goto unlock; - } - - c->mbo_offs += to_copy - left; - if (c->mbo_offs >= c->cfg->buffer_size || - c->cfg->data_type == MOST_CH_CONTROL || - c->cfg->data_type == MOST_CH_ASYNC) { - kfifo_skip(&c->fifo); - mbo->buffer_length = c->mbo_offs; - c->mbo_offs = 0; - most_submit_mbo(mbo); - } - - ret = to_copy - left; -unlock: - mutex_unlock(&c->io_mutex); - return ret; -} - -/** - * aim_read - implements the syscall to read from the device - * @filp: file pointer - * @buf: pointer to user buffer - * @count: number of bytes to read - * @offset: offset from where to start reading - */ -static ssize_t -aim_read(struct file *filp, char __user *buf, size_t count, loff_t *offset) -{ - size_t to_copy, not_copied, copied; - struct mbo *mbo; - struct aim_channel *c = filp->private_data; - - mutex_lock(&c->io_mutex); - while (c->dev && !kfifo_peek(&c->fifo, &mbo)) { - mutex_unlock(&c->io_mutex); - if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; - if (wait_event_interruptible(c->wq, - (!kfifo_is_empty(&c->fifo) || - (!c->dev)))) - return -ERESTARTSYS; - mutex_lock(&c->io_mutex); - } - - /* make sure we don't submit to gone devices */ - if (unlikely(!c->dev)) { - mutex_unlock(&c->io_mutex); - return -ENODEV; - } - - to_copy = min_t(size_t, - count, - mbo->processed_length - c->mbo_offs); - - not_copied = copy_to_user(buf, - mbo->virt_address + c->mbo_offs, - to_copy); - - copied = to_copy - not_copied; - - c->mbo_offs += copied; - if (c->mbo_offs >= mbo->processed_length) { - kfifo_skip(&c->fifo); - most_put_mbo(mbo); - c->mbo_offs = 0; - } - mutex_unlock(&c->io_mutex); - return copied; -} - -static unsigned int aim_poll(struct file *filp, poll_table *wait) -{ - struct aim_channel *c = filp->private_data; - unsigned int mask = 0; - - poll_wait(filp, &c->wq, wait); - - if (c->cfg->direction == MOST_CH_RX) { - if (!kfifo_is_empty(&c->fifo)) - mask |= POLLIN | POLLRDNORM; - } else { - if (!kfifo_is_empty(&c->fifo) || ch_has_mbo(c)) - mask |= POLLOUT | POLLWRNORM; - } - return mask; -} - -/** - * Initialization of struct file_operations - */ -static const struct file_operations channel_fops = { - .owner = THIS_MODULE, - .read = aim_read, - .write = aim_write, - .open = aim_open, - .release = aim_close, - .poll = aim_poll, -}; - -/** - * aim_disconnect_channel - disconnect a channel - * @iface: pointer to interface instance - * @channel_id: channel index - * - * This frees allocated memory and removes the cdev that represents this - * channel in user space. - */ -static int aim_disconnect_channel(struct most_interface *iface, int channel_id) -{ - struct aim_channel *c; - - if (!iface) { - pr_info("Bad interface pointer\n"); - return -EINVAL; - } - - c = get_channel(iface, channel_id); - if (!c) - return -ENXIO; - - mutex_lock(&c->io_mutex); - spin_lock(&c->unlink); - c->dev = NULL; - spin_unlock(&c->unlink); - destroy_cdev(c); - if (c->access_ref) { - stop_channel(c); - wake_up_interruptible(&c->wq); - mutex_unlock(&c->io_mutex); - } else { - mutex_unlock(&c->io_mutex); - destroy_channel(c); - } - return 0; -} - -/** - * aim_rx_completion - completion handler for rx channels - * @mbo: pointer to buffer object that has completed - * - * This searches for the channel linked to this MBO and stores it in the local - * fifo buffer. - */ -static int aim_rx_completion(struct mbo *mbo) -{ - struct aim_channel *c; - - if (!mbo) - return -EINVAL; - - c = get_channel(mbo->ifp, mbo->hdm_channel_id); - if (!c) - return -ENXIO; - - spin_lock(&c->unlink); - if (!c->access_ref || !c->dev) { - spin_unlock(&c->unlink); - return -ENODEV; - } - kfifo_in(&c->fifo, &mbo, 1); - spin_unlock(&c->unlink); -#ifdef DEBUG_MESG - if (kfifo_is_full(&c->fifo)) - pr_info("WARN: Fifo is full\n"); -#endif - wake_up_interruptible(&c->wq); - return 0; -} - -/** - * aim_tx_completion - completion handler for tx channels - * @iface: pointer to interface instance - * @channel_id: channel index/ID - * - * This wakes sleeping processes in the wait-queue. - */ -static int aim_tx_completion(struct most_interface *iface, int channel_id) -{ - struct aim_channel *c; - - if (!iface) { - pr_info("Bad interface pointer\n"); - return -EINVAL; - } - if ((channel_id < 0) || (channel_id >= iface->num_channels)) { - pr_info("Channel ID out of range\n"); - return -EINVAL; - } - - c = get_channel(iface, channel_id); - if (!c) - return -ENXIO; - wake_up_interruptible(&c->wq); - return 0; -} - -/** - * aim_probe - probe function of the driver module - * @iface: pointer to interface instance - * @channel_id: channel index/ID - * @cfg: pointer to actual channel configuration - * @parent: pointer to kobject (needed for sysfs hook-up) - * @name: name of the device to be created - * - * This allocates achannel object and creates the device node in /dev - * - * Returns 0 on success or error code otherwise. - */ -static int aim_probe(struct most_interface *iface, int channel_id, - struct most_channel_config *cfg, - struct kobject *parent, char *name) -{ - struct aim_channel *c; - unsigned long cl_flags; - int retval; - int current_minor; - - if ((!iface) || (!cfg) || (!parent) || (!name)) { - pr_info("Probing AIM with bad arguments"); - return -EINVAL; - } - c = get_channel(iface, channel_id); - if (c) - return -EEXIST; - - current_minor = ida_simple_get(&minor_id, 0, 0, GFP_KERNEL); - if (current_minor < 0) - return current_minor; - - c = kzalloc(sizeof(*c), GFP_KERNEL); - if (!c) { - retval = -ENOMEM; - goto error_alloc_channel; - } - - c->devno = MKDEV(major, current_minor); - cdev_init(&c->cdev, &channel_fops); - c->cdev.owner = THIS_MODULE; - cdev_add(&c->cdev, c->devno, 1); - c->iface = iface; - c->cfg = cfg; - c->channel_id = channel_id; - c->access_ref = 0; - spin_lock_init(&c->unlink); - INIT_KFIFO(c->fifo); - retval = kfifo_alloc(&c->fifo, cfg->num_buffers, GFP_KERNEL); - if (retval) { - pr_info("failed to alloc channel kfifo"); - goto error_alloc_kfifo; - } - init_waitqueue_head(&c->wq); - mutex_init(&c->io_mutex); - spin_lock_irqsave(&ch_list_lock, cl_flags); - list_add_tail(&c->list, &channel_list); - spin_unlock_irqrestore(&ch_list_lock, cl_flags); - c->dev = device_create(aim_class, - NULL, - c->devno, - NULL, - "%s", name); - - if (IS_ERR(c->dev)) { - retval = PTR_ERR(c->dev); - pr_info("failed to create new device node %s\n", name); - goto error_create_device; - } - kobject_uevent(&c->dev->kobj, KOBJ_ADD); - return 0; - -error_create_device: - kfifo_free(&c->fifo); - list_del(&c->list); -error_alloc_kfifo: - cdev_del(&c->cdev); - kfree(c); -error_alloc_channel: - ida_simple_remove(&minor_id, current_minor); - return retval; -} - -static struct most_aim cdev_aim = { - .name = "cdev", - .probe_channel = aim_probe, - .disconnect_channel = aim_disconnect_channel, - .rx_completion = aim_rx_completion, - .tx_completion = aim_tx_completion, -}; - -static int __init mod_init(void) -{ - int err; - - pr_info("init()\n"); - - INIT_LIST_HEAD(&channel_list); - spin_lock_init(&ch_list_lock); - ida_init(&minor_id); - - err = alloc_chrdev_region(&aim_devno, 0, 50, "cdev"); - if (err < 0) - goto dest_ida; - major = MAJOR(aim_devno); - - aim_class = class_create(THIS_MODULE, "most_cdev_aim"); - if (IS_ERR(aim_class)) { - pr_err("no udev support\n"); - err = PTR_ERR(aim_class); - goto free_cdev; - } - err = most_register_aim(&cdev_aim); - if (err) - goto dest_class; - return 0; - -dest_class: - class_destroy(aim_class); -free_cdev: - unregister_chrdev_region(aim_devno, 1); -dest_ida: - ida_destroy(&minor_id); - return err; -} - -static void __exit mod_exit(void) -{ - struct aim_channel *c, *tmp; - - pr_info("exit module\n"); - - most_deregister_aim(&cdev_aim); - - list_for_each_entry_safe(c, tmp, &channel_list, list) { - destroy_cdev(c); - destroy_channel(c); - } - class_destroy(aim_class); - unregister_chrdev_region(aim_devno, 1); - ida_destroy(&minor_id); -} - -module_init(mod_init); -module_exit(mod_exit); -MODULE_AUTHOR("Christian Gromm "); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("character device AIM for mostcore"); diff --git a/drivers/staging/most/cdev/Kconfig b/drivers/staging/most/cdev/Kconfig new file mode 100644 index 000000000000..2b04e26bcbea --- /dev/null +++ b/drivers/staging/most/cdev/Kconfig @@ -0,0 +1,12 @@ +# +# MOST Cdev configuration +# + +config MOST_CDEV + tristate "Cdev" + + ---help--- + Say Y here if you want to commumicate via character devices. + + To compile this driver as a module, choose M here: the + module will be called most_cdev. diff --git a/drivers/staging/most/cdev/Makefile b/drivers/staging/most/cdev/Makefile new file mode 100644 index 000000000000..afb9870eb50f --- /dev/null +++ b/drivers/staging/most/cdev/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_MOST_CDEV) += most_cdev.o + +most_cdev-objs := cdev.o +ccflags-y += -Idrivers/staging/ diff --git a/drivers/staging/most/cdev/cdev.c b/drivers/staging/most/cdev/cdev.c new file mode 100644 index 000000000000..3ae9246c0c5c --- /dev/null +++ b/drivers/staging/most/cdev/cdev.c @@ -0,0 +1,558 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cdev.c - Application interfacing module for character devices + * + * Copyright (C) 2013-2015 Microchip Technology Germany II GmbH & Co. KG + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "most/core.h" + +static dev_t aim_devno; +static struct class *aim_class; +static struct ida minor_id; +static unsigned int major; +static struct most_aim cdev_aim; + +struct aim_channel { + wait_queue_head_t wq; + spinlock_t unlink; /* synchronization lock to unlink channels */ + struct cdev cdev; + struct device *dev; + struct mutex io_mutex; + struct most_interface *iface; + struct most_channel_config *cfg; + unsigned int channel_id; + dev_t devno; + size_t mbo_offs; + DECLARE_KFIFO_PTR(fifo, typeof(struct mbo *)); + int access_ref; + struct list_head list; +}; + +#define to_channel(d) container_of(d, struct aim_channel, cdev) +static struct list_head channel_list; +static spinlock_t ch_list_lock; + +static inline bool ch_has_mbo(struct aim_channel *c) +{ + return channel_has_mbo(c->iface, c->channel_id, &cdev_aim) > 0; +} + +static inline bool ch_get_mbo(struct aim_channel *c, struct mbo **mbo) +{ + if (!kfifo_peek(&c->fifo, mbo)) { + *mbo = most_get_mbo(c->iface, c->channel_id, &cdev_aim); + if (*mbo) + kfifo_in(&c->fifo, mbo, 1); + } + return *mbo; +} + +static struct aim_channel *get_channel(struct most_interface *iface, int id) +{ + struct aim_channel *c, *tmp; + unsigned long flags; + int found_channel = 0; + + spin_lock_irqsave(&ch_list_lock, flags); + list_for_each_entry_safe(c, tmp, &channel_list, list) { + if ((c->iface == iface) && (c->channel_id == id)) { + found_channel = 1; + break; + } + } + spin_unlock_irqrestore(&ch_list_lock, flags); + if (!found_channel) + return NULL; + return c; +} + +static void stop_channel(struct aim_channel *c) +{ + struct mbo *mbo; + + while (kfifo_out((struct kfifo *)&c->fifo, &mbo, 1)) + most_put_mbo(mbo); + most_stop_channel(c->iface, c->channel_id, &cdev_aim); +} + +static void destroy_cdev(struct aim_channel *c) +{ + unsigned long flags; + + device_destroy(aim_class, c->devno); + cdev_del(&c->cdev); + spin_lock_irqsave(&ch_list_lock, flags); + list_del(&c->list); + spin_unlock_irqrestore(&ch_list_lock, flags); +} + +static void destroy_channel(struct aim_channel *c) +{ + ida_simple_remove(&minor_id, MINOR(c->devno)); + kfifo_free(&c->fifo); + kfree(c); +} + +/** + * aim_open - implements the syscall to open the device + * @inode: inode pointer + * @filp: file pointer + * + * This stores the channel pointer in the private data field of + * the file structure and activates the channel within the core. + */ +static int aim_open(struct inode *inode, struct file *filp) +{ + struct aim_channel *c; + int ret; + + c = to_channel(inode->i_cdev); + filp->private_data = c; + + if (((c->cfg->direction == MOST_CH_RX) && + ((filp->f_flags & O_ACCMODE) != O_RDONLY)) || + ((c->cfg->direction == MOST_CH_TX) && + ((filp->f_flags & O_ACCMODE) != O_WRONLY))) { + pr_info("WARN: Access flags mismatch\n"); + return -EACCES; + } + + mutex_lock(&c->io_mutex); + if (!c->dev) { + pr_info("WARN: Device is destroyed\n"); + mutex_unlock(&c->io_mutex); + return -ENODEV; + } + + if (c->access_ref) { + pr_info("WARN: Device is busy\n"); + mutex_unlock(&c->io_mutex); + return -EBUSY; + } + + c->mbo_offs = 0; + ret = most_start_channel(c->iface, c->channel_id, &cdev_aim); + if (!ret) + c->access_ref = 1; + mutex_unlock(&c->io_mutex); + return ret; +} + +/** + * aim_close - implements the syscall to close the device + * @inode: inode pointer + * @filp: file pointer + * + * This stops the channel within the core. + */ +static int aim_close(struct inode *inode, struct file *filp) +{ + struct aim_channel *c = to_channel(inode->i_cdev); + + mutex_lock(&c->io_mutex); + spin_lock(&c->unlink); + c->access_ref = 0; + spin_unlock(&c->unlink); + if (c->dev) { + stop_channel(c); + mutex_unlock(&c->io_mutex); + } else { + mutex_unlock(&c->io_mutex); + destroy_channel(c); + } + return 0; +} + +/** + * aim_write - implements the syscall to write to the device + * @filp: file pointer + * @buf: pointer to user buffer + * @count: number of bytes to write + * @offset: offset from where to start writing + */ +static ssize_t aim_write(struct file *filp, const char __user *buf, + size_t count, loff_t *offset) +{ + int ret; + size_t to_copy, left; + struct mbo *mbo = NULL; + struct aim_channel *c = filp->private_data; + + mutex_lock(&c->io_mutex); + while (c->dev && !ch_get_mbo(c, &mbo)) { + mutex_unlock(&c->io_mutex); + + if ((filp->f_flags & O_NONBLOCK)) + return -EAGAIN; + if (wait_event_interruptible(c->wq, ch_has_mbo(c) || !c->dev)) + return -ERESTARTSYS; + mutex_lock(&c->io_mutex); + } + + if (unlikely(!c->dev)) { + ret = -ENODEV; + goto unlock; + } + + to_copy = min(count, c->cfg->buffer_size - c->mbo_offs); + left = copy_from_user(mbo->virt_address + c->mbo_offs, buf, to_copy); + if (left == to_copy) { + ret = -EFAULT; + goto unlock; + } + + c->mbo_offs += to_copy - left; + if (c->mbo_offs >= c->cfg->buffer_size || + c->cfg->data_type == MOST_CH_CONTROL || + c->cfg->data_type == MOST_CH_ASYNC) { + kfifo_skip(&c->fifo); + mbo->buffer_length = c->mbo_offs; + c->mbo_offs = 0; + most_submit_mbo(mbo); + } + + ret = to_copy - left; +unlock: + mutex_unlock(&c->io_mutex); + return ret; +} + +/** + * aim_read - implements the syscall to read from the device + * @filp: file pointer + * @buf: pointer to user buffer + * @count: number of bytes to read + * @offset: offset from where to start reading + */ +static ssize_t +aim_read(struct file *filp, char __user *buf, size_t count, loff_t *offset) +{ + size_t to_copy, not_copied, copied; + struct mbo *mbo; + struct aim_channel *c = filp->private_data; + + mutex_lock(&c->io_mutex); + while (c->dev && !kfifo_peek(&c->fifo, &mbo)) { + mutex_unlock(&c->io_mutex); + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + if (wait_event_interruptible(c->wq, + (!kfifo_is_empty(&c->fifo) || + (!c->dev)))) + return -ERESTARTSYS; + mutex_lock(&c->io_mutex); + } + + /* make sure we don't submit to gone devices */ + if (unlikely(!c->dev)) { + mutex_unlock(&c->io_mutex); + return -ENODEV; + } + + to_copy = min_t(size_t, + count, + mbo->processed_length - c->mbo_offs); + + not_copied = copy_to_user(buf, + mbo->virt_address + c->mbo_offs, + to_copy); + + copied = to_copy - not_copied; + + c->mbo_offs += copied; + if (c->mbo_offs >= mbo->processed_length) { + kfifo_skip(&c->fifo); + most_put_mbo(mbo); + c->mbo_offs = 0; + } + mutex_unlock(&c->io_mutex); + return copied; +} + +static unsigned int aim_poll(struct file *filp, poll_table *wait) +{ + struct aim_channel *c = filp->private_data; + unsigned int mask = 0; + + poll_wait(filp, &c->wq, wait); + + if (c->cfg->direction == MOST_CH_RX) { + if (!kfifo_is_empty(&c->fifo)) + mask |= POLLIN | POLLRDNORM; + } else { + if (!kfifo_is_empty(&c->fifo) || ch_has_mbo(c)) + mask |= POLLOUT | POLLWRNORM; + } + return mask; +} + +/** + * Initialization of struct file_operations + */ +static const struct file_operations channel_fops = { + .owner = THIS_MODULE, + .read = aim_read, + .write = aim_write, + .open = aim_open, + .release = aim_close, + .poll = aim_poll, +}; + +/** + * aim_disconnect_channel - disconnect a channel + * @iface: pointer to interface instance + * @channel_id: channel index + * + * This frees allocated memory and removes the cdev that represents this + * channel in user space. + */ +static int aim_disconnect_channel(struct most_interface *iface, int channel_id) +{ + struct aim_channel *c; + + if (!iface) { + pr_info("Bad interface pointer\n"); + return -EINVAL; + } + + c = get_channel(iface, channel_id); + if (!c) + return -ENXIO; + + mutex_lock(&c->io_mutex); + spin_lock(&c->unlink); + c->dev = NULL; + spin_unlock(&c->unlink); + destroy_cdev(c); + if (c->access_ref) { + stop_channel(c); + wake_up_interruptible(&c->wq); + mutex_unlock(&c->io_mutex); + } else { + mutex_unlock(&c->io_mutex); + destroy_channel(c); + } + return 0; +} + +/** + * aim_rx_completion - completion handler for rx channels + * @mbo: pointer to buffer object that has completed + * + * This searches for the channel linked to this MBO and stores it in the local + * fifo buffer. + */ +static int aim_rx_completion(struct mbo *mbo) +{ + struct aim_channel *c; + + if (!mbo) + return -EINVAL; + + c = get_channel(mbo->ifp, mbo->hdm_channel_id); + if (!c) + return -ENXIO; + + spin_lock(&c->unlink); + if (!c->access_ref || !c->dev) { + spin_unlock(&c->unlink); + return -ENODEV; + } + kfifo_in(&c->fifo, &mbo, 1); + spin_unlock(&c->unlink); +#ifdef DEBUG_MESG + if (kfifo_is_full(&c->fifo)) + pr_info("WARN: Fifo is full\n"); +#endif + wake_up_interruptible(&c->wq); + return 0; +} + +/** + * aim_tx_completion - completion handler for tx channels + * @iface: pointer to interface instance + * @channel_id: channel index/ID + * + * This wakes sleeping processes in the wait-queue. + */ +static int aim_tx_completion(struct most_interface *iface, int channel_id) +{ + struct aim_channel *c; + + if (!iface) { + pr_info("Bad interface pointer\n"); + return -EINVAL; + } + if ((channel_id < 0) || (channel_id >= iface->num_channels)) { + pr_info("Channel ID out of range\n"); + return -EINVAL; + } + + c = get_channel(iface, channel_id); + if (!c) + return -ENXIO; + wake_up_interruptible(&c->wq); + return 0; +} + +/** + * aim_probe - probe function of the driver module + * @iface: pointer to interface instance + * @channel_id: channel index/ID + * @cfg: pointer to actual channel configuration + * @parent: pointer to kobject (needed for sysfs hook-up) + * @name: name of the device to be created + * + * This allocates achannel object and creates the device node in /dev + * + * Returns 0 on success or error code otherwise. + */ +static int aim_probe(struct most_interface *iface, int channel_id, + struct most_channel_config *cfg, + struct kobject *parent, char *name) +{ + struct aim_channel *c; + unsigned long cl_flags; + int retval; + int current_minor; + + if ((!iface) || (!cfg) || (!parent) || (!name)) { + pr_info("Probing AIM with bad arguments"); + return -EINVAL; + } + c = get_channel(iface, channel_id); + if (c) + return -EEXIST; + + current_minor = ida_simple_get(&minor_id, 0, 0, GFP_KERNEL); + if (current_minor < 0) + return current_minor; + + c = kzalloc(sizeof(*c), GFP_KERNEL); + if (!c) { + retval = -ENOMEM; + goto error_alloc_channel; + } + + c->devno = MKDEV(major, current_minor); + cdev_init(&c->cdev, &channel_fops); + c->cdev.owner = THIS_MODULE; + cdev_add(&c->cdev, c->devno, 1); + c->iface = iface; + c->cfg = cfg; + c->channel_id = channel_id; + c->access_ref = 0; + spin_lock_init(&c->unlink); + INIT_KFIFO(c->fifo); + retval = kfifo_alloc(&c->fifo, cfg->num_buffers, GFP_KERNEL); + if (retval) { + pr_info("failed to alloc channel kfifo"); + goto error_alloc_kfifo; + } + init_waitqueue_head(&c->wq); + mutex_init(&c->io_mutex); + spin_lock_irqsave(&ch_list_lock, cl_flags); + list_add_tail(&c->list, &channel_list); + spin_unlock_irqrestore(&ch_list_lock, cl_flags); + c->dev = device_create(aim_class, + NULL, + c->devno, + NULL, + "%s", name); + + if (IS_ERR(c->dev)) { + retval = PTR_ERR(c->dev); + pr_info("failed to create new device node %s\n", name); + goto error_create_device; + } + kobject_uevent(&c->dev->kobj, KOBJ_ADD); + return 0; + +error_create_device: + kfifo_free(&c->fifo); + list_del(&c->list); +error_alloc_kfifo: + cdev_del(&c->cdev); + kfree(c); +error_alloc_channel: + ida_simple_remove(&minor_id, current_minor); + return retval; +} + +static struct most_aim cdev_aim = { + .name = "cdev", + .probe_channel = aim_probe, + .disconnect_channel = aim_disconnect_channel, + .rx_completion = aim_rx_completion, + .tx_completion = aim_tx_completion, +}; + +static int __init mod_init(void) +{ + int err; + + pr_info("init()\n"); + + INIT_LIST_HEAD(&channel_list); + spin_lock_init(&ch_list_lock); + ida_init(&minor_id); + + err = alloc_chrdev_region(&aim_devno, 0, 50, "cdev"); + if (err < 0) + goto dest_ida; + major = MAJOR(aim_devno); + + aim_class = class_create(THIS_MODULE, "most_cdev_aim"); + if (IS_ERR(aim_class)) { + pr_err("no udev support\n"); + err = PTR_ERR(aim_class); + goto free_cdev; + } + err = most_register_aim(&cdev_aim); + if (err) + goto dest_class; + return 0; + +dest_class: + class_destroy(aim_class); +free_cdev: + unregister_chrdev_region(aim_devno, 1); +dest_ida: + ida_destroy(&minor_id); + return err; +} + +static void __exit mod_exit(void) +{ + struct aim_channel *c, *tmp; + + pr_info("exit module\n"); + + most_deregister_aim(&cdev_aim); + + list_for_each_entry_safe(c, tmp, &channel_list, list) { + destroy_cdev(c); + destroy_channel(c); + } + class_destroy(aim_class); + unregister_chrdev_region(aim_devno, 1); + ida_destroy(&minor_id); +} + +module_init(mod_init); +module_exit(mod_exit); +MODULE_AUTHOR("Christian Gromm "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("character device AIM for mostcore"); -- cgit From 77550c20121f07b0f2389c39e076e034660724e0 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:37 +0100 Subject: staging: most: i2c: rename module This patch renames the folder of the i2c module. It is needed to clear the directory layout. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/Kconfig | 2 +- drivers/staging/most/hdm-i2c/Kconfig | 12 - drivers/staging/most/hdm-i2c/hdm_i2c.c | 417 --------------------------------- drivers/staging/most/i2c/Kconfig | 12 + drivers/staging/most/i2c/Makefile | 4 + drivers/staging/most/i2c/i2c.c | 417 +++++++++++++++++++++++++++++++++ 6 files changed, 434 insertions(+), 430 deletions(-) delete mode 100644 drivers/staging/most/hdm-i2c/Kconfig delete mode 100644 drivers/staging/most/hdm-i2c/hdm_i2c.c create mode 100644 drivers/staging/most/i2c/Kconfig create mode 100644 drivers/staging/most/i2c/Makefile create mode 100644 drivers/staging/most/i2c/i2c.c (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/Kconfig b/drivers/staging/most/Kconfig index 60a2825e6076..b3393a5f5706 100644 --- a/drivers/staging/most/Kconfig +++ b/drivers/staging/most/Kconfig @@ -27,7 +27,7 @@ source "drivers/staging/most/aim-v4l2/Kconfig" source "drivers/staging/most/hdm-dim2/Kconfig" -source "drivers/staging/most/hdm-i2c/Kconfig" +source "drivers/staging/most/i2c/Kconfig" source "drivers/staging/most/hdm-usb/Kconfig" diff --git a/drivers/staging/most/hdm-i2c/Kconfig b/drivers/staging/most/hdm-i2c/Kconfig deleted file mode 100644 index 6fd7983668ad..000000000000 --- a/drivers/staging/most/hdm-i2c/Kconfig +++ /dev/null @@ -1,12 +0,0 @@ -# -# MOST I2C configuration -# - -config HDM_I2C - tristate "I2C HDM" - depends on I2C - ---help--- - Say Y here if you want to connect via I2C to network tranceiver. - - To compile this driver as a module, choose M here: the - module will be called hdm_i2c. diff --git a/drivers/staging/most/hdm-i2c/hdm_i2c.c b/drivers/staging/most/hdm-i2c/hdm_i2c.c deleted file mode 100644 index c73c76d0a6d5..000000000000 --- a/drivers/staging/most/hdm-i2c/hdm_i2c.c +++ /dev/null @@ -1,417 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * hdm_i2c.c - Hardware Dependent Module for I2C Interface - * - * Copyright (C) 2013-2015, Microchip Technology Germany II GmbH & Co. KG - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include - -#include "most/core.h" - -enum { CH_RX, CH_TX, NUM_CHANNELS }; - -#define MAX_BUFFERS_CONTROL 32 -#define MAX_BUF_SIZE_CONTROL 256 - -/** - * list_first_mbo - get the first mbo from a list - * @ptr: the list head to take the mbo from. - */ -#define list_first_mbo(ptr) \ - list_first_entry(ptr, struct mbo, list) - -/* IRQ / Polling option */ -static bool polling_req; -module_param(polling_req, bool, 0444); -MODULE_PARM_DESC(polling_req, "Request Polling. Default = 0 (use irq)"); - -/* Polling Rate */ -static int scan_rate = 100; -module_param(scan_rate, int, 0644); -MODULE_PARM_DESC(scan_rate, "Polling rate in times/sec. Default = 100"); - -struct hdm_i2c { - bool is_open[NUM_CHANNELS]; - bool polling_mode; - struct most_interface most_iface; - struct most_channel_capability capabilities[NUM_CHANNELS]; - struct i2c_client *client; - struct rx { - struct delayed_work dwork; - wait_queue_head_t waitq; - struct list_head list; - struct mutex list_mutex; - } rx; - char name[64]; -}; - -#define to_hdm(iface) container_of(iface, struct hdm_i2c, most_iface) - -/** - * configure_channel - called from MOST core to configure a channel - * @iface: interface the channel belongs to - * @channel: channel to be configured - * @channel_config: structure that holds the configuration information - * - * Return 0 on success, negative on failure. - * - * Receives configuration information from MOST core and initialize the - * corresponding channel. - */ -static int configure_channel(struct most_interface *most_iface, - int ch_idx, - struct most_channel_config *channel_config) -{ - struct hdm_i2c *dev = to_hdm(most_iface); - - BUG_ON(ch_idx < 0 || ch_idx >= NUM_CHANNELS); - BUG_ON(dev->is_open[ch_idx]); - - if (channel_config->data_type != MOST_CH_CONTROL) { - pr_err("bad data type for channel %d\n", ch_idx); - return -EPERM; - } - - if (channel_config->direction != dev->capabilities[ch_idx].direction) { - pr_err("bad direction for channel %d\n", ch_idx); - return -EPERM; - } - - if ((channel_config->direction == MOST_CH_RX) && (dev->polling_mode)) { - schedule_delayed_work(&dev->rx.dwork, - msecs_to_jiffies(MSEC_PER_SEC / 4)); - } - dev->is_open[ch_idx] = true; - - return 0; -} - -/** - * enqueue - called from MOST core to enqueue a buffer for data transfer - * @iface: intended interface - * @channel: ID of the channel the buffer is intended for - * @mbo: pointer to the buffer object - * - * Return 0 on success, negative on failure. - * - * Transmit the data over I2C if it is a "write" request or push the buffer into - * list if it is an "read" request - */ -static int enqueue(struct most_interface *most_iface, - int ch_idx, struct mbo *mbo) -{ - struct hdm_i2c *dev = to_hdm(most_iface); - int ret; - - BUG_ON(ch_idx < 0 || ch_idx >= NUM_CHANNELS); - BUG_ON(!dev->is_open[ch_idx]); - - if (ch_idx == CH_RX) { - /* RX */ - mutex_lock(&dev->rx.list_mutex); - list_add_tail(&mbo->list, &dev->rx.list); - mutex_unlock(&dev->rx.list_mutex); - wake_up_interruptible(&dev->rx.waitq); - } else { - /* TX */ - ret = i2c_master_send(dev->client, mbo->virt_address, - mbo->buffer_length); - if (ret <= 0) { - mbo->processed_length = 0; - mbo->status = MBO_E_INVAL; - } else { - mbo->processed_length = mbo->buffer_length; - mbo->status = MBO_SUCCESS; - } - mbo->complete(mbo); - } - - return 0; -} - -/** - * poison_channel - called from MOST core to poison buffers of a channel - * @iface: pointer to the interface the channel to be poisoned belongs to - * @channel_id: corresponding channel ID - * - * Return 0 on success, negative on failure. - * - * If channel direction is RX, complete the buffers in list with - * status MBO_E_CLOSE - */ -static int poison_channel(struct most_interface *most_iface, - int ch_idx) -{ - struct hdm_i2c *dev = to_hdm(most_iface); - struct mbo *mbo; - - BUG_ON(ch_idx < 0 || ch_idx >= NUM_CHANNELS); - BUG_ON(!dev->is_open[ch_idx]); - - dev->is_open[ch_idx] = false; - - if (ch_idx == CH_RX) { - mutex_lock(&dev->rx.list_mutex); - while (!list_empty(&dev->rx.list)) { - mbo = list_first_mbo(&dev->rx.list); - list_del(&mbo->list); - mutex_unlock(&dev->rx.list_mutex); - - mbo->processed_length = 0; - mbo->status = MBO_E_CLOSE; - mbo->complete(mbo); - - mutex_lock(&dev->rx.list_mutex); - } - mutex_unlock(&dev->rx.list_mutex); - wake_up_interruptible(&dev->rx.waitq); - } - - return 0; -} - -static void do_rx_work(struct hdm_i2c *dev) -{ - struct mbo *mbo; - unsigned char msg[MAX_BUF_SIZE_CONTROL]; - int ret, ch_idx = CH_RX; - u16 pml, data_size; - - /* Read PML (2 bytes) */ - ret = i2c_master_recv(dev->client, msg, 2); - if (ret <= 0) { - pr_err("Failed to receive PML\n"); - return; - } - - pml = (msg[0] << 8) | msg[1]; - if (!pml) - return; - - data_size = pml + 2; - - /* Read the whole message, including PML */ - ret = i2c_master_recv(dev->client, msg, data_size); - if (ret <= 0) { - pr_err("Failed to receive a Port Message\n"); - return; - } - - for (;;) { - /* Conditions to wait for: poisoned channel or free buffer - * available for reading - */ - if (wait_event_interruptible(dev->rx.waitq, - !dev->is_open[ch_idx] || - !list_empty(&dev->rx.list))) { - pr_err("wait_event_interruptible() failed\n"); - return; - } - - if (!dev->is_open[ch_idx]) - return; - - mutex_lock(&dev->rx.list_mutex); - - /* list may be empty if poison or remove is called */ - if (!list_empty(&dev->rx.list)) - break; - - mutex_unlock(&dev->rx.list_mutex); - } - - mbo = list_first_mbo(&dev->rx.list); - list_del(&mbo->list); - mutex_unlock(&dev->rx.list_mutex); - - mbo->processed_length = min(data_size, mbo->buffer_length); - memcpy(mbo->virt_address, msg, mbo->processed_length); - mbo->status = MBO_SUCCESS; - mbo->complete(mbo); -} - -/** - * pending_rx_work - Read pending messages through I2C - * @work: definition of this work item - * - * Invoked by the Interrupt Service Routine, most_irq_handler() - */ -static void pending_rx_work(struct work_struct *work) -{ - struct hdm_i2c *dev = container_of(work, struct hdm_i2c, rx.dwork.work); - - do_rx_work(dev); - - if (dev->polling_mode) { - if (dev->is_open[CH_RX]) - schedule_delayed_work(&dev->rx.dwork, - msecs_to_jiffies(MSEC_PER_SEC - / scan_rate)); - } else { - enable_irq(dev->client->irq); - } -} - -/* - * most_irq_handler - Interrupt Service Routine - * @irq: irq number - * @_dev: private data - * - * Schedules a delayed work - * - * By default the interrupt line behavior is Active Low. Once an interrupt is - * generated by the device, until driver clears the interrupt (by reading - * the PMP message), device keeps the interrupt line in low state. Since i2c - * read is done in work queue, the interrupt line must be disabled temporarily - * to avoid ISR being called repeatedly. Re-enable the interrupt in workqueue, - * after reading the message. - * - * Note: If we use the interrupt line in Falling edge mode, there is a - * possibility to miss interrupts when ISR is getting executed. - * - */ -static irqreturn_t most_irq_handler(int irq, void *_dev) -{ - struct hdm_i2c *dev = _dev; - - disable_irq_nosync(irq); - - schedule_delayed_work(&dev->rx.dwork, 0); - - return IRQ_HANDLED; -} - -/* - * i2c_probe - i2c probe handler - * @client: i2c client device structure - * @id: i2c client device id - * - * Return 0 on success, negative on failure. - * - * Register the i2c client device as a MOST interface - */ -static int i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - struct hdm_i2c *dev; - int ret, i; - struct kobject *kobj; - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - /* ID format: i2c--
*/ - snprintf(dev->name, sizeof(dev->name), "i2c-%d-%04x", - client->adapter->nr, client->addr); - - for (i = 0; i < NUM_CHANNELS; i++) { - dev->is_open[i] = false; - dev->capabilities[i].data_type = MOST_CH_CONTROL; - dev->capabilities[i].num_buffers_packet = MAX_BUFFERS_CONTROL; - dev->capabilities[i].buffer_size_packet = MAX_BUF_SIZE_CONTROL; - } - dev->capabilities[CH_RX].direction = MOST_CH_RX; - dev->capabilities[CH_RX].name_suffix = "rx"; - dev->capabilities[CH_TX].direction = MOST_CH_TX; - dev->capabilities[CH_TX].name_suffix = "tx"; - - dev->most_iface.interface = ITYPE_I2C; - dev->most_iface.description = dev->name; - dev->most_iface.num_channels = NUM_CHANNELS; - dev->most_iface.channel_vector = dev->capabilities; - dev->most_iface.configure = configure_channel; - dev->most_iface.enqueue = enqueue; - dev->most_iface.poison_channel = poison_channel; - - INIT_LIST_HEAD(&dev->rx.list); - mutex_init(&dev->rx.list_mutex); - init_waitqueue_head(&dev->rx.waitq); - - INIT_DELAYED_WORK(&dev->rx.dwork, pending_rx_work); - - dev->client = client; - i2c_set_clientdata(client, dev); - - kobj = most_register_interface(&dev->most_iface); - if (IS_ERR(kobj)) { - pr_err("Failed to register i2c as a MOST interface\n"); - kfree(dev); - return PTR_ERR(kobj); - } - - dev->polling_mode = polling_req || client->irq <= 0; - if (!dev->polling_mode) { - pr_info("Requesting IRQ: %d\n", client->irq); - ret = request_irq(client->irq, most_irq_handler, 0, - client->name, dev); - if (ret) { - pr_info("IRQ request failed: %d, falling back to polling\n", - ret); - dev->polling_mode = true; - } - } - - if (dev->polling_mode) - pr_info("Using polling at rate: %d times/sec\n", scan_rate); - - return 0; -} - -/* - * i2c_remove - i2c remove handler - * @client: i2c client device structure - * - * Return 0 on success. - * - * Unregister the i2c client device as a MOST interface - */ -static int i2c_remove(struct i2c_client *client) -{ - struct hdm_i2c *dev = i2c_get_clientdata(client); - int i; - - if (!dev->polling_mode) - free_irq(client->irq, dev); - - most_deregister_interface(&dev->most_iface); - - for (i = 0 ; i < NUM_CHANNELS; i++) - if (dev->is_open[i]) - poison_channel(&dev->most_iface, i); - cancel_delayed_work_sync(&dev->rx.dwork); - kfree(dev); - - return 0; -} - -static const struct i2c_device_id i2c_id[] = { - { "most_i2c", 0 }, - { }, /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(i2c, i2c_id); - -static struct i2c_driver i2c_driver = { - .driver = { - .name = "hdm_i2c", - }, - .probe = i2c_probe, - .remove = i2c_remove, - .id_table = i2c_id, -}; - -module_i2c_driver(i2c_driver); - -MODULE_AUTHOR("Jain Roy Ambi "); -MODULE_AUTHOR("Andrey Shvetsov "); -MODULE_DESCRIPTION("I2C Hardware Dependent Module"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/most/i2c/Kconfig b/drivers/staging/most/i2c/Kconfig new file mode 100644 index 000000000000..79d0ff27f56d --- /dev/null +++ b/drivers/staging/most/i2c/Kconfig @@ -0,0 +1,12 @@ +# +# MOST I2C configuration +# + +config MOST_I2C + tristate "I2C" + depends on I2C + ---help--- + Say Y here if you want to connect via I2C to network tranceiver. + + To compile this driver as a module, choose M here: the + module will be called most_i2c. diff --git a/drivers/staging/most/i2c/Makefile b/drivers/staging/most/i2c/Makefile new file mode 100644 index 000000000000..a7d094c1e1c2 --- /dev/null +++ b/drivers/staging/most/i2c/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_MOST_I2C) += most_i2c.o + +most_i2c-objs := i2c.o +ccflags-y += -Idrivers/staging/ diff --git a/drivers/staging/most/i2c/i2c.c b/drivers/staging/most/i2c/i2c.c new file mode 100644 index 000000000000..156b2b1bdba9 --- /dev/null +++ b/drivers/staging/most/i2c/i2c.c @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * i2c.c - Hardware Dependent Module for I2C Interface + * + * Copyright (C) 2013-2015, Microchip Technology Germany II GmbH & Co. KG + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +#include "most/core.h" + +enum { CH_RX, CH_TX, NUM_CHANNELS }; + +#define MAX_BUFFERS_CONTROL 32 +#define MAX_BUF_SIZE_CONTROL 256 + +/** + * list_first_mbo - get the first mbo from a list + * @ptr: the list head to take the mbo from. + */ +#define list_first_mbo(ptr) \ + list_first_entry(ptr, struct mbo, list) + +/* IRQ / Polling option */ +static bool polling_req; +module_param(polling_req, bool, 0444); +MODULE_PARM_DESC(polling_req, "Request Polling. Default = 0 (use irq)"); + +/* Polling Rate */ +static int scan_rate = 100; +module_param(scan_rate, int, 0644); +MODULE_PARM_DESC(scan_rate, "Polling rate in times/sec. Default = 100"); + +struct hdm_i2c { + bool is_open[NUM_CHANNELS]; + bool polling_mode; + struct most_interface most_iface; + struct most_channel_capability capabilities[NUM_CHANNELS]; + struct i2c_client *client; + struct rx { + struct delayed_work dwork; + wait_queue_head_t waitq; + struct list_head list; + struct mutex list_mutex; + } rx; + char name[64]; +}; + +#define to_hdm(iface) container_of(iface, struct hdm_i2c, most_iface) + +/** + * configure_channel - called from MOST core to configure a channel + * @iface: interface the channel belongs to + * @channel: channel to be configured + * @channel_config: structure that holds the configuration information + * + * Return 0 on success, negative on failure. + * + * Receives configuration information from MOST core and initialize the + * corresponding channel. + */ +static int configure_channel(struct most_interface *most_iface, + int ch_idx, + struct most_channel_config *channel_config) +{ + struct hdm_i2c *dev = to_hdm(most_iface); + + BUG_ON(ch_idx < 0 || ch_idx >= NUM_CHANNELS); + BUG_ON(dev->is_open[ch_idx]); + + if (channel_config->data_type != MOST_CH_CONTROL) { + pr_err("bad data type for channel %d\n", ch_idx); + return -EPERM; + } + + if (channel_config->direction != dev->capabilities[ch_idx].direction) { + pr_err("bad direction for channel %d\n", ch_idx); + return -EPERM; + } + + if ((channel_config->direction == MOST_CH_RX) && (dev->polling_mode)) { + schedule_delayed_work(&dev->rx.dwork, + msecs_to_jiffies(MSEC_PER_SEC / 4)); + } + dev->is_open[ch_idx] = true; + + return 0; +} + +/** + * enqueue - called from MOST core to enqueue a buffer for data transfer + * @iface: intended interface + * @channel: ID of the channel the buffer is intended for + * @mbo: pointer to the buffer object + * + * Return 0 on success, negative on failure. + * + * Transmit the data over I2C if it is a "write" request or push the buffer into + * list if it is an "read" request + */ +static int enqueue(struct most_interface *most_iface, + int ch_idx, struct mbo *mbo) +{ + struct hdm_i2c *dev = to_hdm(most_iface); + int ret; + + BUG_ON(ch_idx < 0 || ch_idx >= NUM_CHANNELS); + BUG_ON(!dev->is_open[ch_idx]); + + if (ch_idx == CH_RX) { + /* RX */ + mutex_lock(&dev->rx.list_mutex); + list_add_tail(&mbo->list, &dev->rx.list); + mutex_unlock(&dev->rx.list_mutex); + wake_up_interruptible(&dev->rx.waitq); + } else { + /* TX */ + ret = i2c_master_send(dev->client, mbo->virt_address, + mbo->buffer_length); + if (ret <= 0) { + mbo->processed_length = 0; + mbo->status = MBO_E_INVAL; + } else { + mbo->processed_length = mbo->buffer_length; + mbo->status = MBO_SUCCESS; + } + mbo->complete(mbo); + } + + return 0; +} + +/** + * poison_channel - called from MOST core to poison buffers of a channel + * @iface: pointer to the interface the channel to be poisoned belongs to + * @channel_id: corresponding channel ID + * + * Return 0 on success, negative on failure. + * + * If channel direction is RX, complete the buffers in list with + * status MBO_E_CLOSE + */ +static int poison_channel(struct most_interface *most_iface, + int ch_idx) +{ + struct hdm_i2c *dev = to_hdm(most_iface); + struct mbo *mbo; + + BUG_ON(ch_idx < 0 || ch_idx >= NUM_CHANNELS); + BUG_ON(!dev->is_open[ch_idx]); + + dev->is_open[ch_idx] = false; + + if (ch_idx == CH_RX) { + mutex_lock(&dev->rx.list_mutex); + while (!list_empty(&dev->rx.list)) { + mbo = list_first_mbo(&dev->rx.list); + list_del(&mbo->list); + mutex_unlock(&dev->rx.list_mutex); + + mbo->processed_length = 0; + mbo->status = MBO_E_CLOSE; + mbo->complete(mbo); + + mutex_lock(&dev->rx.list_mutex); + } + mutex_unlock(&dev->rx.list_mutex); + wake_up_interruptible(&dev->rx.waitq); + } + + return 0; +} + +static void do_rx_work(struct hdm_i2c *dev) +{ + struct mbo *mbo; + unsigned char msg[MAX_BUF_SIZE_CONTROL]; + int ret, ch_idx = CH_RX; + u16 pml, data_size; + + /* Read PML (2 bytes) */ + ret = i2c_master_recv(dev->client, msg, 2); + if (ret <= 0) { + pr_err("Failed to receive PML\n"); + return; + } + + pml = (msg[0] << 8) | msg[1]; + if (!pml) + return; + + data_size = pml + 2; + + /* Read the whole message, including PML */ + ret = i2c_master_recv(dev->client, msg, data_size); + if (ret <= 0) { + pr_err("Failed to receive a Port Message\n"); + return; + } + + for (;;) { + /* Conditions to wait for: poisoned channel or free buffer + * available for reading + */ + if (wait_event_interruptible(dev->rx.waitq, + !dev->is_open[ch_idx] || + !list_empty(&dev->rx.list))) { + pr_err("wait_event_interruptible() failed\n"); + return; + } + + if (!dev->is_open[ch_idx]) + return; + + mutex_lock(&dev->rx.list_mutex); + + /* list may be empty if poison or remove is called */ + if (!list_empty(&dev->rx.list)) + break; + + mutex_unlock(&dev->rx.list_mutex); + } + + mbo = list_first_mbo(&dev->rx.list); + list_del(&mbo->list); + mutex_unlock(&dev->rx.list_mutex); + + mbo->processed_length = min(data_size, mbo->buffer_length); + memcpy(mbo->virt_address, msg, mbo->processed_length); + mbo->status = MBO_SUCCESS; + mbo->complete(mbo); +} + +/** + * pending_rx_work - Read pending messages through I2C + * @work: definition of this work item + * + * Invoked by the Interrupt Service Routine, most_irq_handler() + */ +static void pending_rx_work(struct work_struct *work) +{ + struct hdm_i2c *dev = container_of(work, struct hdm_i2c, rx.dwork.work); + + do_rx_work(dev); + + if (dev->polling_mode) { + if (dev->is_open[CH_RX]) + schedule_delayed_work(&dev->rx.dwork, + msecs_to_jiffies(MSEC_PER_SEC + / scan_rate)); + } else { + enable_irq(dev->client->irq); + } +} + +/* + * most_irq_handler - Interrupt Service Routine + * @irq: irq number + * @_dev: private data + * + * Schedules a delayed work + * + * By default the interrupt line behavior is Active Low. Once an interrupt is + * generated by the device, until driver clears the interrupt (by reading + * the PMP message), device keeps the interrupt line in low state. Since i2c + * read is done in work queue, the interrupt line must be disabled temporarily + * to avoid ISR being called repeatedly. Re-enable the interrupt in workqueue, + * after reading the message. + * + * Note: If we use the interrupt line in Falling edge mode, there is a + * possibility to miss interrupts when ISR is getting executed. + * + */ +static irqreturn_t most_irq_handler(int irq, void *_dev) +{ + struct hdm_i2c *dev = _dev; + + disable_irq_nosync(irq); + + schedule_delayed_work(&dev->rx.dwork, 0); + + return IRQ_HANDLED; +} + +/* + * i2c_probe - i2c probe handler + * @client: i2c client device structure + * @id: i2c client device id + * + * Return 0 on success, negative on failure. + * + * Register the i2c client device as a MOST interface + */ +static int i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct hdm_i2c *dev; + int ret, i; + struct kobject *kobj; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + /* ID format: i2c--
*/ + snprintf(dev->name, sizeof(dev->name), "i2c-%d-%04x", + client->adapter->nr, client->addr); + + for (i = 0; i < NUM_CHANNELS; i++) { + dev->is_open[i] = false; + dev->capabilities[i].data_type = MOST_CH_CONTROL; + dev->capabilities[i].num_buffers_packet = MAX_BUFFERS_CONTROL; + dev->capabilities[i].buffer_size_packet = MAX_BUF_SIZE_CONTROL; + } + dev->capabilities[CH_RX].direction = MOST_CH_RX; + dev->capabilities[CH_RX].name_suffix = "rx"; + dev->capabilities[CH_TX].direction = MOST_CH_TX; + dev->capabilities[CH_TX].name_suffix = "tx"; + + dev->most_iface.interface = ITYPE_I2C; + dev->most_iface.description = dev->name; + dev->most_iface.num_channels = NUM_CHANNELS; + dev->most_iface.channel_vector = dev->capabilities; + dev->most_iface.configure = configure_channel; + dev->most_iface.enqueue = enqueue; + dev->most_iface.poison_channel = poison_channel; + + INIT_LIST_HEAD(&dev->rx.list); + mutex_init(&dev->rx.list_mutex); + init_waitqueue_head(&dev->rx.waitq); + + INIT_DELAYED_WORK(&dev->rx.dwork, pending_rx_work); + + dev->client = client; + i2c_set_clientdata(client, dev); + + kobj = most_register_interface(&dev->most_iface); + if (IS_ERR(kobj)) { + pr_err("Failed to register i2c as a MOST interface\n"); + kfree(dev); + return PTR_ERR(kobj); + } + + dev->polling_mode = polling_req || client->irq <= 0; + if (!dev->polling_mode) { + pr_info("Requesting IRQ: %d\n", client->irq); + ret = request_irq(client->irq, most_irq_handler, 0, + client->name, dev); + if (ret) { + pr_info("IRQ request failed: %d, falling back to polling\n", + ret); + dev->polling_mode = true; + } + } + + if (dev->polling_mode) + pr_info("Using polling at rate: %d times/sec\n", scan_rate); + + return 0; +} + +/* + * i2c_remove - i2c remove handler + * @client: i2c client device structure + * + * Return 0 on success. + * + * Unregister the i2c client device as a MOST interface + */ +static int i2c_remove(struct i2c_client *client) +{ + struct hdm_i2c *dev = i2c_get_clientdata(client); + int i; + + if (!dev->polling_mode) + free_irq(client->irq, dev); + + most_deregister_interface(&dev->most_iface); + + for (i = 0 ; i < NUM_CHANNELS; i++) + if (dev->is_open[i]) + poison_channel(&dev->most_iface, i); + cancel_delayed_work_sync(&dev->rx.dwork); + kfree(dev); + + return 0; +} + +static const struct i2c_device_id i2c_id[] = { + { "most_i2c", 0 }, + { }, /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(i2c, i2c_id); + +static struct i2c_driver i2c_driver = { + .driver = { + .name = "hdm_i2c", + }, + .probe = i2c_probe, + .remove = i2c_remove, + .id_table = i2c_id, +}; + +module_i2c_driver(i2c_driver); + +MODULE_AUTHOR("Jain Roy Ambi "); +MODULE_AUTHOR("Andrey Shvetsov "); +MODULE_DESCRIPTION("I2C Hardware Dependent Module"); +MODULE_LICENSE("GPL"); -- cgit From 9249c6a6d3560f09cddd04e2fda856f237b64987 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:38 +0100 Subject: staging: most: dim2: rename module This patch renames the folder and source files of the dim2 module. It is needed to clear the directory layout of the driver. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/Kconfig | 2 +- drivers/staging/most/dim2/Kconfig | 16 + drivers/staging/most/dim2/Makefile | 4 + drivers/staging/most/dim2/dim2.c | 912 ++++++++++++++++++++++++++ drivers/staging/most/dim2/dim2.h | 21 + drivers/staging/most/dim2/errors.h | 51 ++ drivers/staging/most/dim2/hal.c | 979 ++++++++++++++++++++++++++++ drivers/staging/most/dim2/hal.h | 106 +++ drivers/staging/most/dim2/reg.h | 157 +++++ drivers/staging/most/dim2/sysfs.c | 109 ++++ drivers/staging/most/dim2/sysfs.h | 30 + drivers/staging/most/hdm-dim2/Kconfig | 16 - drivers/staging/most/hdm-dim2/dim2_errors.h | 51 -- drivers/staging/most/hdm-dim2/dim2_hal.c | 979 ---------------------------- drivers/staging/most/hdm-dim2/dim2_hal.h | 106 --- drivers/staging/most/hdm-dim2/dim2_hdm.c | 912 -------------------------- drivers/staging/most/hdm-dim2/dim2_hdm.h | 21 - drivers/staging/most/hdm-dim2/dim2_reg.h | 157 ----- drivers/staging/most/hdm-dim2/dim2_sysfs.c | 109 ---- drivers/staging/most/hdm-dim2/dim2_sysfs.h | 30 - 20 files changed, 2386 insertions(+), 2382 deletions(-) create mode 100644 drivers/staging/most/dim2/Kconfig create mode 100644 drivers/staging/most/dim2/Makefile create mode 100644 drivers/staging/most/dim2/dim2.c create mode 100644 drivers/staging/most/dim2/dim2.h create mode 100644 drivers/staging/most/dim2/errors.h create mode 100644 drivers/staging/most/dim2/hal.c create mode 100644 drivers/staging/most/dim2/hal.h create mode 100644 drivers/staging/most/dim2/reg.h create mode 100644 drivers/staging/most/dim2/sysfs.c create mode 100644 drivers/staging/most/dim2/sysfs.h delete mode 100644 drivers/staging/most/hdm-dim2/Kconfig delete mode 100644 drivers/staging/most/hdm-dim2/dim2_errors.h delete mode 100644 drivers/staging/most/hdm-dim2/dim2_hal.c delete mode 100644 drivers/staging/most/hdm-dim2/dim2_hal.h delete mode 100644 drivers/staging/most/hdm-dim2/dim2_hdm.c delete mode 100644 drivers/staging/most/hdm-dim2/dim2_hdm.h delete mode 100644 drivers/staging/most/hdm-dim2/dim2_reg.h delete mode 100644 drivers/staging/most/hdm-dim2/dim2_sysfs.c delete mode 100644 drivers/staging/most/hdm-dim2/dim2_sysfs.h (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/Kconfig b/drivers/staging/most/Kconfig index b3393a5f5706..88a415b8d07a 100644 --- a/drivers/staging/most/Kconfig +++ b/drivers/staging/most/Kconfig @@ -25,7 +25,7 @@ source "drivers/staging/most/aim-sound/Kconfig" source "drivers/staging/most/aim-v4l2/Kconfig" -source "drivers/staging/most/hdm-dim2/Kconfig" +source "drivers/staging/most/dim2/Kconfig" source "drivers/staging/most/i2c/Kconfig" diff --git a/drivers/staging/most/dim2/Kconfig b/drivers/staging/most/dim2/Kconfig new file mode 100644 index 000000000000..e39c4e525cac --- /dev/null +++ b/drivers/staging/most/dim2/Kconfig @@ -0,0 +1,16 @@ +# +# MediaLB configuration +# + +config MOST_DIM2 + tristate "DIM2" + depends on HAS_IOMEM + + ---help--- + Say Y here if you want to connect via MediaLB to network transceiver. + This device driver is platform dependent and needs an additional + platform driver to be installed. For more information contact + maintainer of this driver. + + To compile this driver as a module, choose M here: the + module will be called most_dim2. diff --git a/drivers/staging/most/dim2/Makefile b/drivers/staging/most/dim2/Makefile new file mode 100644 index 000000000000..66676f5907ee --- /dev/null +++ b/drivers/staging/most/dim2/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_MOST_DIM2) += most_dim2.o + +most_dim2-objs := dim2.o hal.o sysfs.o +ccflags-y += -Idrivers/staging/ diff --git a/drivers/staging/most/dim2/dim2.c b/drivers/staging/most/dim2/dim2.c new file mode 100644 index 000000000000..921db9880d80 --- /dev/null +++ b/drivers/staging/most/dim2/dim2.c @@ -0,0 +1,912 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * dim2.c - MediaLB DIM2 Hardware Dependent Module + * + * Copyright (C) 2015-2016, Microchip Technology Germany II GmbH & Co. KG + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "most/core.h" +#include "hal.h" +#include "dim2.h" +#include "errors.h" +#include "sysfs.h" + +#define DMA_CHANNELS (32 - 1) /* channel 0 is a system channel */ + +#define MAX_BUFFERS_PACKET 32 +#define MAX_BUFFERS_STREAMING 32 +#define MAX_BUF_SIZE_PACKET 2048 +#define MAX_BUF_SIZE_STREAMING (8 * 1024) + +/* command line parameter to select clock speed */ +static char *clock_speed; +module_param(clock_speed, charp, 0000); +MODULE_PARM_DESC(clock_speed, "MediaLB Clock Speed"); + +/* + * The parameter representing the number of frames per sub-buffer for + * synchronous channels. Valid values: [0 .. 6]. + * + * The values 0, 1, 2, 3, 4, 5, 6 represent corresponding number of frames per + * sub-buffer 1, 2, 4, 8, 16, 32, 64. + */ +static u8 fcnt = 4; /* (1 << fcnt) frames per subbuffer */ +module_param(fcnt, byte, 0000); +MODULE_PARM_DESC(fcnt, "Num of frames per sub-buffer for sync channels as a power of 2"); + +static DEFINE_SPINLOCK(dim_lock); + +static void dim2_tasklet_fn(unsigned long data); +static DECLARE_TASKLET(dim2_tasklet, dim2_tasklet_fn, 0); + +/** + * struct hdm_channel - private structure to keep channel specific data + * @is_initialized: identifier to know whether the channel is initialized + * @ch: HAL specific channel data + * @pending_list: list to keep MBO's before starting transfer + * @started_list: list to keep MBO's after starting transfer + * @direction: channel direction (TX or RX) + * @data_type: channel data type + */ +struct hdm_channel { + char name[sizeof "caNNN"]; + bool is_initialized; + struct dim_channel ch; + struct list_head pending_list; /* before dim_enqueue_buffer() */ + struct list_head started_list; /* after dim_enqueue_buffer() */ + enum most_channel_direction direction; + enum most_channel_data_type data_type; +}; + +/** + * struct dim2_hdm - private structure to keep interface specific data + * @hch: an array of channel specific data + * @most_iface: most interface structure + * @capabilities: an array of channel capability data + * @io_base: I/O register base address + * @clk_speed: user selectable (through command line parameter) clock speed + * @netinfo_task: thread to deliver network status + * @netinfo_waitq: waitq for the thread to sleep + * @deliver_netinfo: to identify whether network status received + * @mac_addrs: INIC mac address + * @link_state: network link state + * @atx_idx: index of async tx channel + */ +struct dim2_hdm { + struct hdm_channel hch[DMA_CHANNELS]; + struct most_channel_capability capabilities[DMA_CHANNELS]; + struct most_interface most_iface; + char name[16 + sizeof "dim2-"]; + void __iomem *io_base; + int clk_speed; + struct task_struct *netinfo_task; + wait_queue_head_t netinfo_waitq; + int deliver_netinfo; + unsigned char mac_addrs[6]; + unsigned char link_state; + int atx_idx; + struct medialb_bus bus; + void (*on_netinfo)(struct most_interface *, + unsigned char, unsigned char *); +}; + +#define iface_to_hdm(iface) container_of(iface, struct dim2_hdm, most_iface) + +/* Macro to identify a network status message */ +#define PACKET_IS_NET_INFO(p) \ + (((p)[1] == 0x18) && ((p)[2] == 0x05) && ((p)[3] == 0x0C) && \ + ((p)[13] == 0x3C) && ((p)[14] == 0x00) && ((p)[15] == 0x0A)) + +bool dim2_sysfs_get_state_cb(void) +{ + bool state; + unsigned long flags; + + spin_lock_irqsave(&dim_lock, flags); + state = dim_get_lock_state(); + spin_unlock_irqrestore(&dim_lock, flags); + + return state; +} + +/** + * dimcb_io_read - callback from HAL to read an I/O register + * @ptr32: register address + */ +u32 dimcb_io_read(u32 __iomem *ptr32) +{ + return readl(ptr32); +} + +/** + * dimcb_io_write - callback from HAL to write value to an I/O register + * @ptr32: register address + * @value: value to write + */ +void dimcb_io_write(u32 __iomem *ptr32, u32 value) +{ + writel(value, ptr32); +} + +/** + * dimcb_on_error - callback from HAL to report miscommunication between + * HDM and HAL + * @error_id: Error ID + * @error_message: Error message. Some text in a free format + */ +void dimcb_on_error(u8 error_id, const char *error_message) +{ + pr_err("dimcb_on_error: error_id - %d, error_message - %s\n", error_id, + error_message); +} + +/** + * startup_dim - initialize the dim2 interface + * @pdev: platform device + * + * Get the value of command line parameter "clock_speed" if given or use the + * default value, enable the clock and PLL, and initialize the dim2 interface. + */ +static int startup_dim(struct platform_device *pdev) +{ + struct dim2_hdm *dev = platform_get_drvdata(pdev); + struct dim2_platform_data *pdata = pdev->dev.platform_data; + u8 hal_ret; + + dev->clk_speed = -1; + + if (clock_speed) { + if (!strcmp(clock_speed, "256fs")) + dev->clk_speed = CLK_256FS; + else if (!strcmp(clock_speed, "512fs")) + dev->clk_speed = CLK_512FS; + else if (!strcmp(clock_speed, "1024fs")) + dev->clk_speed = CLK_1024FS; + else if (!strcmp(clock_speed, "2048fs")) + dev->clk_speed = CLK_2048FS; + else if (!strcmp(clock_speed, "3072fs")) + dev->clk_speed = CLK_3072FS; + else if (!strcmp(clock_speed, "4096fs")) + dev->clk_speed = CLK_4096FS; + else if (!strcmp(clock_speed, "6144fs")) + dev->clk_speed = CLK_6144FS; + else if (!strcmp(clock_speed, "8192fs")) + dev->clk_speed = CLK_8192FS; + } + + if (dev->clk_speed == -1) { + pr_info("Bad or missing clock speed parameter, using default value: 3072fs\n"); + dev->clk_speed = CLK_3072FS; + } else { + pr_info("Selected clock speed: %s\n", clock_speed); + } + if (pdata && pdata->init) { + int ret = pdata->init(pdata, dev->io_base, dev->clk_speed); + + if (ret) + return ret; + } + + pr_info("sync: num of frames per sub-buffer: %u\n", fcnt); + hal_ret = dim_startup(dev->io_base, dev->clk_speed, fcnt); + if (hal_ret != DIM_NO_ERROR) { + pr_err("dim_startup failed: %d\n", hal_ret); + if (pdata && pdata->destroy) + pdata->destroy(pdata); + return -ENODEV; + } + + return 0; +} + +/** + * try_start_dim_transfer - try to transfer a buffer on a channel + * @hdm_ch: channel specific data + * + * Transfer a buffer from pending_list if the channel is ready + */ +static int try_start_dim_transfer(struct hdm_channel *hdm_ch) +{ + u16 buf_size; + struct list_head *head = &hdm_ch->pending_list; + struct mbo *mbo; + unsigned long flags; + struct dim_ch_state_t st; + + BUG_ON(!hdm_ch); + BUG_ON(!hdm_ch->is_initialized); + + spin_lock_irqsave(&dim_lock, flags); + if (list_empty(head)) { + spin_unlock_irqrestore(&dim_lock, flags); + return -EAGAIN; + } + + if (!dim_get_channel_state(&hdm_ch->ch, &st)->ready) { + spin_unlock_irqrestore(&dim_lock, flags); + return -EAGAIN; + } + + mbo = list_first_entry(head, struct mbo, list); + buf_size = mbo->buffer_length; + + if (dim_dbr_space(&hdm_ch->ch) < buf_size) { + spin_unlock_irqrestore(&dim_lock, flags); + return -EAGAIN; + } + + BUG_ON(mbo->bus_address == 0); + if (!dim_enqueue_buffer(&hdm_ch->ch, mbo->bus_address, buf_size)) { + list_del(head->next); + spin_unlock_irqrestore(&dim_lock, flags); + mbo->processed_length = 0; + mbo->status = MBO_E_INVAL; + mbo->complete(mbo); + return -EFAULT; + } + + list_move_tail(head->next, &hdm_ch->started_list); + spin_unlock_irqrestore(&dim_lock, flags); + + return 0; +} + +/** + * deliver_netinfo_thread - thread to deliver network status to mostcore + * @data: private data + * + * Wait for network status and deliver it to mostcore once it is received + */ +static int deliver_netinfo_thread(void *data) +{ + struct dim2_hdm *dev = data; + + while (!kthread_should_stop()) { + wait_event_interruptible(dev->netinfo_waitq, + dev->deliver_netinfo || + kthread_should_stop()); + + if (dev->deliver_netinfo) { + dev->deliver_netinfo--; + if (dev->on_netinfo) { + dev->on_netinfo(&dev->most_iface, + dev->link_state, + dev->mac_addrs); + } + } + } + + return 0; +} + +/** + * retrieve_netinfo - retrieve network status from received buffer + * @dev: private data + * @mbo: received MBO + * + * Parse the message in buffer and get node address, link state, MAC address. + * Wake up a thread to deliver this status to mostcore + */ +static void retrieve_netinfo(struct dim2_hdm *dev, struct mbo *mbo) +{ + u8 *data = mbo->virt_address; + + pr_info("Node Address: 0x%03x\n", (u16)data[16] << 8 | data[17]); + dev->link_state = data[18]; + pr_info("NIState: %d\n", dev->link_state); + memcpy(dev->mac_addrs, data + 19, 6); + dev->deliver_netinfo++; + wake_up_interruptible(&dev->netinfo_waitq); +} + +/** + * service_done_flag - handle completed buffers + * @dev: private data + * @ch_idx: channel index + * + * Return back the completed buffers to mostcore, using completion callback + */ +static void service_done_flag(struct dim2_hdm *dev, int ch_idx) +{ + struct hdm_channel *hdm_ch = dev->hch + ch_idx; + struct dim_ch_state_t st; + struct list_head *head; + struct mbo *mbo; + int done_buffers; + unsigned long flags; + u8 *data; + + BUG_ON(!hdm_ch); + BUG_ON(!hdm_ch->is_initialized); + + spin_lock_irqsave(&dim_lock, flags); + + done_buffers = dim_get_channel_state(&hdm_ch->ch, &st)->done_buffers; + if (!done_buffers) { + spin_unlock_irqrestore(&dim_lock, flags); + return; + } + + if (!dim_detach_buffers(&hdm_ch->ch, done_buffers)) { + spin_unlock_irqrestore(&dim_lock, flags); + return; + } + spin_unlock_irqrestore(&dim_lock, flags); + + head = &hdm_ch->started_list; + + while (done_buffers) { + spin_lock_irqsave(&dim_lock, flags); + if (list_empty(head)) { + spin_unlock_irqrestore(&dim_lock, flags); + pr_crit("hard error: started_mbo list is empty whereas DIM2 has sent buffers\n"); + break; + } + + mbo = list_first_entry(head, struct mbo, list); + list_del(head->next); + spin_unlock_irqrestore(&dim_lock, flags); + + data = mbo->virt_address; + + if (hdm_ch->data_type == MOST_CH_ASYNC && + hdm_ch->direction == MOST_CH_RX && + PACKET_IS_NET_INFO(data)) { + retrieve_netinfo(dev, mbo); + + spin_lock_irqsave(&dim_lock, flags); + list_add_tail(&mbo->list, &hdm_ch->pending_list); + spin_unlock_irqrestore(&dim_lock, flags); + } else { + if (hdm_ch->data_type == MOST_CH_CONTROL || + hdm_ch->data_type == MOST_CH_ASYNC) { + u32 const data_size = + (u32)data[0] * 256 + data[1] + 2; + + mbo->processed_length = + min_t(u32, data_size, + mbo->buffer_length); + } else { + mbo->processed_length = mbo->buffer_length; + } + mbo->status = MBO_SUCCESS; + mbo->complete(mbo); + } + + done_buffers--; + } +} + +static struct dim_channel **get_active_channels(struct dim2_hdm *dev, + struct dim_channel **buffer) +{ + int idx = 0; + int ch_idx; + + for (ch_idx = 0; ch_idx < DMA_CHANNELS; ch_idx++) { + if (dev->hch[ch_idx].is_initialized) + buffer[idx++] = &dev->hch[ch_idx].ch; + } + buffer[idx++] = NULL; + + return buffer; +} + +static irqreturn_t dim2_mlb_isr(int irq, void *_dev) +{ + struct dim2_hdm *dev = _dev; + unsigned long flags; + + spin_lock_irqsave(&dim_lock, flags); + dim_service_mlb_int_irq(); + spin_unlock_irqrestore(&dim_lock, flags); + + if (dev->atx_idx >= 0 && dev->hch[dev->atx_idx].is_initialized) + while (!try_start_dim_transfer(dev->hch + dev->atx_idx)) + continue; + + return IRQ_HANDLED; +} + +/** + * dim2_tasklet_fn - tasklet function + * @data: private data + * + * Service each initialized channel, if needed + */ +static void dim2_tasklet_fn(unsigned long data) +{ + struct dim2_hdm *dev = (struct dim2_hdm *)data; + unsigned long flags; + int ch_idx; + + for (ch_idx = 0; ch_idx < DMA_CHANNELS; ch_idx++) { + if (!dev->hch[ch_idx].is_initialized) + continue; + + spin_lock_irqsave(&dim_lock, flags); + dim_service_channel(&dev->hch[ch_idx].ch); + spin_unlock_irqrestore(&dim_lock, flags); + + service_done_flag(dev, ch_idx); + while (!try_start_dim_transfer(dev->hch + ch_idx)) + continue; + } +} + +/** + * dim2_ahb_isr - interrupt service routine + * @irq: irq number + * @_dev: private data + * + * Acknowledge the interrupt and schedule a tasklet to service channels. + * Return IRQ_HANDLED. + */ +static irqreturn_t dim2_ahb_isr(int irq, void *_dev) +{ + struct dim2_hdm *dev = _dev; + struct dim_channel *buffer[DMA_CHANNELS + 1]; + unsigned long flags; + + spin_lock_irqsave(&dim_lock, flags); + dim_service_ahb_int_irq(get_active_channels(dev, buffer)); + spin_unlock_irqrestore(&dim_lock, flags); + + dim2_tasklet.data = (unsigned long)dev; + tasklet_schedule(&dim2_tasklet); + return IRQ_HANDLED; +} + +/** + * complete_all_mbos - complete MBO's in a list + * @head: list head + * + * Delete all the entries in list and return back MBO's to mostcore using + * completion call back. + */ +static void complete_all_mbos(struct list_head *head) +{ + unsigned long flags; + struct mbo *mbo; + + for (;;) { + spin_lock_irqsave(&dim_lock, flags); + if (list_empty(head)) { + spin_unlock_irqrestore(&dim_lock, flags); + break; + } + + mbo = list_first_entry(head, struct mbo, list); + list_del(head->next); + spin_unlock_irqrestore(&dim_lock, flags); + + mbo->processed_length = 0; + mbo->status = MBO_E_CLOSE; + mbo->complete(mbo); + } +} + +/** + * configure_channel - initialize a channel + * @iface: interface the channel belongs to + * @channel: channel to be configured + * @channel_config: structure that holds the configuration information + * + * Receives configuration information from mostcore and initialize + * the corresponding channel. Return 0 on success, negative on failure. + */ +static int configure_channel(struct most_interface *most_iface, int ch_idx, + struct most_channel_config *ccfg) +{ + struct dim2_hdm *dev = iface_to_hdm(most_iface); + bool const is_tx = ccfg->direction == MOST_CH_TX; + u16 const sub_size = ccfg->subbuffer_size; + u16 const buf_size = ccfg->buffer_size; + u16 new_size; + unsigned long flags; + u8 hal_ret; + int const ch_addr = ch_idx * 2 + 2; + struct hdm_channel *const hdm_ch = dev->hch + ch_idx; + + BUG_ON(ch_idx < 0 || ch_idx >= DMA_CHANNELS); + + if (hdm_ch->is_initialized) + return -EPERM; + + switch (ccfg->data_type) { + case MOST_CH_CONTROL: + new_size = dim_norm_ctrl_async_buffer_size(buf_size); + if (new_size == 0) { + pr_err("%s: too small buffer size\n", hdm_ch->name); + return -EINVAL; + } + ccfg->buffer_size = new_size; + if (new_size != buf_size) + pr_warn("%s: fixed buffer size (%d -> %d)\n", + hdm_ch->name, buf_size, new_size); + spin_lock_irqsave(&dim_lock, flags); + hal_ret = dim_init_control(&hdm_ch->ch, is_tx, ch_addr, + is_tx ? new_size * 2 : new_size); + break; + case MOST_CH_ASYNC: + new_size = dim_norm_ctrl_async_buffer_size(buf_size); + if (new_size == 0) { + pr_err("%s: too small buffer size\n", hdm_ch->name); + return -EINVAL; + } + ccfg->buffer_size = new_size; + if (new_size != buf_size) + pr_warn("%s: fixed buffer size (%d -> %d)\n", + hdm_ch->name, buf_size, new_size); + spin_lock_irqsave(&dim_lock, flags); + hal_ret = dim_init_async(&hdm_ch->ch, is_tx, ch_addr, + is_tx ? new_size * 2 : new_size); + break; + case MOST_CH_ISOC: + new_size = dim_norm_isoc_buffer_size(buf_size, sub_size); + if (new_size == 0) { + pr_err("%s: invalid sub-buffer size or too small buffer size\n", + hdm_ch->name); + return -EINVAL; + } + ccfg->buffer_size = new_size; + if (new_size != buf_size) + pr_warn("%s: fixed buffer size (%d -> %d)\n", + hdm_ch->name, buf_size, new_size); + spin_lock_irqsave(&dim_lock, flags); + hal_ret = dim_init_isoc(&hdm_ch->ch, is_tx, ch_addr, sub_size); + break; + case MOST_CH_SYNC: + new_size = dim_norm_sync_buffer_size(buf_size, sub_size); + if (new_size == 0) { + pr_err("%s: invalid sub-buffer size or too small buffer size\n", + hdm_ch->name); + return -EINVAL; + } + ccfg->buffer_size = new_size; + if (new_size != buf_size) + pr_warn("%s: fixed buffer size (%d -> %d)\n", + hdm_ch->name, buf_size, new_size); + spin_lock_irqsave(&dim_lock, flags); + hal_ret = dim_init_sync(&hdm_ch->ch, is_tx, ch_addr, sub_size); + break; + default: + pr_err("%s: configure failed, bad channel type: %d\n", + hdm_ch->name, ccfg->data_type); + return -EINVAL; + } + + if (hal_ret != DIM_NO_ERROR) { + spin_unlock_irqrestore(&dim_lock, flags); + pr_err("%s: configure failed (%d), type: %d, is_tx: %d\n", + hdm_ch->name, hal_ret, ccfg->data_type, (int)is_tx); + return -ENODEV; + } + + hdm_ch->data_type = ccfg->data_type; + hdm_ch->direction = ccfg->direction; + hdm_ch->is_initialized = true; + + if (hdm_ch->data_type == MOST_CH_ASYNC && + hdm_ch->direction == MOST_CH_TX && + dev->atx_idx < 0) + dev->atx_idx = ch_idx; + + spin_unlock_irqrestore(&dim_lock, flags); + + return 0; +} + +/** + * enqueue - enqueue a buffer for data transfer + * @iface: intended interface + * @channel: ID of the channel the buffer is intended for + * @mbo: pointer to the buffer object + * + * Push the buffer into pending_list and try to transfer one buffer from + * pending_list. Return 0 on success, negative on failure. + */ +static int enqueue(struct most_interface *most_iface, int ch_idx, + struct mbo *mbo) +{ + struct dim2_hdm *dev = iface_to_hdm(most_iface); + struct hdm_channel *hdm_ch = dev->hch + ch_idx; + unsigned long flags; + + BUG_ON(ch_idx < 0 || ch_idx >= DMA_CHANNELS); + + if (!hdm_ch->is_initialized) + return -EPERM; + + if (mbo->bus_address == 0) + return -EFAULT; + + spin_lock_irqsave(&dim_lock, flags); + list_add_tail(&mbo->list, &hdm_ch->pending_list); + spin_unlock_irqrestore(&dim_lock, flags); + + (void)try_start_dim_transfer(hdm_ch); + + return 0; +} + +/** + * request_netinfo - triggers retrieving of network info + * @iface: pointer to the interface + * @channel_id: corresponding channel ID + * + * Send a command to INIC which triggers retrieving of network info by means of + * "Message exchange over MDP/MEP". Return 0 on success, negative on failure. + */ +static void request_netinfo(struct most_interface *most_iface, int ch_idx, + void (*on_netinfo)(struct most_interface *, + unsigned char, unsigned char *)) +{ + struct dim2_hdm *dev = iface_to_hdm(most_iface); + struct mbo *mbo; + u8 *data; + + dev->on_netinfo = on_netinfo; + if (!on_netinfo) + return; + + if (dev->atx_idx < 0) { + pr_err("Async Tx Not initialized\n"); + return; + } + + mbo = most_get_mbo(&dev->most_iface, dev->atx_idx, NULL); + if (!mbo) + return; + + mbo->buffer_length = 5; + + data = mbo->virt_address; + + data[0] = 0x00; /* PML High byte */ + data[1] = 0x03; /* PML Low byte */ + data[2] = 0x02; /* PMHL */ + data[3] = 0x08; /* FPH */ + data[4] = 0x40; /* FMF (FIFO cmd msg - Triggers NAOverMDP) */ + + most_submit_mbo(mbo); +} + +/** + * poison_channel - poison buffers of a channel + * @iface: pointer to the interface the channel to be poisoned belongs to + * @channel_id: corresponding channel ID + * + * Destroy a channel and complete all the buffers in both started_list & + * pending_list. Return 0 on success, negative on failure. + */ +static int poison_channel(struct most_interface *most_iface, int ch_idx) +{ + struct dim2_hdm *dev = iface_to_hdm(most_iface); + struct hdm_channel *hdm_ch = dev->hch + ch_idx; + unsigned long flags; + u8 hal_ret; + int ret = 0; + + BUG_ON(ch_idx < 0 || ch_idx >= DMA_CHANNELS); + + if (!hdm_ch->is_initialized) + return -EPERM; + + tasklet_disable(&dim2_tasklet); + spin_lock_irqsave(&dim_lock, flags); + hal_ret = dim_destroy_channel(&hdm_ch->ch); + hdm_ch->is_initialized = false; + if (ch_idx == dev->atx_idx) + dev->atx_idx = -1; + spin_unlock_irqrestore(&dim_lock, flags); + tasklet_enable(&dim2_tasklet); + if (hal_ret != DIM_NO_ERROR) { + pr_err("HAL Failed to close channel %s\n", hdm_ch->name); + ret = -EFAULT; + } + + complete_all_mbos(&hdm_ch->started_list); + complete_all_mbos(&hdm_ch->pending_list); + + return ret; +} + +/* + * dim2_probe - dim2 probe handler + * @pdev: platform device structure + * + * Register the dim2 interface with mostcore and initialize it. + * Return 0 on success, negative on failure. + */ +static int dim2_probe(struct platform_device *pdev) +{ + struct dim2_hdm *dev; + struct resource *res; + int ret, i; + struct kobject *kobj; + int irq; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->atx_idx = -1; + + platform_set_drvdata(pdev, dev); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dev->io_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dev->io_base)) + return PTR_ERR(dev->io_base); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get ahb0_int irq: %d\n", irq); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, dim2_ahb_isr, 0, + "dim2_ahb0_int", dev); + if (ret) { + dev_err(&pdev->dev, "failed to request ahb0_int irq %d\n", irq); + return ret; + } + + irq = platform_get_irq(pdev, 1); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get mlb_int irq: %d\n", irq); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, dim2_mlb_isr, 0, + "dim2_mlb_int", dev); + if (ret) { + dev_err(&pdev->dev, "failed to request mlb_int irq %d\n", irq); + return ret; + } + + init_waitqueue_head(&dev->netinfo_waitq); + dev->deliver_netinfo = 0; + dev->netinfo_task = kthread_run(&deliver_netinfo_thread, (void *)dev, + "dim2_netinfo"); + if (IS_ERR(dev->netinfo_task)) + return PTR_ERR(dev->netinfo_task); + + for (i = 0; i < DMA_CHANNELS; i++) { + struct most_channel_capability *cap = dev->capabilities + i; + struct hdm_channel *hdm_ch = dev->hch + i; + + INIT_LIST_HEAD(&hdm_ch->pending_list); + INIT_LIST_HEAD(&hdm_ch->started_list); + hdm_ch->is_initialized = false; + snprintf(hdm_ch->name, sizeof(hdm_ch->name), "ca%d", i * 2 + 2); + + cap->name_suffix = hdm_ch->name; + cap->direction = MOST_CH_RX | MOST_CH_TX; + cap->data_type = MOST_CH_CONTROL | MOST_CH_ASYNC | + MOST_CH_ISOC | MOST_CH_SYNC; + cap->num_buffers_packet = MAX_BUFFERS_PACKET; + cap->buffer_size_packet = MAX_BUF_SIZE_PACKET; + cap->num_buffers_streaming = MAX_BUFFERS_STREAMING; + cap->buffer_size_streaming = MAX_BUF_SIZE_STREAMING; + } + + { + const char *fmt; + + if (sizeof(res->start) == sizeof(long long)) + fmt = "dim2-%016llx"; + else if (sizeof(res->start) == sizeof(long)) + fmt = "dim2-%016lx"; + else + fmt = "dim2-%016x"; + + snprintf(dev->name, sizeof(dev->name), fmt, res->start); + } + + dev->most_iface.interface = ITYPE_MEDIALB_DIM2; + dev->most_iface.description = dev->name; + dev->most_iface.num_channels = DMA_CHANNELS; + dev->most_iface.channel_vector = dev->capabilities; + dev->most_iface.configure = configure_channel; + dev->most_iface.enqueue = enqueue; + dev->most_iface.poison_channel = poison_channel; + dev->most_iface.request_netinfo = request_netinfo; + + kobj = most_register_interface(&dev->most_iface); + if (IS_ERR(kobj)) { + ret = PTR_ERR(kobj); + dev_err(&pdev->dev, "failed to register MOST interface\n"); + goto err_stop_thread; + } + + ret = dim2_sysfs_probe(&dev->bus, kobj); + if (ret) + goto err_unreg_iface; + + ret = startup_dim(pdev); + if (ret) { + dev_err(&pdev->dev, "failed to initialize DIM2\n"); + goto err_destroy_bus; + } + + return 0; + +err_destroy_bus: + dim2_sysfs_destroy(&dev->bus); +err_unreg_iface: + most_deregister_interface(&dev->most_iface); +err_stop_thread: + kthread_stop(dev->netinfo_task); + + return ret; +} + +/** + * dim2_remove - dim2 remove handler + * @pdev: platform device structure + * + * Unregister the interface from mostcore + */ +static int dim2_remove(struct platform_device *pdev) +{ + struct dim2_hdm *dev = platform_get_drvdata(pdev); + struct dim2_platform_data *pdata = pdev->dev.platform_data; + unsigned long flags; + + spin_lock_irqsave(&dim_lock, flags); + dim_shutdown(); + spin_unlock_irqrestore(&dim_lock, flags); + + if (pdata && pdata->destroy) + pdata->destroy(pdata); + + dim2_sysfs_destroy(&dev->bus); + most_deregister_interface(&dev->most_iface); + kthread_stop(dev->netinfo_task); + + /* + * break link to local platform_device_id struct + * to prevent crash by unload platform device module + */ + pdev->id_entry = NULL; + + return 0; +} + +static const struct platform_device_id dim2_id[] = { + { "medialb_dim2" }, + { }, /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(platform, dim2_id); + +static struct platform_driver dim2_driver = { + .probe = dim2_probe, + .remove = dim2_remove, + .id_table = dim2_id, + .driver = { + .name = "hdm_dim2", + }, +}; + +module_platform_driver(dim2_driver); + +MODULE_AUTHOR("Jain Roy Ambi "); +MODULE_AUTHOR("Andrey Shvetsov "); +MODULE_DESCRIPTION("MediaLB DIM2 Hardware Dependent Module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/most/dim2/dim2.h b/drivers/staging/most/dim2/dim2.h new file mode 100644 index 000000000000..6a9fc51a2eb4 --- /dev/null +++ b/drivers/staging/most/dim2/dim2.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * dim2.h - MediaLB DIM2 HDM Header + * + * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG + */ + +#ifndef DIM2_HDM_H +#define DIM2_HDM_H + +struct device; + +/* platform dependent data for dim2 interface */ +struct dim2_platform_data { + int (*init)(struct dim2_platform_data *pd, void __iomem *io_base, + int clk_speed); + void (*destroy)(struct dim2_platform_data *pd); + void *priv; +}; + +#endif /* DIM2_HDM_H */ diff --git a/drivers/staging/most/dim2/errors.h b/drivers/staging/most/dim2/errors.h new file mode 100644 index 000000000000..3487510fbd2f --- /dev/null +++ b/drivers/staging/most/dim2/errors.h @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * errors.h - Definitions of errors for DIM2 HAL API + * (MediaLB, Device Interface Macro IP, OS62420) + * + * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG + */ + +#ifndef _MOST_DIM_ERRORS_H +#define _MOST_DIM_ERRORS_H + +/** + * MOST DIM errors. + */ +enum dim_errors_t { + /** Not an error */ + DIM_NO_ERROR = 0, + + /** Bad base address for DIM2 IP */ + DIM_INIT_ERR_DIM_ADDR = 0x10, + + /**< Bad MediaLB clock */ + DIM_INIT_ERR_MLB_CLOCK, + + /** Bad channel address */ + DIM_INIT_ERR_CHANNEL_ADDRESS, + + /** Out of DBR memory */ + DIM_INIT_ERR_OUT_OF_MEMORY, + + /** DIM API is called while DIM is not initialized successfully */ + DIM_ERR_DRIVER_NOT_INITIALIZED = 0x20, + + /** + * Configuration does not respect hardware limitations + * for isochronous or synchronous channels + */ + DIM_ERR_BAD_CONFIG, + + /** + * Buffer size does not respect hardware limitations + * for isochronous or synchronous channels + */ + DIM_ERR_BAD_BUFFER_SIZE, + + DIM_ERR_UNDERFLOW, + + DIM_ERR_OVERFLOW, +}; + +#endif /* _MOST_DIM_ERRORS_H */ diff --git a/drivers/staging/most/dim2/hal.c b/drivers/staging/most/dim2/hal.c new file mode 100644 index 000000000000..17c04e1c5e62 --- /dev/null +++ b/drivers/staging/most/dim2/hal.c @@ -0,0 +1,979 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * hal.c - DIM2 HAL implementation + * (MediaLB, Device Interface Macro IP, OS62420) + * + * Copyright (C) 2015-2016, Microchip Technology Germany II GmbH & Co. KG + */ + +/* Author: Andrey Shvetsov */ + +#include "hal.h" +#include "errors.h" +#include "reg.h" +#include +#include + +/* + * Size factor for isochronous DBR buffer. + * Minimal value is 3. + */ +#define ISOC_DBR_FACTOR 3u + +/* + * Number of 32-bit units for DBR map. + * + * 1: block size is 512, max allocation is 16K + * 2: block size is 256, max allocation is 8K + * 4: block size is 128, max allocation is 4K + * 8: block size is 64, max allocation is 2K + * + * Min allocated space is block size. + * Max possible allocated space is 32 blocks. + */ +#define DBR_MAP_SIZE 2 + +/* -------------------------------------------------------------------------- */ +/* not configurable area */ + +#define CDT 0x00 +#define ADT 0x40 +#define MLB_CAT 0x80 +#define AHB_CAT 0x88 + +#define DBR_SIZE (16 * 1024) /* specified by IP */ +#define DBR_BLOCK_SIZE (DBR_SIZE / 32 / DBR_MAP_SIZE) + +#define ROUND_UP_TO(x, d) (DIV_ROUND_UP(x, (d)) * (d)) + +/* -------------------------------------------------------------------------- */ +/* generic helper functions and macros */ + +static inline u32 bit_mask(u8 position) +{ + return (u32)1 << position; +} + +static inline bool dim_on_error(u8 error_id, const char *error_message) +{ + dimcb_on_error(error_id, error_message); + return false; +} + +/* -------------------------------------------------------------------------- */ +/* types and local variables */ + +struct async_tx_dbr { + u8 ch_addr; + u16 rpc; + u16 wpc; + u16 rest_size; + u16 sz_queue[CDT0_RPC_MASK + 1]; +}; + +struct lld_global_vars_t { + bool dim_is_initialized; + bool mcm_is_initialized; + struct dim2_regs __iomem *dim2; /* DIM2 core base address */ + struct async_tx_dbr atx_dbr; + u32 fcnt; + u32 dbr_map[DBR_MAP_SIZE]; +}; + +static struct lld_global_vars_t g = { false }; + +/* -------------------------------------------------------------------------- */ + +static int dbr_get_mask_size(u16 size) +{ + int i; + + for (i = 0; i < 6; i++) + if (size <= (DBR_BLOCK_SIZE << i)) + return 1 << i; + return 0; +} + +/** + * Allocates DBR memory. + * @param size Allocating memory size. + * @return Offset in DBR memory by success or DBR_SIZE if out of memory. + */ +static int alloc_dbr(u16 size) +{ + int mask_size; + int i, block_idx = 0; + + if (size <= 0) + return DBR_SIZE; /* out of memory */ + + mask_size = dbr_get_mask_size(size); + if (mask_size == 0) + return DBR_SIZE; /* out of memory */ + + for (i = 0; i < DBR_MAP_SIZE; i++) { + u32 const blocks = DIV_ROUND_UP(size, DBR_BLOCK_SIZE); + u32 mask = ~((~(u32)0) << blocks); + + do { + if ((g.dbr_map[i] & mask) == 0) { + g.dbr_map[i] |= mask; + return block_idx * DBR_BLOCK_SIZE; + } + block_idx += mask_size; + /* do shift left with 2 steps in case mask_size == 32 */ + mask <<= mask_size - 1; + } while ((mask <<= 1) != 0); + } + + return DBR_SIZE; /* out of memory */ +} + +static void free_dbr(int offs, int size) +{ + int block_idx = offs / DBR_BLOCK_SIZE; + u32 const blocks = DIV_ROUND_UP(size, DBR_BLOCK_SIZE); + u32 mask = ~((~(u32)0) << blocks); + + mask <<= block_idx % 32; + g.dbr_map[block_idx / 32] &= ~mask; +} + +/* -------------------------------------------------------------------------- */ + +static void dim2_transfer_madr(u32 val) +{ + dimcb_io_write(&g.dim2->MADR, val); + + /* wait for transfer completion */ + while ((dimcb_io_read(&g.dim2->MCTL) & 1) != 1) + continue; + + dimcb_io_write(&g.dim2->MCTL, 0); /* clear transfer complete */ +} + +static void dim2_clear_dbr(u16 addr, u16 size) +{ + enum { MADR_TB_BIT = 30, MADR_WNR_BIT = 31 }; + + u16 const end_addr = addr + size; + u32 const cmd = bit_mask(MADR_WNR_BIT) | bit_mask(MADR_TB_BIT); + + dimcb_io_write(&g.dim2->MCTL, 0); /* clear transfer complete */ + dimcb_io_write(&g.dim2->MDAT0, 0); + + for (; addr < end_addr; addr++) + dim2_transfer_madr(cmd | addr); +} + +static u32 dim2_read_ctr(u32 ctr_addr, u16 mdat_idx) +{ + dim2_transfer_madr(ctr_addr); + + return dimcb_io_read((&g.dim2->MDAT0) + mdat_idx); +} + +static void dim2_write_ctr_mask(u32 ctr_addr, const u32 *mask, const u32 *value) +{ + enum { MADR_WNR_BIT = 31 }; + + dimcb_io_write(&g.dim2->MCTL, 0); /* clear transfer complete */ + + if (mask[0] != 0) + dimcb_io_write(&g.dim2->MDAT0, value[0]); + if (mask[1] != 0) + dimcb_io_write(&g.dim2->MDAT1, value[1]); + if (mask[2] != 0) + dimcb_io_write(&g.dim2->MDAT2, value[2]); + if (mask[3] != 0) + dimcb_io_write(&g.dim2->MDAT3, value[3]); + + dimcb_io_write(&g.dim2->MDWE0, mask[0]); + dimcb_io_write(&g.dim2->MDWE1, mask[1]); + dimcb_io_write(&g.dim2->MDWE2, mask[2]); + dimcb_io_write(&g.dim2->MDWE3, mask[3]); + + dim2_transfer_madr(bit_mask(MADR_WNR_BIT) | ctr_addr); +} + +static inline void dim2_write_ctr(u32 ctr_addr, const u32 *value) +{ + u32 const mask[4] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + + dim2_write_ctr_mask(ctr_addr, mask, value); +} + +static inline void dim2_clear_ctr(u32 ctr_addr) +{ + u32 const value[4] = { 0, 0, 0, 0 }; + + dim2_write_ctr(ctr_addr, value); +} + +static void dim2_configure_cat(u8 cat_base, u8 ch_addr, u8 ch_type, + bool read_not_write) +{ + bool isoc_fce = ch_type == CAT_CT_VAL_ISOC; + bool sync_mfe = ch_type == CAT_CT_VAL_SYNC; + u16 const cat = + (read_not_write << CAT_RNW_BIT) | + (ch_type << CAT_CT_SHIFT) | + (ch_addr << CAT_CL_SHIFT) | + (isoc_fce << CAT_FCE_BIT) | + (sync_mfe << CAT_MFE_BIT) | + (false << CAT_MT_BIT) | + (true << CAT_CE_BIT); + u8 const ctr_addr = cat_base + ch_addr / 8; + u8 const idx = (ch_addr % 8) / 2; + u8 const shift = (ch_addr % 2) * 16; + u32 mask[4] = { 0, 0, 0, 0 }; + u32 value[4] = { 0, 0, 0, 0 }; + + mask[idx] = (u32)0xFFFF << shift; + value[idx] = cat << shift; + dim2_write_ctr_mask(ctr_addr, mask, value); +} + +static void dim2_clear_cat(u8 cat_base, u8 ch_addr) +{ + u8 const ctr_addr = cat_base + ch_addr / 8; + u8 const idx = (ch_addr % 8) / 2; + u8 const shift = (ch_addr % 2) * 16; + u32 mask[4] = { 0, 0, 0, 0 }; + u32 value[4] = { 0, 0, 0, 0 }; + + mask[idx] = (u32)0xFFFF << shift; + dim2_write_ctr_mask(ctr_addr, mask, value); +} + +static void dim2_configure_cdt(u8 ch_addr, u16 dbr_address, u16 hw_buffer_size, + u16 packet_length) +{ + u32 cdt[4] = { 0, 0, 0, 0 }; + + if (packet_length) + cdt[1] = ((packet_length - 1) << CDT1_BS_ISOC_SHIFT); + + cdt[3] = + ((hw_buffer_size - 1) << CDT3_BD_SHIFT) | + (dbr_address << CDT3_BA_SHIFT); + dim2_write_ctr(CDT + ch_addr, cdt); +} + +static u16 dim2_rpc(u8 ch_addr) +{ + u32 cdt0 = dim2_read_ctr(CDT + ch_addr, 0); + + return (cdt0 >> CDT0_RPC_SHIFT) & CDT0_RPC_MASK; +} + +static void dim2_clear_cdt(u8 ch_addr) +{ + u32 cdt[4] = { 0, 0, 0, 0 }; + + dim2_write_ctr(CDT + ch_addr, cdt); +} + +static void dim2_configure_adt(u8 ch_addr) +{ + u32 adt[4] = { 0, 0, 0, 0 }; + + adt[0] = + (true << ADT0_CE_BIT) | + (true << ADT0_LE_BIT) | + (0 << ADT0_PG_BIT); + + dim2_write_ctr(ADT + ch_addr, adt); +} + +static void dim2_clear_adt(u8 ch_addr) +{ + u32 adt[4] = { 0, 0, 0, 0 }; + + dim2_write_ctr(ADT + ch_addr, adt); +} + +static void dim2_start_ctrl_async(u8 ch_addr, u8 idx, u32 buf_addr, + u16 buffer_size) +{ + u8 const shift = idx * 16; + + u32 mask[4] = { 0, 0, 0, 0 }; + u32 adt[4] = { 0, 0, 0, 0 }; + + mask[1] = + bit_mask(ADT1_PS_BIT + shift) | + bit_mask(ADT1_RDY_BIT + shift) | + (ADT1_CTRL_ASYNC_BD_MASK << (ADT1_BD_SHIFT + shift)); + adt[1] = + (true << (ADT1_PS_BIT + shift)) | + (true << (ADT1_RDY_BIT + shift)) | + ((buffer_size - 1) << (ADT1_BD_SHIFT + shift)); + + mask[idx + 2] = 0xFFFFFFFF; + adt[idx + 2] = buf_addr; + + dim2_write_ctr_mask(ADT + ch_addr, mask, adt); +} + +static void dim2_start_isoc_sync(u8 ch_addr, u8 idx, u32 buf_addr, + u16 buffer_size) +{ + u8 const shift = idx * 16; + + u32 mask[4] = { 0, 0, 0, 0 }; + u32 adt[4] = { 0, 0, 0, 0 }; + + mask[1] = + bit_mask(ADT1_RDY_BIT + shift) | + (ADT1_ISOC_SYNC_BD_MASK << (ADT1_BD_SHIFT + shift)); + adt[1] = + (true << (ADT1_RDY_BIT + shift)) | + ((buffer_size - 1) << (ADT1_BD_SHIFT + shift)); + + mask[idx + 2] = 0xFFFFFFFF; + adt[idx + 2] = buf_addr; + + dim2_write_ctr_mask(ADT + ch_addr, mask, adt); +} + +static void dim2_clear_ctram(void) +{ + u32 ctr_addr; + + for (ctr_addr = 0; ctr_addr < 0x90; ctr_addr++) + dim2_clear_ctr(ctr_addr); +} + +static void dim2_configure_channel( + u8 ch_addr, u8 type, u8 is_tx, u16 dbr_address, u16 hw_buffer_size, + u16 packet_length) +{ + dim2_configure_cdt(ch_addr, dbr_address, hw_buffer_size, packet_length); + dim2_configure_cat(MLB_CAT, ch_addr, type, is_tx ? 1 : 0); + + dim2_configure_adt(ch_addr); + dim2_configure_cat(AHB_CAT, ch_addr, type, is_tx ? 0 : 1); + + /* unmask interrupt for used channel, enable mlb_sys_int[0] interrupt */ + dimcb_io_write(&g.dim2->ACMR0, + dimcb_io_read(&g.dim2->ACMR0) | bit_mask(ch_addr)); +} + +static void dim2_clear_channel(u8 ch_addr) +{ + /* mask interrupt for used channel, disable mlb_sys_int[0] interrupt */ + dimcb_io_write(&g.dim2->ACMR0, + dimcb_io_read(&g.dim2->ACMR0) & ~bit_mask(ch_addr)); + + dim2_clear_cat(AHB_CAT, ch_addr); + dim2_clear_adt(ch_addr); + + dim2_clear_cat(MLB_CAT, ch_addr); + dim2_clear_cdt(ch_addr); + + /* clear channel status bit */ + dimcb_io_write(&g.dim2->ACSR0, bit_mask(ch_addr)); +} + +/* -------------------------------------------------------------------------- */ +/* trace async tx dbr fill state */ + +static inline u16 norm_pc(u16 pc) +{ + return pc & CDT0_RPC_MASK; +} + +static void dbrcnt_init(u8 ch_addr, u16 dbr_size) +{ + g.atx_dbr.rest_size = dbr_size; + g.atx_dbr.rpc = dim2_rpc(ch_addr); + g.atx_dbr.wpc = g.atx_dbr.rpc; +} + +static void dbrcnt_enq(int buf_sz) +{ + g.atx_dbr.rest_size -= buf_sz; + g.atx_dbr.sz_queue[norm_pc(g.atx_dbr.wpc)] = buf_sz; + g.atx_dbr.wpc++; +} + +u16 dim_dbr_space(struct dim_channel *ch) +{ + u16 cur_rpc; + struct async_tx_dbr *dbr = &g.atx_dbr; + + if (ch->addr != dbr->ch_addr) + return 0xFFFF; + + cur_rpc = dim2_rpc(ch->addr); + + while (norm_pc(dbr->rpc) != cur_rpc) { + dbr->rest_size += dbr->sz_queue[norm_pc(dbr->rpc)]; + dbr->rpc++; + } + + if ((u16)(dbr->wpc - dbr->rpc) >= CDT0_RPC_MASK) + return 0; + + return dbr->rest_size; +} + +/* -------------------------------------------------------------------------- */ +/* channel state helpers */ + +static void state_init(struct int_ch_state *state) +{ + state->request_counter = 0; + state->service_counter = 0; + + state->idx1 = 0; + state->idx2 = 0; + state->level = 0; +} + +/* -------------------------------------------------------------------------- */ +/* macro helper functions */ + +static inline bool check_channel_address(u32 ch_address) +{ + return ch_address > 0 && (ch_address % 2) == 0 && + (ch_address / 2) <= (u32)CAT_CL_MASK; +} + +static inline bool check_packet_length(u32 packet_length) +{ + u16 const max_size = ((u16)CDT3_BD_ISOC_MASK + 1u) / ISOC_DBR_FACTOR; + + if (packet_length <= 0) + return false; /* too small */ + + if (packet_length > max_size) + return false; /* too big */ + + if (packet_length - 1u > (u32)CDT1_BS_ISOC_MASK) + return false; /* too big */ + + return true; +} + +static inline bool check_bytes_per_frame(u32 bytes_per_frame) +{ + u16 const bd_factor = g.fcnt + 2; + u16 const max_size = ((u16)CDT3_BD_MASK + 1u) >> bd_factor; + + if (bytes_per_frame <= 0) + return false; /* too small */ + + if (bytes_per_frame > max_size) + return false; /* too big */ + + return true; +} + +static inline u16 norm_ctrl_async_buffer_size(u16 buf_size) +{ + u16 const max_size = (u16)ADT1_CTRL_ASYNC_BD_MASK + 1u; + + if (buf_size > max_size) + return max_size; + + return buf_size; +} + +static inline u16 norm_isoc_buffer_size(u16 buf_size, u16 packet_length) +{ + u16 n; + u16 const max_size = (u16)ADT1_ISOC_SYNC_BD_MASK + 1u; + + if (buf_size > max_size) + buf_size = max_size; + + n = buf_size / packet_length; + + if (n < 2u) + return 0; /* too small buffer for given packet_length */ + + return packet_length * n; +} + +static inline u16 norm_sync_buffer_size(u16 buf_size, u16 bytes_per_frame) +{ + u16 n; + u16 const max_size = (u16)ADT1_ISOC_SYNC_BD_MASK + 1u; + u32 const unit = bytes_per_frame << g.fcnt; + + if (buf_size > max_size) + buf_size = max_size; + + n = buf_size / unit; + + if (n < 1u) + return 0; /* too small buffer for given bytes_per_frame */ + + return unit * n; +} + +static void dim2_cleanup(void) +{ + /* disable MediaLB */ + dimcb_io_write(&g.dim2->MLBC0, false << MLBC0_MLBEN_BIT); + + dim2_clear_ctram(); + + /* disable mlb_int interrupt */ + dimcb_io_write(&g.dim2->MIEN, 0); + + /* clear status for all dma channels */ + dimcb_io_write(&g.dim2->ACSR0, 0xFFFFFFFF); + dimcb_io_write(&g.dim2->ACSR1, 0xFFFFFFFF); + + /* mask interrupts for all channels */ + dimcb_io_write(&g.dim2->ACMR0, 0); + dimcb_io_write(&g.dim2->ACMR1, 0); +} + +static void dim2_initialize(bool enable_6pin, u8 mlb_clock) +{ + dim2_cleanup(); + + /* configure and enable MediaLB */ + dimcb_io_write(&g.dim2->MLBC0, + enable_6pin << MLBC0_MLBPEN_BIT | + mlb_clock << MLBC0_MLBCLK_SHIFT | + g.fcnt << MLBC0_FCNT_SHIFT | + true << MLBC0_MLBEN_BIT); + + /* activate all HBI channels */ + dimcb_io_write(&g.dim2->HCMR0, 0xFFFFFFFF); + dimcb_io_write(&g.dim2->HCMR1, 0xFFFFFFFF); + + /* enable HBI */ + dimcb_io_write(&g.dim2->HCTL, bit_mask(HCTL_EN_BIT)); + + /* configure DMA */ + dimcb_io_write(&g.dim2->ACTL, + ACTL_DMA_MODE_VAL_DMA_MODE_1 << ACTL_DMA_MODE_BIT | + true << ACTL_SCE_BIT); +} + +static bool dim2_is_mlb_locked(void) +{ + u32 const mask0 = bit_mask(MLBC0_MLBLK_BIT); + u32 const mask1 = bit_mask(MLBC1_CLKMERR_BIT) | + bit_mask(MLBC1_LOCKERR_BIT); + u32 const c1 = dimcb_io_read(&g.dim2->MLBC1); + u32 const nda_mask = (u32)MLBC1_NDA_MASK << MLBC1_NDA_SHIFT; + + dimcb_io_write(&g.dim2->MLBC1, c1 & nda_mask); + return (dimcb_io_read(&g.dim2->MLBC1) & mask1) == 0 && + (dimcb_io_read(&g.dim2->MLBC0) & mask0) != 0; +} + +/* -------------------------------------------------------------------------- */ +/* channel help routines */ + +static inline bool service_channel(u8 ch_addr, u8 idx) +{ + u8 const shift = idx * 16; + u32 const adt1 = dim2_read_ctr(ADT + ch_addr, 1); + u32 mask[4] = { 0, 0, 0, 0 }; + u32 adt_w[4] = { 0, 0, 0, 0 }; + + if (((adt1 >> (ADT1_DNE_BIT + shift)) & 1) == 0) + return false; + + mask[1] = + bit_mask(ADT1_DNE_BIT + shift) | + bit_mask(ADT1_ERR_BIT + shift) | + bit_mask(ADT1_RDY_BIT + shift); + dim2_write_ctr_mask(ADT + ch_addr, mask, adt_w); + + /* clear channel status bit */ + dimcb_io_write(&g.dim2->ACSR0, bit_mask(ch_addr)); + + return true; +} + +/* -------------------------------------------------------------------------- */ +/* channel init routines */ + +static void isoc_init(struct dim_channel *ch, u8 ch_addr, u16 packet_length) +{ + state_init(&ch->state); + + ch->addr = ch_addr; + + ch->packet_length = packet_length; + ch->bytes_per_frame = 0; + ch->done_sw_buffers_number = 0; +} + +static void sync_init(struct dim_channel *ch, u8 ch_addr, u16 bytes_per_frame) +{ + state_init(&ch->state); + + ch->addr = ch_addr; + + ch->packet_length = 0; + ch->bytes_per_frame = bytes_per_frame; + ch->done_sw_buffers_number = 0; +} + +static void channel_init(struct dim_channel *ch, u8 ch_addr) +{ + state_init(&ch->state); + + ch->addr = ch_addr; + + ch->packet_length = 0; + ch->bytes_per_frame = 0; + ch->done_sw_buffers_number = 0; +} + +/* returns true if channel interrupt state is cleared */ +static bool channel_service_interrupt(struct dim_channel *ch) +{ + struct int_ch_state *const state = &ch->state; + + if (!service_channel(ch->addr, state->idx2)) + return false; + + state->idx2 ^= 1; + state->request_counter++; + return true; +} + +static bool channel_start(struct dim_channel *ch, u32 buf_addr, u16 buf_size) +{ + struct int_ch_state *const state = &ch->state; + + if (buf_size <= 0) + return dim_on_error(DIM_ERR_BAD_BUFFER_SIZE, "Bad buffer size"); + + if (ch->packet_length == 0 && ch->bytes_per_frame == 0 && + buf_size != norm_ctrl_async_buffer_size(buf_size)) + return dim_on_error(DIM_ERR_BAD_BUFFER_SIZE, + "Bad control/async buffer size"); + + if (ch->packet_length && + buf_size != norm_isoc_buffer_size(buf_size, ch->packet_length)) + return dim_on_error(DIM_ERR_BAD_BUFFER_SIZE, + "Bad isochronous buffer size"); + + if (ch->bytes_per_frame && + buf_size != norm_sync_buffer_size(buf_size, ch->bytes_per_frame)) + return dim_on_error(DIM_ERR_BAD_BUFFER_SIZE, + "Bad synchronous buffer size"); + + if (state->level >= 2u) + return dim_on_error(DIM_ERR_OVERFLOW, "Channel overflow"); + + ++state->level; + + if (ch->addr == g.atx_dbr.ch_addr) + dbrcnt_enq(buf_size); + + if (ch->packet_length || ch->bytes_per_frame) + dim2_start_isoc_sync(ch->addr, state->idx1, buf_addr, buf_size); + else + dim2_start_ctrl_async(ch->addr, state->idx1, buf_addr, + buf_size); + state->idx1 ^= 1; + + return true; +} + +static u8 channel_service(struct dim_channel *ch) +{ + struct int_ch_state *const state = &ch->state; + + if (state->service_counter != state->request_counter) { + state->service_counter++; + if (state->level == 0) + return DIM_ERR_UNDERFLOW; + + --state->level; + ch->done_sw_buffers_number++; + } + + return DIM_NO_ERROR; +} + +static bool channel_detach_buffers(struct dim_channel *ch, u16 buffers_number) +{ + if (buffers_number > ch->done_sw_buffers_number) + return dim_on_error(DIM_ERR_UNDERFLOW, "Channel underflow"); + + ch->done_sw_buffers_number -= buffers_number; + return true; +} + +/* -------------------------------------------------------------------------- */ +/* API */ + +u8 dim_startup(struct dim2_regs __iomem *dim_base_address, u32 mlb_clock, + u32 fcnt) +{ + g.dim_is_initialized = false; + + if (!dim_base_address) + return DIM_INIT_ERR_DIM_ADDR; + + /* MediaLB clock: 0 - 256 fs, 1 - 512 fs, 2 - 1024 fs, 3 - 2048 fs */ + /* MediaLB clock: 4 - 3072 fs, 5 - 4096 fs, 6 - 6144 fs, 7 - 8192 fs */ + if (mlb_clock >= 8) + return DIM_INIT_ERR_MLB_CLOCK; + + if (fcnt > MLBC0_FCNT_MAX_VAL) + return DIM_INIT_ERR_MLB_CLOCK; + + g.dim2 = dim_base_address; + g.fcnt = fcnt; + g.dbr_map[0] = 0; + g.dbr_map[1] = 0; + + dim2_initialize(mlb_clock >= 3, mlb_clock); + + g.dim_is_initialized = true; + + return DIM_NO_ERROR; +} + +void dim_shutdown(void) +{ + g.dim_is_initialized = false; + dim2_cleanup(); +} + +bool dim_get_lock_state(void) +{ + return dim2_is_mlb_locked(); +} + +static u8 init_ctrl_async(struct dim_channel *ch, u8 type, u8 is_tx, + u16 ch_address, u16 hw_buffer_size) +{ + if (!g.dim_is_initialized || !ch) + return DIM_ERR_DRIVER_NOT_INITIALIZED; + + if (!check_channel_address(ch_address)) + return DIM_INIT_ERR_CHANNEL_ADDRESS; + + ch->dbr_size = ROUND_UP_TO(hw_buffer_size, DBR_BLOCK_SIZE); + ch->dbr_addr = alloc_dbr(ch->dbr_size); + if (ch->dbr_addr >= DBR_SIZE) + return DIM_INIT_ERR_OUT_OF_MEMORY; + + channel_init(ch, ch_address / 2); + + dim2_configure_channel(ch->addr, type, is_tx, + ch->dbr_addr, ch->dbr_size, 0); + + return DIM_NO_ERROR; +} + +void dim_service_mlb_int_irq(void) +{ + dimcb_io_write(&g.dim2->MS0, 0); + dimcb_io_write(&g.dim2->MS1, 0); +} + +u16 dim_norm_ctrl_async_buffer_size(u16 buf_size) +{ + return norm_ctrl_async_buffer_size(buf_size); +} + +/** + * Retrieves maximal possible correct buffer size for isochronous data type + * conform to given packet length and not bigger than given buffer size. + * + * Returns non-zero correct buffer size or zero by error. + */ +u16 dim_norm_isoc_buffer_size(u16 buf_size, u16 packet_length) +{ + if (!check_packet_length(packet_length)) + return 0; + + return norm_isoc_buffer_size(buf_size, packet_length); +} + +/** + * Retrieves maximal possible correct buffer size for synchronous data type + * conform to given bytes per frame and not bigger than given buffer size. + * + * Returns non-zero correct buffer size or zero by error. + */ +u16 dim_norm_sync_buffer_size(u16 buf_size, u16 bytes_per_frame) +{ + if (!check_bytes_per_frame(bytes_per_frame)) + return 0; + + return norm_sync_buffer_size(buf_size, bytes_per_frame); +} + +u8 dim_init_control(struct dim_channel *ch, u8 is_tx, u16 ch_address, + u16 max_buffer_size) +{ + return init_ctrl_async(ch, CAT_CT_VAL_CONTROL, is_tx, ch_address, + max_buffer_size); +} + +u8 dim_init_async(struct dim_channel *ch, u8 is_tx, u16 ch_address, + u16 max_buffer_size) +{ + u8 ret = init_ctrl_async(ch, CAT_CT_VAL_ASYNC, is_tx, ch_address, + max_buffer_size); + + if (is_tx && !g.atx_dbr.ch_addr) { + g.atx_dbr.ch_addr = ch->addr; + dbrcnt_init(ch->addr, ch->dbr_size); + dimcb_io_write(&g.dim2->MIEN, bit_mask(20)); + } + + return ret; +} + +u8 dim_init_isoc(struct dim_channel *ch, u8 is_tx, u16 ch_address, + u16 packet_length) +{ + if (!g.dim_is_initialized || !ch) + return DIM_ERR_DRIVER_NOT_INITIALIZED; + + if (!check_channel_address(ch_address)) + return DIM_INIT_ERR_CHANNEL_ADDRESS; + + if (!check_packet_length(packet_length)) + return DIM_ERR_BAD_CONFIG; + + ch->dbr_size = packet_length * ISOC_DBR_FACTOR; + ch->dbr_addr = alloc_dbr(ch->dbr_size); + if (ch->dbr_addr >= DBR_SIZE) + return DIM_INIT_ERR_OUT_OF_MEMORY; + + isoc_init(ch, ch_address / 2, packet_length); + + dim2_configure_channel(ch->addr, CAT_CT_VAL_ISOC, is_tx, ch->dbr_addr, + ch->dbr_size, packet_length); + + return DIM_NO_ERROR; +} + +u8 dim_init_sync(struct dim_channel *ch, u8 is_tx, u16 ch_address, + u16 bytes_per_frame) +{ + u16 bd_factor = g.fcnt + 2; + + if (!g.dim_is_initialized || !ch) + return DIM_ERR_DRIVER_NOT_INITIALIZED; + + if (!check_channel_address(ch_address)) + return DIM_INIT_ERR_CHANNEL_ADDRESS; + + if (!check_bytes_per_frame(bytes_per_frame)) + return DIM_ERR_BAD_CONFIG; + + ch->dbr_size = bytes_per_frame << bd_factor; + ch->dbr_addr = alloc_dbr(ch->dbr_size); + if (ch->dbr_addr >= DBR_SIZE) + return DIM_INIT_ERR_OUT_OF_MEMORY; + + sync_init(ch, ch_address / 2, bytes_per_frame); + + dim2_clear_dbr(ch->dbr_addr, ch->dbr_size); + dim2_configure_channel(ch->addr, CAT_CT_VAL_SYNC, is_tx, + ch->dbr_addr, ch->dbr_size, 0); + + return DIM_NO_ERROR; +} + +u8 dim_destroy_channel(struct dim_channel *ch) +{ + if (!g.dim_is_initialized || !ch) + return DIM_ERR_DRIVER_NOT_INITIALIZED; + + if (ch->addr == g.atx_dbr.ch_addr) { + dimcb_io_write(&g.dim2->MIEN, 0); + g.atx_dbr.ch_addr = 0; + } + + dim2_clear_channel(ch->addr); + if (ch->dbr_addr < DBR_SIZE) + free_dbr(ch->dbr_addr, ch->dbr_size); + ch->dbr_addr = DBR_SIZE; + + return DIM_NO_ERROR; +} + +void dim_service_ahb_int_irq(struct dim_channel *const *channels) +{ + bool state_changed; + + if (!g.dim_is_initialized) { + dim_on_error(DIM_ERR_DRIVER_NOT_INITIALIZED, + "DIM is not initialized"); + return; + } + + if (!channels) { + dim_on_error(DIM_ERR_DRIVER_NOT_INITIALIZED, "Bad channels"); + return; + } + + /* + * Use while-loop and a flag to make sure the age is changed back at + * least once, otherwise the interrupt may never come if CPU generates + * interrupt on changing age. + * This cycle runs not more than number of channels, because + * channel_service_interrupt() routine doesn't start the channel again. + */ + do { + struct dim_channel *const *ch = channels; + + state_changed = false; + + while (*ch) { + state_changed |= channel_service_interrupt(*ch); + ++ch; + } + } while (state_changed); +} + +u8 dim_service_channel(struct dim_channel *ch) +{ + if (!g.dim_is_initialized || !ch) + return DIM_ERR_DRIVER_NOT_INITIALIZED; + + return channel_service(ch); +} + +struct dim_ch_state_t *dim_get_channel_state(struct dim_channel *ch, + struct dim_ch_state_t *state_ptr) +{ + if (!ch || !state_ptr) + return NULL; + + state_ptr->ready = ch->state.level < 2; + state_ptr->done_buffers = ch->done_sw_buffers_number; + + return state_ptr; +} + +bool dim_enqueue_buffer(struct dim_channel *ch, u32 buffer_addr, + u16 buffer_size) +{ + if (!ch) + return dim_on_error(DIM_ERR_DRIVER_NOT_INITIALIZED, + "Bad channel"); + + return channel_start(ch, buffer_addr, buffer_size); +} + +bool dim_detach_buffers(struct dim_channel *ch, u16 buffers_number) +{ + if (!ch) + return dim_on_error(DIM_ERR_DRIVER_NOT_INITIALIZED, + "Bad channel"); + + return channel_detach_buffers(ch, buffers_number); +} diff --git a/drivers/staging/most/dim2/hal.h b/drivers/staging/most/dim2/hal.h new file mode 100644 index 000000000000..e04a5350f134 --- /dev/null +++ b/drivers/staging/most/dim2/hal.h @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * hal.h - DIM2 HAL interface + * (MediaLB, Device Interface Macro IP, OS62420) + * + * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG + */ + +#ifndef _DIM2_HAL_H +#define _DIM2_HAL_H + +#include +#include "reg.h" + +/* + * The values below are specified in the hardware specification. + * So, they should not be changed until the hardware specification changes. + */ +enum mlb_clk_speed { + CLK_256FS = 0, + CLK_512FS = 1, + CLK_1024FS = 2, + CLK_2048FS = 3, + CLK_3072FS = 4, + CLK_4096FS = 5, + CLK_6144FS = 6, + CLK_8192FS = 7, +}; + +struct dim_ch_state_t { + bool ready; /* Shows readiness to enqueue next buffer */ + u16 done_buffers; /* Number of completed buffers */ +}; + +struct int_ch_state { + /* changed only in interrupt context */ + volatile int request_counter; + + /* changed only in task context */ + volatile int service_counter; + + u8 idx1; + u8 idx2; + u8 level; /* [0..2], buffering level */ +}; + +struct dim_channel { + struct int_ch_state state; + u8 addr; + u16 dbr_addr; + u16 dbr_size; + u16 packet_length; /*< Isochronous packet length in bytes. */ + u16 bytes_per_frame; /*< Synchronous bytes per frame. */ + u16 done_sw_buffers_number; /*< Done software buffers number. */ +}; + +u8 dim_startup(struct dim2_regs __iomem *dim_base_address, u32 mlb_clock, + u32 fcnt); + +void dim_shutdown(void); + +bool dim_get_lock_state(void); + +u16 dim_norm_ctrl_async_buffer_size(u16 buf_size); + +u16 dim_norm_isoc_buffer_size(u16 buf_size, u16 packet_length); + +u16 dim_norm_sync_buffer_size(u16 buf_size, u16 bytes_per_frame); + +u8 dim_init_control(struct dim_channel *ch, u8 is_tx, u16 ch_address, + u16 max_buffer_size); + +u8 dim_init_async(struct dim_channel *ch, u8 is_tx, u16 ch_address, + u16 max_buffer_size); + +u8 dim_init_isoc(struct dim_channel *ch, u8 is_tx, u16 ch_address, + u16 packet_length); + +u8 dim_init_sync(struct dim_channel *ch, u8 is_tx, u16 ch_address, + u16 bytes_per_frame); + +u8 dim_destroy_channel(struct dim_channel *ch); + +void dim_service_mlb_int_irq(void); + +void dim_service_ahb_int_irq(struct dim_channel *const *channels); + +u8 dim_service_channel(struct dim_channel *ch); + +struct dim_ch_state_t *dim_get_channel_state(struct dim_channel *ch, + struct dim_ch_state_t *state_ptr); + +u16 dim_dbr_space(struct dim_channel *ch); + +bool dim_enqueue_buffer(struct dim_channel *ch, u32 buffer_addr, + u16 buffer_size); + +bool dim_detach_buffers(struct dim_channel *ch, u16 buffers_number); + +u32 dimcb_io_read(u32 __iomem *ptr32); + +void dimcb_io_write(u32 __iomem *ptr32, u32 value); + +void dimcb_on_error(u8 error_id, const char *error_message); + +#endif /* _DIM2_HAL_H */ diff --git a/drivers/staging/most/dim2/reg.h b/drivers/staging/most/dim2/reg.h new file mode 100644 index 000000000000..69cbf78239f1 --- /dev/null +++ b/drivers/staging/most/dim2/reg.h @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * reg.h - Definitions for registers of DIM2 + * (MediaLB, Device Interface Macro IP, OS62420) + * + * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG + */ + +#ifndef DIM2_OS62420_H +#define DIM2_OS62420_H + +#include + +struct dim2_regs { + /* 0x00 */ u32 MLBC0; + /* 0x01 */ u32 rsvd0[1]; + /* 0x02 */ u32 MLBPC0; + /* 0x03 */ u32 MS0; + /* 0x04 */ u32 rsvd1[1]; + /* 0x05 */ u32 MS1; + /* 0x06 */ u32 rsvd2[2]; + /* 0x08 */ u32 MSS; + /* 0x09 */ u32 MSD; + /* 0x0A */ u32 rsvd3[1]; + /* 0x0B */ u32 MIEN; + /* 0x0C */ u32 rsvd4[1]; + /* 0x0D */ u32 MLBPC2; + /* 0x0E */ u32 MLBPC1; + /* 0x0F */ u32 MLBC1; + /* 0x10 */ u32 rsvd5[0x10]; + /* 0x20 */ u32 HCTL; + /* 0x21 */ u32 rsvd6[1]; + /* 0x22 */ u32 HCMR0; + /* 0x23 */ u32 HCMR1; + /* 0x24 */ u32 HCER0; + /* 0x25 */ u32 HCER1; + /* 0x26 */ u32 HCBR0; + /* 0x27 */ u32 HCBR1; + /* 0x28 */ u32 rsvd7[8]; + /* 0x30 */ u32 MDAT0; + /* 0x31 */ u32 MDAT1; + /* 0x32 */ u32 MDAT2; + /* 0x33 */ u32 MDAT3; + /* 0x34 */ u32 MDWE0; + /* 0x35 */ u32 MDWE1; + /* 0x36 */ u32 MDWE2; + /* 0x37 */ u32 MDWE3; + /* 0x38 */ u32 MCTL; + /* 0x39 */ u32 MADR; + /* 0x3A */ u32 rsvd8[0xB6]; + /* 0xF0 */ u32 ACTL; + /* 0xF1 */ u32 rsvd9[3]; + /* 0xF4 */ u32 ACSR0; + /* 0xF5 */ u32 ACSR1; + /* 0xF6 */ u32 ACMR0; + /* 0xF7 */ u32 ACMR1; +}; + +#define DIM2_MASK(n) (~((~(u32)0) << (n))) + +enum { + MLBC0_MLBLK_BIT = 7, + + MLBC0_MLBPEN_BIT = 5, + + MLBC0_MLBCLK_SHIFT = 2, + MLBC0_MLBCLK_VAL_256FS = 0, + MLBC0_MLBCLK_VAL_512FS = 1, + MLBC0_MLBCLK_VAL_1024FS = 2, + MLBC0_MLBCLK_VAL_2048FS = 3, + + MLBC0_FCNT_SHIFT = 15, + MLBC0_FCNT_MASK = 7, + MLBC0_FCNT_MAX_VAL = 6, + + MLBC0_MLBEN_BIT = 0, + + MIEN_CTX_BREAK_BIT = 29, + MIEN_CTX_PE_BIT = 28, + MIEN_CTX_DONE_BIT = 27, + + MIEN_CRX_BREAK_BIT = 26, + MIEN_CRX_PE_BIT = 25, + MIEN_CRX_DONE_BIT = 24, + + MIEN_ATX_BREAK_BIT = 22, + MIEN_ATX_PE_BIT = 21, + MIEN_ATX_DONE_BIT = 20, + + MIEN_ARX_BREAK_BIT = 19, + MIEN_ARX_PE_BIT = 18, + MIEN_ARX_DONE_BIT = 17, + + MIEN_SYNC_PE_BIT = 16, + + MIEN_ISOC_BUFO_BIT = 1, + MIEN_ISOC_PE_BIT = 0, + + MLBC1_NDA_SHIFT = 8, + MLBC1_NDA_MASK = 0xFF, + + MLBC1_CLKMERR_BIT = 7, + MLBC1_LOCKERR_BIT = 6, + + ACTL_DMA_MODE_BIT = 2, + ACTL_DMA_MODE_VAL_DMA_MODE_0 = 0, + ACTL_DMA_MODE_VAL_DMA_MODE_1 = 1, + ACTL_SCE_BIT = 0, + + HCTL_EN_BIT = 15 +}; + +enum { + CDT0_RPC_SHIFT = 16 + 11, + CDT0_RPC_MASK = DIM2_MASK(5), + + CDT1_BS_ISOC_SHIFT = 0, + CDT1_BS_ISOC_MASK = DIM2_MASK(9), + + CDT3_BD_SHIFT = 0, + CDT3_BD_MASK = DIM2_MASK(12), + CDT3_BD_ISOC_MASK = DIM2_MASK(13), + CDT3_BA_SHIFT = 16, + + ADT0_CE_BIT = 15, + ADT0_LE_BIT = 14, + ADT0_PG_BIT = 13, + + ADT1_RDY_BIT = 15, + ADT1_DNE_BIT = 14, + ADT1_ERR_BIT = 13, + ADT1_PS_BIT = 12, + ADT1_MEP_BIT = 11, + ADT1_BD_SHIFT = 0, + ADT1_CTRL_ASYNC_BD_MASK = DIM2_MASK(11), + ADT1_ISOC_SYNC_BD_MASK = DIM2_MASK(13), + + CAT_FCE_BIT = 14, + CAT_MFE_BIT = 14, + + CAT_MT_BIT = 13, + + CAT_RNW_BIT = 12, + + CAT_CE_BIT = 11, + + CAT_CT_SHIFT = 8, + CAT_CT_VAL_SYNC = 0, + CAT_CT_VAL_CONTROL = 1, + CAT_CT_VAL_ASYNC = 2, + CAT_CT_VAL_ISOC = 3, + + CAT_CL_SHIFT = 0, + CAT_CL_MASK = DIM2_MASK(6) +}; + +#endif /* DIM2_OS62420_H */ diff --git a/drivers/staging/most/dim2/sysfs.c b/drivers/staging/most/dim2/sysfs.c new file mode 100644 index 000000000000..ec1f4cecf9e7 --- /dev/null +++ b/drivers/staging/most/dim2/sysfs.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * sysfs.c - MediaLB sysfs information + * + * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG + */ + +/* Author: Andrey Shvetsov */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include "sysfs.h" + +struct bus_attr { + struct attribute attr; + ssize_t (*show)(struct medialb_bus *bus, char *buf); + ssize_t (*store)(struct medialb_bus *bus, const char *buf, + size_t count); +}; + +static ssize_t state_show(struct medialb_bus *bus, char *buf) +{ + bool state = dim2_sysfs_get_state_cb(); + + return sprintf(buf, "%s\n", state ? "locked" : ""); +} + +static struct bus_attr state_attr = __ATTR_RO(state); + +static struct attribute *bus_default_attrs[] = { + &state_attr.attr, + NULL, +}; + +static const struct attribute_group bus_attr_group = { + .attrs = bus_default_attrs, +}; + +static void bus_kobj_release(struct kobject *kobj) +{ +} + +static ssize_t bus_kobj_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct medialb_bus *bus = + container_of(kobj, struct medialb_bus, kobj_group); + struct bus_attr *xattr = container_of(attr, struct bus_attr, attr); + + if (!xattr->show) + return -EIO; + + return xattr->show(bus, buf); +} + +static ssize_t bus_kobj_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + struct medialb_bus *bus = + container_of(kobj, struct medialb_bus, kobj_group); + struct bus_attr *xattr = container_of(attr, struct bus_attr, attr); + + if (!xattr->store) + return -EIO; + + return xattr->store(bus, buf, count); +} + +static struct sysfs_ops const bus_kobj_sysfs_ops = { + .show = bus_kobj_attr_show, + .store = bus_kobj_attr_store, +}; + +static struct kobj_type bus_ktype = { + .release = bus_kobj_release, + .sysfs_ops = &bus_kobj_sysfs_ops, +}; + +int dim2_sysfs_probe(struct medialb_bus *bus, struct kobject *parent_kobj) +{ + int err; + + kobject_init(&bus->kobj_group, &bus_ktype); + err = kobject_add(&bus->kobj_group, parent_kobj, "bus"); + if (err) { + pr_err("kobject_add() failed: %d\n", err); + goto err_kobject_add; + } + + err = sysfs_create_group(&bus->kobj_group, &bus_attr_group); + if (err) { + pr_err("sysfs_create_group() failed: %d\n", err); + goto err_create_group; + } + + return 0; + +err_create_group: + kobject_put(&bus->kobj_group); + +err_kobject_add: + return err; +} + +void dim2_sysfs_destroy(struct medialb_bus *bus) +{ + kobject_put(&bus->kobj_group); +} diff --git a/drivers/staging/most/dim2/sysfs.h b/drivers/staging/most/dim2/sysfs.h new file mode 100644 index 000000000000..a33ebd8b45f5 --- /dev/null +++ b/drivers/staging/most/dim2/sysfs.h @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * sysfs.h - MediaLB sysfs information + * + * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG + */ + +/* Author: Andrey Shvetsov */ + +#ifndef DIM2_SYSFS_H +#define DIM2_SYSFS_H + +#include + +struct medialb_bus { + struct kobject kobj_group; +}; + +struct dim2_hdm; + +int dim2_sysfs_probe(struct medialb_bus *bus, struct kobject *parent_kobj); +void dim2_sysfs_destroy(struct medialb_bus *bus); + +/* + * callback, + * must deliver MediaLB state as true if locked or false if unlocked + */ +bool dim2_sysfs_get_state_cb(void); + +#endif /* DIM2_SYSFS_H */ diff --git a/drivers/staging/most/hdm-dim2/Kconfig b/drivers/staging/most/hdm-dim2/Kconfig deleted file mode 100644 index 663bfebff674..000000000000 --- a/drivers/staging/most/hdm-dim2/Kconfig +++ /dev/null @@ -1,16 +0,0 @@ -# -# MediaLB configuration -# - -config HDM_DIM2 - tristate "DIM2 HDM" - depends on HAS_IOMEM - - ---help--- - Say Y here if you want to connect via MediaLB to network transceiver. - This device driver is platform dependent and needs an additional - platform driver to be installed. For more information contact - maintainer of this driver. - - To compile this driver as a module, choose M here: the - module will be called hdm_dim2. diff --git a/drivers/staging/most/hdm-dim2/dim2_errors.h b/drivers/staging/most/hdm-dim2/dim2_errors.h deleted file mode 100644 index 8b90196076d5..000000000000 --- a/drivers/staging/most/hdm-dim2/dim2_errors.h +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * dim2_errors.h - Definitions of errors for DIM2 HAL API - * (MediaLB, Device Interface Macro IP, OS62420) - * - * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG - */ - -#ifndef _MOST_DIM_ERRORS_H -#define _MOST_DIM_ERRORS_H - -/** - * MOST DIM errors. - */ -enum dim_errors_t { - /** Not an error */ - DIM_NO_ERROR = 0, - - /** Bad base address for DIM2 IP */ - DIM_INIT_ERR_DIM_ADDR = 0x10, - - /**< Bad MediaLB clock */ - DIM_INIT_ERR_MLB_CLOCK, - - /** Bad channel address */ - DIM_INIT_ERR_CHANNEL_ADDRESS, - - /** Out of DBR memory */ - DIM_INIT_ERR_OUT_OF_MEMORY, - - /** DIM API is called while DIM is not initialized successfully */ - DIM_ERR_DRIVER_NOT_INITIALIZED = 0x20, - - /** - * Configuration does not respect hardware limitations - * for isochronous or synchronous channels - */ - DIM_ERR_BAD_CONFIG, - - /** - * Buffer size does not respect hardware limitations - * for isochronous or synchronous channels - */ - DIM_ERR_BAD_BUFFER_SIZE, - - DIM_ERR_UNDERFLOW, - - DIM_ERR_OVERFLOW, -}; - -#endif /* _MOST_DIM_ERRORS_H */ diff --git a/drivers/staging/most/hdm-dim2/dim2_hal.c b/drivers/staging/most/hdm-dim2/dim2_hal.c deleted file mode 100644 index f98ac935729c..000000000000 --- a/drivers/staging/most/hdm-dim2/dim2_hal.c +++ /dev/null @@ -1,979 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * dim2_hal.c - DIM2 HAL implementation - * (MediaLB, Device Interface Macro IP, OS62420) - * - * Copyright (C) 2015-2016, Microchip Technology Germany II GmbH & Co. KG - */ - -/* Author: Andrey Shvetsov */ - -#include "dim2_hal.h" -#include "dim2_errors.h" -#include "dim2_reg.h" -#include -#include - -/* - * Size factor for isochronous DBR buffer. - * Minimal value is 3. - */ -#define ISOC_DBR_FACTOR 3u - -/* - * Number of 32-bit units for DBR map. - * - * 1: block size is 512, max allocation is 16K - * 2: block size is 256, max allocation is 8K - * 4: block size is 128, max allocation is 4K - * 8: block size is 64, max allocation is 2K - * - * Min allocated space is block size. - * Max possible allocated space is 32 blocks. - */ -#define DBR_MAP_SIZE 2 - -/* -------------------------------------------------------------------------- */ -/* not configurable area */ - -#define CDT 0x00 -#define ADT 0x40 -#define MLB_CAT 0x80 -#define AHB_CAT 0x88 - -#define DBR_SIZE (16 * 1024) /* specified by IP */ -#define DBR_BLOCK_SIZE (DBR_SIZE / 32 / DBR_MAP_SIZE) - -#define ROUND_UP_TO(x, d) (DIV_ROUND_UP(x, (d)) * (d)) - -/* -------------------------------------------------------------------------- */ -/* generic helper functions and macros */ - -static inline u32 bit_mask(u8 position) -{ - return (u32)1 << position; -} - -static inline bool dim_on_error(u8 error_id, const char *error_message) -{ - dimcb_on_error(error_id, error_message); - return false; -} - -/* -------------------------------------------------------------------------- */ -/* types and local variables */ - -struct async_tx_dbr { - u8 ch_addr; - u16 rpc; - u16 wpc; - u16 rest_size; - u16 sz_queue[CDT0_RPC_MASK + 1]; -}; - -struct lld_global_vars_t { - bool dim_is_initialized; - bool mcm_is_initialized; - struct dim2_regs __iomem *dim2; /* DIM2 core base address */ - struct async_tx_dbr atx_dbr; - u32 fcnt; - u32 dbr_map[DBR_MAP_SIZE]; -}; - -static struct lld_global_vars_t g = { false }; - -/* -------------------------------------------------------------------------- */ - -static int dbr_get_mask_size(u16 size) -{ - int i; - - for (i = 0; i < 6; i++) - if (size <= (DBR_BLOCK_SIZE << i)) - return 1 << i; - return 0; -} - -/** - * Allocates DBR memory. - * @param size Allocating memory size. - * @return Offset in DBR memory by success or DBR_SIZE if out of memory. - */ -static int alloc_dbr(u16 size) -{ - int mask_size; - int i, block_idx = 0; - - if (size <= 0) - return DBR_SIZE; /* out of memory */ - - mask_size = dbr_get_mask_size(size); - if (mask_size == 0) - return DBR_SIZE; /* out of memory */ - - for (i = 0; i < DBR_MAP_SIZE; i++) { - u32 const blocks = DIV_ROUND_UP(size, DBR_BLOCK_SIZE); - u32 mask = ~((~(u32)0) << blocks); - - do { - if ((g.dbr_map[i] & mask) == 0) { - g.dbr_map[i] |= mask; - return block_idx * DBR_BLOCK_SIZE; - } - block_idx += mask_size; - /* do shift left with 2 steps in case mask_size == 32 */ - mask <<= mask_size - 1; - } while ((mask <<= 1) != 0); - } - - return DBR_SIZE; /* out of memory */ -} - -static void free_dbr(int offs, int size) -{ - int block_idx = offs / DBR_BLOCK_SIZE; - u32 const blocks = DIV_ROUND_UP(size, DBR_BLOCK_SIZE); - u32 mask = ~((~(u32)0) << blocks); - - mask <<= block_idx % 32; - g.dbr_map[block_idx / 32] &= ~mask; -} - -/* -------------------------------------------------------------------------- */ - -static void dim2_transfer_madr(u32 val) -{ - dimcb_io_write(&g.dim2->MADR, val); - - /* wait for transfer completion */ - while ((dimcb_io_read(&g.dim2->MCTL) & 1) != 1) - continue; - - dimcb_io_write(&g.dim2->MCTL, 0); /* clear transfer complete */ -} - -static void dim2_clear_dbr(u16 addr, u16 size) -{ - enum { MADR_TB_BIT = 30, MADR_WNR_BIT = 31 }; - - u16 const end_addr = addr + size; - u32 const cmd = bit_mask(MADR_WNR_BIT) | bit_mask(MADR_TB_BIT); - - dimcb_io_write(&g.dim2->MCTL, 0); /* clear transfer complete */ - dimcb_io_write(&g.dim2->MDAT0, 0); - - for (; addr < end_addr; addr++) - dim2_transfer_madr(cmd | addr); -} - -static u32 dim2_read_ctr(u32 ctr_addr, u16 mdat_idx) -{ - dim2_transfer_madr(ctr_addr); - - return dimcb_io_read((&g.dim2->MDAT0) + mdat_idx); -} - -static void dim2_write_ctr_mask(u32 ctr_addr, const u32 *mask, const u32 *value) -{ - enum { MADR_WNR_BIT = 31 }; - - dimcb_io_write(&g.dim2->MCTL, 0); /* clear transfer complete */ - - if (mask[0] != 0) - dimcb_io_write(&g.dim2->MDAT0, value[0]); - if (mask[1] != 0) - dimcb_io_write(&g.dim2->MDAT1, value[1]); - if (mask[2] != 0) - dimcb_io_write(&g.dim2->MDAT2, value[2]); - if (mask[3] != 0) - dimcb_io_write(&g.dim2->MDAT3, value[3]); - - dimcb_io_write(&g.dim2->MDWE0, mask[0]); - dimcb_io_write(&g.dim2->MDWE1, mask[1]); - dimcb_io_write(&g.dim2->MDWE2, mask[2]); - dimcb_io_write(&g.dim2->MDWE3, mask[3]); - - dim2_transfer_madr(bit_mask(MADR_WNR_BIT) | ctr_addr); -} - -static inline void dim2_write_ctr(u32 ctr_addr, const u32 *value) -{ - u32 const mask[4] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; - - dim2_write_ctr_mask(ctr_addr, mask, value); -} - -static inline void dim2_clear_ctr(u32 ctr_addr) -{ - u32 const value[4] = { 0, 0, 0, 0 }; - - dim2_write_ctr(ctr_addr, value); -} - -static void dim2_configure_cat(u8 cat_base, u8 ch_addr, u8 ch_type, - bool read_not_write) -{ - bool isoc_fce = ch_type == CAT_CT_VAL_ISOC; - bool sync_mfe = ch_type == CAT_CT_VAL_SYNC; - u16 const cat = - (read_not_write << CAT_RNW_BIT) | - (ch_type << CAT_CT_SHIFT) | - (ch_addr << CAT_CL_SHIFT) | - (isoc_fce << CAT_FCE_BIT) | - (sync_mfe << CAT_MFE_BIT) | - (false << CAT_MT_BIT) | - (true << CAT_CE_BIT); - u8 const ctr_addr = cat_base + ch_addr / 8; - u8 const idx = (ch_addr % 8) / 2; - u8 const shift = (ch_addr % 2) * 16; - u32 mask[4] = { 0, 0, 0, 0 }; - u32 value[4] = { 0, 0, 0, 0 }; - - mask[idx] = (u32)0xFFFF << shift; - value[idx] = cat << shift; - dim2_write_ctr_mask(ctr_addr, mask, value); -} - -static void dim2_clear_cat(u8 cat_base, u8 ch_addr) -{ - u8 const ctr_addr = cat_base + ch_addr / 8; - u8 const idx = (ch_addr % 8) / 2; - u8 const shift = (ch_addr % 2) * 16; - u32 mask[4] = { 0, 0, 0, 0 }; - u32 value[4] = { 0, 0, 0, 0 }; - - mask[idx] = (u32)0xFFFF << shift; - dim2_write_ctr_mask(ctr_addr, mask, value); -} - -static void dim2_configure_cdt(u8 ch_addr, u16 dbr_address, u16 hw_buffer_size, - u16 packet_length) -{ - u32 cdt[4] = { 0, 0, 0, 0 }; - - if (packet_length) - cdt[1] = ((packet_length - 1) << CDT1_BS_ISOC_SHIFT); - - cdt[3] = - ((hw_buffer_size - 1) << CDT3_BD_SHIFT) | - (dbr_address << CDT3_BA_SHIFT); - dim2_write_ctr(CDT + ch_addr, cdt); -} - -static u16 dim2_rpc(u8 ch_addr) -{ - u32 cdt0 = dim2_read_ctr(CDT + ch_addr, 0); - - return (cdt0 >> CDT0_RPC_SHIFT) & CDT0_RPC_MASK; -} - -static void dim2_clear_cdt(u8 ch_addr) -{ - u32 cdt[4] = { 0, 0, 0, 0 }; - - dim2_write_ctr(CDT + ch_addr, cdt); -} - -static void dim2_configure_adt(u8 ch_addr) -{ - u32 adt[4] = { 0, 0, 0, 0 }; - - adt[0] = - (true << ADT0_CE_BIT) | - (true << ADT0_LE_BIT) | - (0 << ADT0_PG_BIT); - - dim2_write_ctr(ADT + ch_addr, adt); -} - -static void dim2_clear_adt(u8 ch_addr) -{ - u32 adt[4] = { 0, 0, 0, 0 }; - - dim2_write_ctr(ADT + ch_addr, adt); -} - -static void dim2_start_ctrl_async(u8 ch_addr, u8 idx, u32 buf_addr, - u16 buffer_size) -{ - u8 const shift = idx * 16; - - u32 mask[4] = { 0, 0, 0, 0 }; - u32 adt[4] = { 0, 0, 0, 0 }; - - mask[1] = - bit_mask(ADT1_PS_BIT + shift) | - bit_mask(ADT1_RDY_BIT + shift) | - (ADT1_CTRL_ASYNC_BD_MASK << (ADT1_BD_SHIFT + shift)); - adt[1] = - (true << (ADT1_PS_BIT + shift)) | - (true << (ADT1_RDY_BIT + shift)) | - ((buffer_size - 1) << (ADT1_BD_SHIFT + shift)); - - mask[idx + 2] = 0xFFFFFFFF; - adt[idx + 2] = buf_addr; - - dim2_write_ctr_mask(ADT + ch_addr, mask, adt); -} - -static void dim2_start_isoc_sync(u8 ch_addr, u8 idx, u32 buf_addr, - u16 buffer_size) -{ - u8 const shift = idx * 16; - - u32 mask[4] = { 0, 0, 0, 0 }; - u32 adt[4] = { 0, 0, 0, 0 }; - - mask[1] = - bit_mask(ADT1_RDY_BIT + shift) | - (ADT1_ISOC_SYNC_BD_MASK << (ADT1_BD_SHIFT + shift)); - adt[1] = - (true << (ADT1_RDY_BIT + shift)) | - ((buffer_size - 1) << (ADT1_BD_SHIFT + shift)); - - mask[idx + 2] = 0xFFFFFFFF; - adt[idx + 2] = buf_addr; - - dim2_write_ctr_mask(ADT + ch_addr, mask, adt); -} - -static void dim2_clear_ctram(void) -{ - u32 ctr_addr; - - for (ctr_addr = 0; ctr_addr < 0x90; ctr_addr++) - dim2_clear_ctr(ctr_addr); -} - -static void dim2_configure_channel( - u8 ch_addr, u8 type, u8 is_tx, u16 dbr_address, u16 hw_buffer_size, - u16 packet_length) -{ - dim2_configure_cdt(ch_addr, dbr_address, hw_buffer_size, packet_length); - dim2_configure_cat(MLB_CAT, ch_addr, type, is_tx ? 1 : 0); - - dim2_configure_adt(ch_addr); - dim2_configure_cat(AHB_CAT, ch_addr, type, is_tx ? 0 : 1); - - /* unmask interrupt for used channel, enable mlb_sys_int[0] interrupt */ - dimcb_io_write(&g.dim2->ACMR0, - dimcb_io_read(&g.dim2->ACMR0) | bit_mask(ch_addr)); -} - -static void dim2_clear_channel(u8 ch_addr) -{ - /* mask interrupt for used channel, disable mlb_sys_int[0] interrupt */ - dimcb_io_write(&g.dim2->ACMR0, - dimcb_io_read(&g.dim2->ACMR0) & ~bit_mask(ch_addr)); - - dim2_clear_cat(AHB_CAT, ch_addr); - dim2_clear_adt(ch_addr); - - dim2_clear_cat(MLB_CAT, ch_addr); - dim2_clear_cdt(ch_addr); - - /* clear channel status bit */ - dimcb_io_write(&g.dim2->ACSR0, bit_mask(ch_addr)); -} - -/* -------------------------------------------------------------------------- */ -/* trace async tx dbr fill state */ - -static inline u16 norm_pc(u16 pc) -{ - return pc & CDT0_RPC_MASK; -} - -static void dbrcnt_init(u8 ch_addr, u16 dbr_size) -{ - g.atx_dbr.rest_size = dbr_size; - g.atx_dbr.rpc = dim2_rpc(ch_addr); - g.atx_dbr.wpc = g.atx_dbr.rpc; -} - -static void dbrcnt_enq(int buf_sz) -{ - g.atx_dbr.rest_size -= buf_sz; - g.atx_dbr.sz_queue[norm_pc(g.atx_dbr.wpc)] = buf_sz; - g.atx_dbr.wpc++; -} - -u16 dim_dbr_space(struct dim_channel *ch) -{ - u16 cur_rpc; - struct async_tx_dbr *dbr = &g.atx_dbr; - - if (ch->addr != dbr->ch_addr) - return 0xFFFF; - - cur_rpc = dim2_rpc(ch->addr); - - while (norm_pc(dbr->rpc) != cur_rpc) { - dbr->rest_size += dbr->sz_queue[norm_pc(dbr->rpc)]; - dbr->rpc++; - } - - if ((u16)(dbr->wpc - dbr->rpc) >= CDT0_RPC_MASK) - return 0; - - return dbr->rest_size; -} - -/* -------------------------------------------------------------------------- */ -/* channel state helpers */ - -static void state_init(struct int_ch_state *state) -{ - state->request_counter = 0; - state->service_counter = 0; - - state->idx1 = 0; - state->idx2 = 0; - state->level = 0; -} - -/* -------------------------------------------------------------------------- */ -/* macro helper functions */ - -static inline bool check_channel_address(u32 ch_address) -{ - return ch_address > 0 && (ch_address % 2) == 0 && - (ch_address / 2) <= (u32)CAT_CL_MASK; -} - -static inline bool check_packet_length(u32 packet_length) -{ - u16 const max_size = ((u16)CDT3_BD_ISOC_MASK + 1u) / ISOC_DBR_FACTOR; - - if (packet_length <= 0) - return false; /* too small */ - - if (packet_length > max_size) - return false; /* too big */ - - if (packet_length - 1u > (u32)CDT1_BS_ISOC_MASK) - return false; /* too big */ - - return true; -} - -static inline bool check_bytes_per_frame(u32 bytes_per_frame) -{ - u16 const bd_factor = g.fcnt + 2; - u16 const max_size = ((u16)CDT3_BD_MASK + 1u) >> bd_factor; - - if (bytes_per_frame <= 0) - return false; /* too small */ - - if (bytes_per_frame > max_size) - return false; /* too big */ - - return true; -} - -static inline u16 norm_ctrl_async_buffer_size(u16 buf_size) -{ - u16 const max_size = (u16)ADT1_CTRL_ASYNC_BD_MASK + 1u; - - if (buf_size > max_size) - return max_size; - - return buf_size; -} - -static inline u16 norm_isoc_buffer_size(u16 buf_size, u16 packet_length) -{ - u16 n; - u16 const max_size = (u16)ADT1_ISOC_SYNC_BD_MASK + 1u; - - if (buf_size > max_size) - buf_size = max_size; - - n = buf_size / packet_length; - - if (n < 2u) - return 0; /* too small buffer for given packet_length */ - - return packet_length * n; -} - -static inline u16 norm_sync_buffer_size(u16 buf_size, u16 bytes_per_frame) -{ - u16 n; - u16 const max_size = (u16)ADT1_ISOC_SYNC_BD_MASK + 1u; - u32 const unit = bytes_per_frame << g.fcnt; - - if (buf_size > max_size) - buf_size = max_size; - - n = buf_size / unit; - - if (n < 1u) - return 0; /* too small buffer for given bytes_per_frame */ - - return unit * n; -} - -static void dim2_cleanup(void) -{ - /* disable MediaLB */ - dimcb_io_write(&g.dim2->MLBC0, false << MLBC0_MLBEN_BIT); - - dim2_clear_ctram(); - - /* disable mlb_int interrupt */ - dimcb_io_write(&g.dim2->MIEN, 0); - - /* clear status for all dma channels */ - dimcb_io_write(&g.dim2->ACSR0, 0xFFFFFFFF); - dimcb_io_write(&g.dim2->ACSR1, 0xFFFFFFFF); - - /* mask interrupts for all channels */ - dimcb_io_write(&g.dim2->ACMR0, 0); - dimcb_io_write(&g.dim2->ACMR1, 0); -} - -static void dim2_initialize(bool enable_6pin, u8 mlb_clock) -{ - dim2_cleanup(); - - /* configure and enable MediaLB */ - dimcb_io_write(&g.dim2->MLBC0, - enable_6pin << MLBC0_MLBPEN_BIT | - mlb_clock << MLBC0_MLBCLK_SHIFT | - g.fcnt << MLBC0_FCNT_SHIFT | - true << MLBC0_MLBEN_BIT); - - /* activate all HBI channels */ - dimcb_io_write(&g.dim2->HCMR0, 0xFFFFFFFF); - dimcb_io_write(&g.dim2->HCMR1, 0xFFFFFFFF); - - /* enable HBI */ - dimcb_io_write(&g.dim2->HCTL, bit_mask(HCTL_EN_BIT)); - - /* configure DMA */ - dimcb_io_write(&g.dim2->ACTL, - ACTL_DMA_MODE_VAL_DMA_MODE_1 << ACTL_DMA_MODE_BIT | - true << ACTL_SCE_BIT); -} - -static bool dim2_is_mlb_locked(void) -{ - u32 const mask0 = bit_mask(MLBC0_MLBLK_BIT); - u32 const mask1 = bit_mask(MLBC1_CLKMERR_BIT) | - bit_mask(MLBC1_LOCKERR_BIT); - u32 const c1 = dimcb_io_read(&g.dim2->MLBC1); - u32 const nda_mask = (u32)MLBC1_NDA_MASK << MLBC1_NDA_SHIFT; - - dimcb_io_write(&g.dim2->MLBC1, c1 & nda_mask); - return (dimcb_io_read(&g.dim2->MLBC1) & mask1) == 0 && - (dimcb_io_read(&g.dim2->MLBC0) & mask0) != 0; -} - -/* -------------------------------------------------------------------------- */ -/* channel help routines */ - -static inline bool service_channel(u8 ch_addr, u8 idx) -{ - u8 const shift = idx * 16; - u32 const adt1 = dim2_read_ctr(ADT + ch_addr, 1); - u32 mask[4] = { 0, 0, 0, 0 }; - u32 adt_w[4] = { 0, 0, 0, 0 }; - - if (((adt1 >> (ADT1_DNE_BIT + shift)) & 1) == 0) - return false; - - mask[1] = - bit_mask(ADT1_DNE_BIT + shift) | - bit_mask(ADT1_ERR_BIT + shift) | - bit_mask(ADT1_RDY_BIT + shift); - dim2_write_ctr_mask(ADT + ch_addr, mask, adt_w); - - /* clear channel status bit */ - dimcb_io_write(&g.dim2->ACSR0, bit_mask(ch_addr)); - - return true; -} - -/* -------------------------------------------------------------------------- */ -/* channel init routines */ - -static void isoc_init(struct dim_channel *ch, u8 ch_addr, u16 packet_length) -{ - state_init(&ch->state); - - ch->addr = ch_addr; - - ch->packet_length = packet_length; - ch->bytes_per_frame = 0; - ch->done_sw_buffers_number = 0; -} - -static void sync_init(struct dim_channel *ch, u8 ch_addr, u16 bytes_per_frame) -{ - state_init(&ch->state); - - ch->addr = ch_addr; - - ch->packet_length = 0; - ch->bytes_per_frame = bytes_per_frame; - ch->done_sw_buffers_number = 0; -} - -static void channel_init(struct dim_channel *ch, u8 ch_addr) -{ - state_init(&ch->state); - - ch->addr = ch_addr; - - ch->packet_length = 0; - ch->bytes_per_frame = 0; - ch->done_sw_buffers_number = 0; -} - -/* returns true if channel interrupt state is cleared */ -static bool channel_service_interrupt(struct dim_channel *ch) -{ - struct int_ch_state *const state = &ch->state; - - if (!service_channel(ch->addr, state->idx2)) - return false; - - state->idx2 ^= 1; - state->request_counter++; - return true; -} - -static bool channel_start(struct dim_channel *ch, u32 buf_addr, u16 buf_size) -{ - struct int_ch_state *const state = &ch->state; - - if (buf_size <= 0) - return dim_on_error(DIM_ERR_BAD_BUFFER_SIZE, "Bad buffer size"); - - if (ch->packet_length == 0 && ch->bytes_per_frame == 0 && - buf_size != norm_ctrl_async_buffer_size(buf_size)) - return dim_on_error(DIM_ERR_BAD_BUFFER_SIZE, - "Bad control/async buffer size"); - - if (ch->packet_length && - buf_size != norm_isoc_buffer_size(buf_size, ch->packet_length)) - return dim_on_error(DIM_ERR_BAD_BUFFER_SIZE, - "Bad isochronous buffer size"); - - if (ch->bytes_per_frame && - buf_size != norm_sync_buffer_size(buf_size, ch->bytes_per_frame)) - return dim_on_error(DIM_ERR_BAD_BUFFER_SIZE, - "Bad synchronous buffer size"); - - if (state->level >= 2u) - return dim_on_error(DIM_ERR_OVERFLOW, "Channel overflow"); - - ++state->level; - - if (ch->addr == g.atx_dbr.ch_addr) - dbrcnt_enq(buf_size); - - if (ch->packet_length || ch->bytes_per_frame) - dim2_start_isoc_sync(ch->addr, state->idx1, buf_addr, buf_size); - else - dim2_start_ctrl_async(ch->addr, state->idx1, buf_addr, - buf_size); - state->idx1 ^= 1; - - return true; -} - -static u8 channel_service(struct dim_channel *ch) -{ - struct int_ch_state *const state = &ch->state; - - if (state->service_counter != state->request_counter) { - state->service_counter++; - if (state->level == 0) - return DIM_ERR_UNDERFLOW; - - --state->level; - ch->done_sw_buffers_number++; - } - - return DIM_NO_ERROR; -} - -static bool channel_detach_buffers(struct dim_channel *ch, u16 buffers_number) -{ - if (buffers_number > ch->done_sw_buffers_number) - return dim_on_error(DIM_ERR_UNDERFLOW, "Channel underflow"); - - ch->done_sw_buffers_number -= buffers_number; - return true; -} - -/* -------------------------------------------------------------------------- */ -/* API */ - -u8 dim_startup(struct dim2_regs __iomem *dim_base_address, u32 mlb_clock, - u32 fcnt) -{ - g.dim_is_initialized = false; - - if (!dim_base_address) - return DIM_INIT_ERR_DIM_ADDR; - - /* MediaLB clock: 0 - 256 fs, 1 - 512 fs, 2 - 1024 fs, 3 - 2048 fs */ - /* MediaLB clock: 4 - 3072 fs, 5 - 4096 fs, 6 - 6144 fs, 7 - 8192 fs */ - if (mlb_clock >= 8) - return DIM_INIT_ERR_MLB_CLOCK; - - if (fcnt > MLBC0_FCNT_MAX_VAL) - return DIM_INIT_ERR_MLB_CLOCK; - - g.dim2 = dim_base_address; - g.fcnt = fcnt; - g.dbr_map[0] = 0; - g.dbr_map[1] = 0; - - dim2_initialize(mlb_clock >= 3, mlb_clock); - - g.dim_is_initialized = true; - - return DIM_NO_ERROR; -} - -void dim_shutdown(void) -{ - g.dim_is_initialized = false; - dim2_cleanup(); -} - -bool dim_get_lock_state(void) -{ - return dim2_is_mlb_locked(); -} - -static u8 init_ctrl_async(struct dim_channel *ch, u8 type, u8 is_tx, - u16 ch_address, u16 hw_buffer_size) -{ - if (!g.dim_is_initialized || !ch) - return DIM_ERR_DRIVER_NOT_INITIALIZED; - - if (!check_channel_address(ch_address)) - return DIM_INIT_ERR_CHANNEL_ADDRESS; - - ch->dbr_size = ROUND_UP_TO(hw_buffer_size, DBR_BLOCK_SIZE); - ch->dbr_addr = alloc_dbr(ch->dbr_size); - if (ch->dbr_addr >= DBR_SIZE) - return DIM_INIT_ERR_OUT_OF_MEMORY; - - channel_init(ch, ch_address / 2); - - dim2_configure_channel(ch->addr, type, is_tx, - ch->dbr_addr, ch->dbr_size, 0); - - return DIM_NO_ERROR; -} - -void dim_service_mlb_int_irq(void) -{ - dimcb_io_write(&g.dim2->MS0, 0); - dimcb_io_write(&g.dim2->MS1, 0); -} - -u16 dim_norm_ctrl_async_buffer_size(u16 buf_size) -{ - return norm_ctrl_async_buffer_size(buf_size); -} - -/** - * Retrieves maximal possible correct buffer size for isochronous data type - * conform to given packet length and not bigger than given buffer size. - * - * Returns non-zero correct buffer size or zero by error. - */ -u16 dim_norm_isoc_buffer_size(u16 buf_size, u16 packet_length) -{ - if (!check_packet_length(packet_length)) - return 0; - - return norm_isoc_buffer_size(buf_size, packet_length); -} - -/** - * Retrieves maximal possible correct buffer size for synchronous data type - * conform to given bytes per frame and not bigger than given buffer size. - * - * Returns non-zero correct buffer size or zero by error. - */ -u16 dim_norm_sync_buffer_size(u16 buf_size, u16 bytes_per_frame) -{ - if (!check_bytes_per_frame(bytes_per_frame)) - return 0; - - return norm_sync_buffer_size(buf_size, bytes_per_frame); -} - -u8 dim_init_control(struct dim_channel *ch, u8 is_tx, u16 ch_address, - u16 max_buffer_size) -{ - return init_ctrl_async(ch, CAT_CT_VAL_CONTROL, is_tx, ch_address, - max_buffer_size); -} - -u8 dim_init_async(struct dim_channel *ch, u8 is_tx, u16 ch_address, - u16 max_buffer_size) -{ - u8 ret = init_ctrl_async(ch, CAT_CT_VAL_ASYNC, is_tx, ch_address, - max_buffer_size); - - if (is_tx && !g.atx_dbr.ch_addr) { - g.atx_dbr.ch_addr = ch->addr; - dbrcnt_init(ch->addr, ch->dbr_size); - dimcb_io_write(&g.dim2->MIEN, bit_mask(20)); - } - - return ret; -} - -u8 dim_init_isoc(struct dim_channel *ch, u8 is_tx, u16 ch_address, - u16 packet_length) -{ - if (!g.dim_is_initialized || !ch) - return DIM_ERR_DRIVER_NOT_INITIALIZED; - - if (!check_channel_address(ch_address)) - return DIM_INIT_ERR_CHANNEL_ADDRESS; - - if (!check_packet_length(packet_length)) - return DIM_ERR_BAD_CONFIG; - - ch->dbr_size = packet_length * ISOC_DBR_FACTOR; - ch->dbr_addr = alloc_dbr(ch->dbr_size); - if (ch->dbr_addr >= DBR_SIZE) - return DIM_INIT_ERR_OUT_OF_MEMORY; - - isoc_init(ch, ch_address / 2, packet_length); - - dim2_configure_channel(ch->addr, CAT_CT_VAL_ISOC, is_tx, ch->dbr_addr, - ch->dbr_size, packet_length); - - return DIM_NO_ERROR; -} - -u8 dim_init_sync(struct dim_channel *ch, u8 is_tx, u16 ch_address, - u16 bytes_per_frame) -{ - u16 bd_factor = g.fcnt + 2; - - if (!g.dim_is_initialized || !ch) - return DIM_ERR_DRIVER_NOT_INITIALIZED; - - if (!check_channel_address(ch_address)) - return DIM_INIT_ERR_CHANNEL_ADDRESS; - - if (!check_bytes_per_frame(bytes_per_frame)) - return DIM_ERR_BAD_CONFIG; - - ch->dbr_size = bytes_per_frame << bd_factor; - ch->dbr_addr = alloc_dbr(ch->dbr_size); - if (ch->dbr_addr >= DBR_SIZE) - return DIM_INIT_ERR_OUT_OF_MEMORY; - - sync_init(ch, ch_address / 2, bytes_per_frame); - - dim2_clear_dbr(ch->dbr_addr, ch->dbr_size); - dim2_configure_channel(ch->addr, CAT_CT_VAL_SYNC, is_tx, - ch->dbr_addr, ch->dbr_size, 0); - - return DIM_NO_ERROR; -} - -u8 dim_destroy_channel(struct dim_channel *ch) -{ - if (!g.dim_is_initialized || !ch) - return DIM_ERR_DRIVER_NOT_INITIALIZED; - - if (ch->addr == g.atx_dbr.ch_addr) { - dimcb_io_write(&g.dim2->MIEN, 0); - g.atx_dbr.ch_addr = 0; - } - - dim2_clear_channel(ch->addr); - if (ch->dbr_addr < DBR_SIZE) - free_dbr(ch->dbr_addr, ch->dbr_size); - ch->dbr_addr = DBR_SIZE; - - return DIM_NO_ERROR; -} - -void dim_service_ahb_int_irq(struct dim_channel *const *channels) -{ - bool state_changed; - - if (!g.dim_is_initialized) { - dim_on_error(DIM_ERR_DRIVER_NOT_INITIALIZED, - "DIM is not initialized"); - return; - } - - if (!channels) { - dim_on_error(DIM_ERR_DRIVER_NOT_INITIALIZED, "Bad channels"); - return; - } - - /* - * Use while-loop and a flag to make sure the age is changed back at - * least once, otherwise the interrupt may never come if CPU generates - * interrupt on changing age. - * This cycle runs not more than number of channels, because - * channel_service_interrupt() routine doesn't start the channel again. - */ - do { - struct dim_channel *const *ch = channels; - - state_changed = false; - - while (*ch) { - state_changed |= channel_service_interrupt(*ch); - ++ch; - } - } while (state_changed); -} - -u8 dim_service_channel(struct dim_channel *ch) -{ - if (!g.dim_is_initialized || !ch) - return DIM_ERR_DRIVER_NOT_INITIALIZED; - - return channel_service(ch); -} - -struct dim_ch_state_t *dim_get_channel_state(struct dim_channel *ch, - struct dim_ch_state_t *state_ptr) -{ - if (!ch || !state_ptr) - return NULL; - - state_ptr->ready = ch->state.level < 2; - state_ptr->done_buffers = ch->done_sw_buffers_number; - - return state_ptr; -} - -bool dim_enqueue_buffer(struct dim_channel *ch, u32 buffer_addr, - u16 buffer_size) -{ - if (!ch) - return dim_on_error(DIM_ERR_DRIVER_NOT_INITIALIZED, - "Bad channel"); - - return channel_start(ch, buffer_addr, buffer_size); -} - -bool dim_detach_buffers(struct dim_channel *ch, u16 buffers_number) -{ - if (!ch) - return dim_on_error(DIM_ERR_DRIVER_NOT_INITIALIZED, - "Bad channel"); - - return channel_detach_buffers(ch, buffers_number); -} diff --git a/drivers/staging/most/hdm-dim2/dim2_hal.h b/drivers/staging/most/hdm-dim2/dim2_hal.h deleted file mode 100644 index fce9ae96121b..000000000000 --- a/drivers/staging/most/hdm-dim2/dim2_hal.h +++ /dev/null @@ -1,106 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * dim2_hal.h - DIM2 HAL interface - * (MediaLB, Device Interface Macro IP, OS62420) - * - * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG - */ - -#ifndef _DIM2_HAL_H -#define _DIM2_HAL_H - -#include -#include "dim2_reg.h" - -/* - * The values below are specified in the hardware specification. - * So, they should not be changed until the hardware specification changes. - */ -enum mlb_clk_speed { - CLK_256FS = 0, - CLK_512FS = 1, - CLK_1024FS = 2, - CLK_2048FS = 3, - CLK_3072FS = 4, - CLK_4096FS = 5, - CLK_6144FS = 6, - CLK_8192FS = 7, -}; - -struct dim_ch_state_t { - bool ready; /* Shows readiness to enqueue next buffer */ - u16 done_buffers; /* Number of completed buffers */ -}; - -struct int_ch_state { - /* changed only in interrupt context */ - volatile int request_counter; - - /* changed only in task context */ - volatile int service_counter; - - u8 idx1; - u8 idx2; - u8 level; /* [0..2], buffering level */ -}; - -struct dim_channel { - struct int_ch_state state; - u8 addr; - u16 dbr_addr; - u16 dbr_size; - u16 packet_length; /*< Isochronous packet length in bytes. */ - u16 bytes_per_frame; /*< Synchronous bytes per frame. */ - u16 done_sw_buffers_number; /*< Done software buffers number. */ -}; - -u8 dim_startup(struct dim2_regs __iomem *dim_base_address, u32 mlb_clock, - u32 fcnt); - -void dim_shutdown(void); - -bool dim_get_lock_state(void); - -u16 dim_norm_ctrl_async_buffer_size(u16 buf_size); - -u16 dim_norm_isoc_buffer_size(u16 buf_size, u16 packet_length); - -u16 dim_norm_sync_buffer_size(u16 buf_size, u16 bytes_per_frame); - -u8 dim_init_control(struct dim_channel *ch, u8 is_tx, u16 ch_address, - u16 max_buffer_size); - -u8 dim_init_async(struct dim_channel *ch, u8 is_tx, u16 ch_address, - u16 max_buffer_size); - -u8 dim_init_isoc(struct dim_channel *ch, u8 is_tx, u16 ch_address, - u16 packet_length); - -u8 dim_init_sync(struct dim_channel *ch, u8 is_tx, u16 ch_address, - u16 bytes_per_frame); - -u8 dim_destroy_channel(struct dim_channel *ch); - -void dim_service_mlb_int_irq(void); - -void dim_service_ahb_int_irq(struct dim_channel *const *channels); - -u8 dim_service_channel(struct dim_channel *ch); - -struct dim_ch_state_t *dim_get_channel_state(struct dim_channel *ch, - struct dim_ch_state_t *state_ptr); - -u16 dim_dbr_space(struct dim_channel *ch); - -bool dim_enqueue_buffer(struct dim_channel *ch, u32 buffer_addr, - u16 buffer_size); - -bool dim_detach_buffers(struct dim_channel *ch, u16 buffers_number); - -u32 dimcb_io_read(u32 __iomem *ptr32); - -void dimcb_io_write(u32 __iomem *ptr32, u32 value); - -void dimcb_on_error(u8 error_id, const char *error_message); - -#endif /* _DIM2_HAL_H */ diff --git a/drivers/staging/most/hdm-dim2/dim2_hdm.c b/drivers/staging/most/hdm-dim2/dim2_hdm.c deleted file mode 100644 index fedd2d06742a..000000000000 --- a/drivers/staging/most/hdm-dim2/dim2_hdm.c +++ /dev/null @@ -1,912 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * dim2_hdm.c - MediaLB DIM2 Hardware Dependent Module - * - * Copyright (C) 2015-2016, Microchip Technology Germany II GmbH & Co. KG - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "most/core.h" -#include "dim2_hal.h" -#include "dim2_hdm.h" -#include "dim2_errors.h" -#include "dim2_sysfs.h" - -#define DMA_CHANNELS (32 - 1) /* channel 0 is a system channel */ - -#define MAX_BUFFERS_PACKET 32 -#define MAX_BUFFERS_STREAMING 32 -#define MAX_BUF_SIZE_PACKET 2048 -#define MAX_BUF_SIZE_STREAMING (8 * 1024) - -/* command line parameter to select clock speed */ -static char *clock_speed; -module_param(clock_speed, charp, 0000); -MODULE_PARM_DESC(clock_speed, "MediaLB Clock Speed"); - -/* - * The parameter representing the number of frames per sub-buffer for - * synchronous channels. Valid values: [0 .. 6]. - * - * The values 0, 1, 2, 3, 4, 5, 6 represent corresponding number of frames per - * sub-buffer 1, 2, 4, 8, 16, 32, 64. - */ -static u8 fcnt = 4; /* (1 << fcnt) frames per subbuffer */ -module_param(fcnt, byte, 0000); -MODULE_PARM_DESC(fcnt, "Num of frames per sub-buffer for sync channels as a power of 2"); - -static DEFINE_SPINLOCK(dim_lock); - -static void dim2_tasklet_fn(unsigned long data); -static DECLARE_TASKLET(dim2_tasklet, dim2_tasklet_fn, 0); - -/** - * struct hdm_channel - private structure to keep channel specific data - * @is_initialized: identifier to know whether the channel is initialized - * @ch: HAL specific channel data - * @pending_list: list to keep MBO's before starting transfer - * @started_list: list to keep MBO's after starting transfer - * @direction: channel direction (TX or RX) - * @data_type: channel data type - */ -struct hdm_channel { - char name[sizeof "caNNN"]; - bool is_initialized; - struct dim_channel ch; - struct list_head pending_list; /* before dim_enqueue_buffer() */ - struct list_head started_list; /* after dim_enqueue_buffer() */ - enum most_channel_direction direction; - enum most_channel_data_type data_type; -}; - -/** - * struct dim2_hdm - private structure to keep interface specific data - * @hch: an array of channel specific data - * @most_iface: most interface structure - * @capabilities: an array of channel capability data - * @io_base: I/O register base address - * @clk_speed: user selectable (through command line parameter) clock speed - * @netinfo_task: thread to deliver network status - * @netinfo_waitq: waitq for the thread to sleep - * @deliver_netinfo: to identify whether network status received - * @mac_addrs: INIC mac address - * @link_state: network link state - * @atx_idx: index of async tx channel - */ -struct dim2_hdm { - struct hdm_channel hch[DMA_CHANNELS]; - struct most_channel_capability capabilities[DMA_CHANNELS]; - struct most_interface most_iface; - char name[16 + sizeof "dim2-"]; - void __iomem *io_base; - int clk_speed; - struct task_struct *netinfo_task; - wait_queue_head_t netinfo_waitq; - int deliver_netinfo; - unsigned char mac_addrs[6]; - unsigned char link_state; - int atx_idx; - struct medialb_bus bus; - void (*on_netinfo)(struct most_interface *, - unsigned char, unsigned char *); -}; - -#define iface_to_hdm(iface) container_of(iface, struct dim2_hdm, most_iface) - -/* Macro to identify a network status message */ -#define PACKET_IS_NET_INFO(p) \ - (((p)[1] == 0x18) && ((p)[2] == 0x05) && ((p)[3] == 0x0C) && \ - ((p)[13] == 0x3C) && ((p)[14] == 0x00) && ((p)[15] == 0x0A)) - -bool dim2_sysfs_get_state_cb(void) -{ - bool state; - unsigned long flags; - - spin_lock_irqsave(&dim_lock, flags); - state = dim_get_lock_state(); - spin_unlock_irqrestore(&dim_lock, flags); - - return state; -} - -/** - * dimcb_io_read - callback from HAL to read an I/O register - * @ptr32: register address - */ -u32 dimcb_io_read(u32 __iomem *ptr32) -{ - return readl(ptr32); -} - -/** - * dimcb_io_write - callback from HAL to write value to an I/O register - * @ptr32: register address - * @value: value to write - */ -void dimcb_io_write(u32 __iomem *ptr32, u32 value) -{ - writel(value, ptr32); -} - -/** - * dimcb_on_error - callback from HAL to report miscommunication between - * HDM and HAL - * @error_id: Error ID - * @error_message: Error message. Some text in a free format - */ -void dimcb_on_error(u8 error_id, const char *error_message) -{ - pr_err("dimcb_on_error: error_id - %d, error_message - %s\n", error_id, - error_message); -} - -/** - * startup_dim - initialize the dim2 interface - * @pdev: platform device - * - * Get the value of command line parameter "clock_speed" if given or use the - * default value, enable the clock and PLL, and initialize the dim2 interface. - */ -static int startup_dim(struct platform_device *pdev) -{ - struct dim2_hdm *dev = platform_get_drvdata(pdev); - struct dim2_platform_data *pdata = pdev->dev.platform_data; - u8 hal_ret; - - dev->clk_speed = -1; - - if (clock_speed) { - if (!strcmp(clock_speed, "256fs")) - dev->clk_speed = CLK_256FS; - else if (!strcmp(clock_speed, "512fs")) - dev->clk_speed = CLK_512FS; - else if (!strcmp(clock_speed, "1024fs")) - dev->clk_speed = CLK_1024FS; - else if (!strcmp(clock_speed, "2048fs")) - dev->clk_speed = CLK_2048FS; - else if (!strcmp(clock_speed, "3072fs")) - dev->clk_speed = CLK_3072FS; - else if (!strcmp(clock_speed, "4096fs")) - dev->clk_speed = CLK_4096FS; - else if (!strcmp(clock_speed, "6144fs")) - dev->clk_speed = CLK_6144FS; - else if (!strcmp(clock_speed, "8192fs")) - dev->clk_speed = CLK_8192FS; - } - - if (dev->clk_speed == -1) { - pr_info("Bad or missing clock speed parameter, using default value: 3072fs\n"); - dev->clk_speed = CLK_3072FS; - } else { - pr_info("Selected clock speed: %s\n", clock_speed); - } - if (pdata && pdata->init) { - int ret = pdata->init(pdata, dev->io_base, dev->clk_speed); - - if (ret) - return ret; - } - - pr_info("sync: num of frames per sub-buffer: %u\n", fcnt); - hal_ret = dim_startup(dev->io_base, dev->clk_speed, fcnt); - if (hal_ret != DIM_NO_ERROR) { - pr_err("dim_startup failed: %d\n", hal_ret); - if (pdata && pdata->destroy) - pdata->destroy(pdata); - return -ENODEV; - } - - return 0; -} - -/** - * try_start_dim_transfer - try to transfer a buffer on a channel - * @hdm_ch: channel specific data - * - * Transfer a buffer from pending_list if the channel is ready - */ -static int try_start_dim_transfer(struct hdm_channel *hdm_ch) -{ - u16 buf_size; - struct list_head *head = &hdm_ch->pending_list; - struct mbo *mbo; - unsigned long flags; - struct dim_ch_state_t st; - - BUG_ON(!hdm_ch); - BUG_ON(!hdm_ch->is_initialized); - - spin_lock_irqsave(&dim_lock, flags); - if (list_empty(head)) { - spin_unlock_irqrestore(&dim_lock, flags); - return -EAGAIN; - } - - if (!dim_get_channel_state(&hdm_ch->ch, &st)->ready) { - spin_unlock_irqrestore(&dim_lock, flags); - return -EAGAIN; - } - - mbo = list_first_entry(head, struct mbo, list); - buf_size = mbo->buffer_length; - - if (dim_dbr_space(&hdm_ch->ch) < buf_size) { - spin_unlock_irqrestore(&dim_lock, flags); - return -EAGAIN; - } - - BUG_ON(mbo->bus_address == 0); - if (!dim_enqueue_buffer(&hdm_ch->ch, mbo->bus_address, buf_size)) { - list_del(head->next); - spin_unlock_irqrestore(&dim_lock, flags); - mbo->processed_length = 0; - mbo->status = MBO_E_INVAL; - mbo->complete(mbo); - return -EFAULT; - } - - list_move_tail(head->next, &hdm_ch->started_list); - spin_unlock_irqrestore(&dim_lock, flags); - - return 0; -} - -/** - * deliver_netinfo_thread - thread to deliver network status to mostcore - * @data: private data - * - * Wait for network status and deliver it to mostcore once it is received - */ -static int deliver_netinfo_thread(void *data) -{ - struct dim2_hdm *dev = data; - - while (!kthread_should_stop()) { - wait_event_interruptible(dev->netinfo_waitq, - dev->deliver_netinfo || - kthread_should_stop()); - - if (dev->deliver_netinfo) { - dev->deliver_netinfo--; - if (dev->on_netinfo) { - dev->on_netinfo(&dev->most_iface, - dev->link_state, - dev->mac_addrs); - } - } - } - - return 0; -} - -/** - * retrieve_netinfo - retrieve network status from received buffer - * @dev: private data - * @mbo: received MBO - * - * Parse the message in buffer and get node address, link state, MAC address. - * Wake up a thread to deliver this status to mostcore - */ -static void retrieve_netinfo(struct dim2_hdm *dev, struct mbo *mbo) -{ - u8 *data = mbo->virt_address; - - pr_info("Node Address: 0x%03x\n", (u16)data[16] << 8 | data[17]); - dev->link_state = data[18]; - pr_info("NIState: %d\n", dev->link_state); - memcpy(dev->mac_addrs, data + 19, 6); - dev->deliver_netinfo++; - wake_up_interruptible(&dev->netinfo_waitq); -} - -/** - * service_done_flag - handle completed buffers - * @dev: private data - * @ch_idx: channel index - * - * Return back the completed buffers to mostcore, using completion callback - */ -static void service_done_flag(struct dim2_hdm *dev, int ch_idx) -{ - struct hdm_channel *hdm_ch = dev->hch + ch_idx; - struct dim_ch_state_t st; - struct list_head *head; - struct mbo *mbo; - int done_buffers; - unsigned long flags; - u8 *data; - - BUG_ON(!hdm_ch); - BUG_ON(!hdm_ch->is_initialized); - - spin_lock_irqsave(&dim_lock, flags); - - done_buffers = dim_get_channel_state(&hdm_ch->ch, &st)->done_buffers; - if (!done_buffers) { - spin_unlock_irqrestore(&dim_lock, flags); - return; - } - - if (!dim_detach_buffers(&hdm_ch->ch, done_buffers)) { - spin_unlock_irqrestore(&dim_lock, flags); - return; - } - spin_unlock_irqrestore(&dim_lock, flags); - - head = &hdm_ch->started_list; - - while (done_buffers) { - spin_lock_irqsave(&dim_lock, flags); - if (list_empty(head)) { - spin_unlock_irqrestore(&dim_lock, flags); - pr_crit("hard error: started_mbo list is empty whereas DIM2 has sent buffers\n"); - break; - } - - mbo = list_first_entry(head, struct mbo, list); - list_del(head->next); - spin_unlock_irqrestore(&dim_lock, flags); - - data = mbo->virt_address; - - if (hdm_ch->data_type == MOST_CH_ASYNC && - hdm_ch->direction == MOST_CH_RX && - PACKET_IS_NET_INFO(data)) { - retrieve_netinfo(dev, mbo); - - spin_lock_irqsave(&dim_lock, flags); - list_add_tail(&mbo->list, &hdm_ch->pending_list); - spin_unlock_irqrestore(&dim_lock, flags); - } else { - if (hdm_ch->data_type == MOST_CH_CONTROL || - hdm_ch->data_type == MOST_CH_ASYNC) { - u32 const data_size = - (u32)data[0] * 256 + data[1] + 2; - - mbo->processed_length = - min_t(u32, data_size, - mbo->buffer_length); - } else { - mbo->processed_length = mbo->buffer_length; - } - mbo->status = MBO_SUCCESS; - mbo->complete(mbo); - } - - done_buffers--; - } -} - -static struct dim_channel **get_active_channels(struct dim2_hdm *dev, - struct dim_channel **buffer) -{ - int idx = 0; - int ch_idx; - - for (ch_idx = 0; ch_idx < DMA_CHANNELS; ch_idx++) { - if (dev->hch[ch_idx].is_initialized) - buffer[idx++] = &dev->hch[ch_idx].ch; - } - buffer[idx++] = NULL; - - return buffer; -} - -static irqreturn_t dim2_mlb_isr(int irq, void *_dev) -{ - struct dim2_hdm *dev = _dev; - unsigned long flags; - - spin_lock_irqsave(&dim_lock, flags); - dim_service_mlb_int_irq(); - spin_unlock_irqrestore(&dim_lock, flags); - - if (dev->atx_idx >= 0 && dev->hch[dev->atx_idx].is_initialized) - while (!try_start_dim_transfer(dev->hch + dev->atx_idx)) - continue; - - return IRQ_HANDLED; -} - -/** - * dim2_tasklet_fn - tasklet function - * @data: private data - * - * Service each initialized channel, if needed - */ -static void dim2_tasklet_fn(unsigned long data) -{ - struct dim2_hdm *dev = (struct dim2_hdm *)data; - unsigned long flags; - int ch_idx; - - for (ch_idx = 0; ch_idx < DMA_CHANNELS; ch_idx++) { - if (!dev->hch[ch_idx].is_initialized) - continue; - - spin_lock_irqsave(&dim_lock, flags); - dim_service_channel(&dev->hch[ch_idx].ch); - spin_unlock_irqrestore(&dim_lock, flags); - - service_done_flag(dev, ch_idx); - while (!try_start_dim_transfer(dev->hch + ch_idx)) - continue; - } -} - -/** - * dim2_ahb_isr - interrupt service routine - * @irq: irq number - * @_dev: private data - * - * Acknowledge the interrupt and schedule a tasklet to service channels. - * Return IRQ_HANDLED. - */ -static irqreturn_t dim2_ahb_isr(int irq, void *_dev) -{ - struct dim2_hdm *dev = _dev; - struct dim_channel *buffer[DMA_CHANNELS + 1]; - unsigned long flags; - - spin_lock_irqsave(&dim_lock, flags); - dim_service_ahb_int_irq(get_active_channels(dev, buffer)); - spin_unlock_irqrestore(&dim_lock, flags); - - dim2_tasklet.data = (unsigned long)dev; - tasklet_schedule(&dim2_tasklet); - return IRQ_HANDLED; -} - -/** - * complete_all_mbos - complete MBO's in a list - * @head: list head - * - * Delete all the entries in list and return back MBO's to mostcore using - * completion call back. - */ -static void complete_all_mbos(struct list_head *head) -{ - unsigned long flags; - struct mbo *mbo; - - for (;;) { - spin_lock_irqsave(&dim_lock, flags); - if (list_empty(head)) { - spin_unlock_irqrestore(&dim_lock, flags); - break; - } - - mbo = list_first_entry(head, struct mbo, list); - list_del(head->next); - spin_unlock_irqrestore(&dim_lock, flags); - - mbo->processed_length = 0; - mbo->status = MBO_E_CLOSE; - mbo->complete(mbo); - } -} - -/** - * configure_channel - initialize a channel - * @iface: interface the channel belongs to - * @channel: channel to be configured - * @channel_config: structure that holds the configuration information - * - * Receives configuration information from mostcore and initialize - * the corresponding channel. Return 0 on success, negative on failure. - */ -static int configure_channel(struct most_interface *most_iface, int ch_idx, - struct most_channel_config *ccfg) -{ - struct dim2_hdm *dev = iface_to_hdm(most_iface); - bool const is_tx = ccfg->direction == MOST_CH_TX; - u16 const sub_size = ccfg->subbuffer_size; - u16 const buf_size = ccfg->buffer_size; - u16 new_size; - unsigned long flags; - u8 hal_ret; - int const ch_addr = ch_idx * 2 + 2; - struct hdm_channel *const hdm_ch = dev->hch + ch_idx; - - BUG_ON(ch_idx < 0 || ch_idx >= DMA_CHANNELS); - - if (hdm_ch->is_initialized) - return -EPERM; - - switch (ccfg->data_type) { - case MOST_CH_CONTROL: - new_size = dim_norm_ctrl_async_buffer_size(buf_size); - if (new_size == 0) { - pr_err("%s: too small buffer size\n", hdm_ch->name); - return -EINVAL; - } - ccfg->buffer_size = new_size; - if (new_size != buf_size) - pr_warn("%s: fixed buffer size (%d -> %d)\n", - hdm_ch->name, buf_size, new_size); - spin_lock_irqsave(&dim_lock, flags); - hal_ret = dim_init_control(&hdm_ch->ch, is_tx, ch_addr, - is_tx ? new_size * 2 : new_size); - break; - case MOST_CH_ASYNC: - new_size = dim_norm_ctrl_async_buffer_size(buf_size); - if (new_size == 0) { - pr_err("%s: too small buffer size\n", hdm_ch->name); - return -EINVAL; - } - ccfg->buffer_size = new_size; - if (new_size != buf_size) - pr_warn("%s: fixed buffer size (%d -> %d)\n", - hdm_ch->name, buf_size, new_size); - spin_lock_irqsave(&dim_lock, flags); - hal_ret = dim_init_async(&hdm_ch->ch, is_tx, ch_addr, - is_tx ? new_size * 2 : new_size); - break; - case MOST_CH_ISOC: - new_size = dim_norm_isoc_buffer_size(buf_size, sub_size); - if (new_size == 0) { - pr_err("%s: invalid sub-buffer size or too small buffer size\n", - hdm_ch->name); - return -EINVAL; - } - ccfg->buffer_size = new_size; - if (new_size != buf_size) - pr_warn("%s: fixed buffer size (%d -> %d)\n", - hdm_ch->name, buf_size, new_size); - spin_lock_irqsave(&dim_lock, flags); - hal_ret = dim_init_isoc(&hdm_ch->ch, is_tx, ch_addr, sub_size); - break; - case MOST_CH_SYNC: - new_size = dim_norm_sync_buffer_size(buf_size, sub_size); - if (new_size == 0) { - pr_err("%s: invalid sub-buffer size or too small buffer size\n", - hdm_ch->name); - return -EINVAL; - } - ccfg->buffer_size = new_size; - if (new_size != buf_size) - pr_warn("%s: fixed buffer size (%d -> %d)\n", - hdm_ch->name, buf_size, new_size); - spin_lock_irqsave(&dim_lock, flags); - hal_ret = dim_init_sync(&hdm_ch->ch, is_tx, ch_addr, sub_size); - break; - default: - pr_err("%s: configure failed, bad channel type: %d\n", - hdm_ch->name, ccfg->data_type); - return -EINVAL; - } - - if (hal_ret != DIM_NO_ERROR) { - spin_unlock_irqrestore(&dim_lock, flags); - pr_err("%s: configure failed (%d), type: %d, is_tx: %d\n", - hdm_ch->name, hal_ret, ccfg->data_type, (int)is_tx); - return -ENODEV; - } - - hdm_ch->data_type = ccfg->data_type; - hdm_ch->direction = ccfg->direction; - hdm_ch->is_initialized = true; - - if (hdm_ch->data_type == MOST_CH_ASYNC && - hdm_ch->direction == MOST_CH_TX && - dev->atx_idx < 0) - dev->atx_idx = ch_idx; - - spin_unlock_irqrestore(&dim_lock, flags); - - return 0; -} - -/** - * enqueue - enqueue a buffer for data transfer - * @iface: intended interface - * @channel: ID of the channel the buffer is intended for - * @mbo: pointer to the buffer object - * - * Push the buffer into pending_list and try to transfer one buffer from - * pending_list. Return 0 on success, negative on failure. - */ -static int enqueue(struct most_interface *most_iface, int ch_idx, - struct mbo *mbo) -{ - struct dim2_hdm *dev = iface_to_hdm(most_iface); - struct hdm_channel *hdm_ch = dev->hch + ch_idx; - unsigned long flags; - - BUG_ON(ch_idx < 0 || ch_idx >= DMA_CHANNELS); - - if (!hdm_ch->is_initialized) - return -EPERM; - - if (mbo->bus_address == 0) - return -EFAULT; - - spin_lock_irqsave(&dim_lock, flags); - list_add_tail(&mbo->list, &hdm_ch->pending_list); - spin_unlock_irqrestore(&dim_lock, flags); - - (void)try_start_dim_transfer(hdm_ch); - - return 0; -} - -/** - * request_netinfo - triggers retrieving of network info - * @iface: pointer to the interface - * @channel_id: corresponding channel ID - * - * Send a command to INIC which triggers retrieving of network info by means of - * "Message exchange over MDP/MEP". Return 0 on success, negative on failure. - */ -static void request_netinfo(struct most_interface *most_iface, int ch_idx, - void (*on_netinfo)(struct most_interface *, - unsigned char, unsigned char *)) -{ - struct dim2_hdm *dev = iface_to_hdm(most_iface); - struct mbo *mbo; - u8 *data; - - dev->on_netinfo = on_netinfo; - if (!on_netinfo) - return; - - if (dev->atx_idx < 0) { - pr_err("Async Tx Not initialized\n"); - return; - } - - mbo = most_get_mbo(&dev->most_iface, dev->atx_idx, NULL); - if (!mbo) - return; - - mbo->buffer_length = 5; - - data = mbo->virt_address; - - data[0] = 0x00; /* PML High byte */ - data[1] = 0x03; /* PML Low byte */ - data[2] = 0x02; /* PMHL */ - data[3] = 0x08; /* FPH */ - data[4] = 0x40; /* FMF (FIFO cmd msg - Triggers NAOverMDP) */ - - most_submit_mbo(mbo); -} - -/** - * poison_channel - poison buffers of a channel - * @iface: pointer to the interface the channel to be poisoned belongs to - * @channel_id: corresponding channel ID - * - * Destroy a channel and complete all the buffers in both started_list & - * pending_list. Return 0 on success, negative on failure. - */ -static int poison_channel(struct most_interface *most_iface, int ch_idx) -{ - struct dim2_hdm *dev = iface_to_hdm(most_iface); - struct hdm_channel *hdm_ch = dev->hch + ch_idx; - unsigned long flags; - u8 hal_ret; - int ret = 0; - - BUG_ON(ch_idx < 0 || ch_idx >= DMA_CHANNELS); - - if (!hdm_ch->is_initialized) - return -EPERM; - - tasklet_disable(&dim2_tasklet); - spin_lock_irqsave(&dim_lock, flags); - hal_ret = dim_destroy_channel(&hdm_ch->ch); - hdm_ch->is_initialized = false; - if (ch_idx == dev->atx_idx) - dev->atx_idx = -1; - spin_unlock_irqrestore(&dim_lock, flags); - tasklet_enable(&dim2_tasklet); - if (hal_ret != DIM_NO_ERROR) { - pr_err("HAL Failed to close channel %s\n", hdm_ch->name); - ret = -EFAULT; - } - - complete_all_mbos(&hdm_ch->started_list); - complete_all_mbos(&hdm_ch->pending_list); - - return ret; -} - -/* - * dim2_probe - dim2 probe handler - * @pdev: platform device structure - * - * Register the dim2 interface with mostcore and initialize it. - * Return 0 on success, negative on failure. - */ -static int dim2_probe(struct platform_device *pdev) -{ - struct dim2_hdm *dev; - struct resource *res; - int ret, i; - struct kobject *kobj; - int irq; - - dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - dev->atx_idx = -1; - - platform_set_drvdata(pdev, dev); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dev->io_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(dev->io_base)) - return PTR_ERR(dev->io_base); - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "failed to get ahb0_int irq: %d\n", irq); - return irq; - } - - ret = devm_request_irq(&pdev->dev, irq, dim2_ahb_isr, 0, - "dim2_ahb0_int", dev); - if (ret) { - dev_err(&pdev->dev, "failed to request ahb0_int irq %d\n", irq); - return ret; - } - - irq = platform_get_irq(pdev, 1); - if (irq < 0) { - dev_err(&pdev->dev, "failed to get mlb_int irq: %d\n", irq); - return irq; - } - - ret = devm_request_irq(&pdev->dev, irq, dim2_mlb_isr, 0, - "dim2_mlb_int", dev); - if (ret) { - dev_err(&pdev->dev, "failed to request mlb_int irq %d\n", irq); - return ret; - } - - init_waitqueue_head(&dev->netinfo_waitq); - dev->deliver_netinfo = 0; - dev->netinfo_task = kthread_run(&deliver_netinfo_thread, (void *)dev, - "dim2_netinfo"); - if (IS_ERR(dev->netinfo_task)) - return PTR_ERR(dev->netinfo_task); - - for (i = 0; i < DMA_CHANNELS; i++) { - struct most_channel_capability *cap = dev->capabilities + i; - struct hdm_channel *hdm_ch = dev->hch + i; - - INIT_LIST_HEAD(&hdm_ch->pending_list); - INIT_LIST_HEAD(&hdm_ch->started_list); - hdm_ch->is_initialized = false; - snprintf(hdm_ch->name, sizeof(hdm_ch->name), "ca%d", i * 2 + 2); - - cap->name_suffix = hdm_ch->name; - cap->direction = MOST_CH_RX | MOST_CH_TX; - cap->data_type = MOST_CH_CONTROL | MOST_CH_ASYNC | - MOST_CH_ISOC | MOST_CH_SYNC; - cap->num_buffers_packet = MAX_BUFFERS_PACKET; - cap->buffer_size_packet = MAX_BUF_SIZE_PACKET; - cap->num_buffers_streaming = MAX_BUFFERS_STREAMING; - cap->buffer_size_streaming = MAX_BUF_SIZE_STREAMING; - } - - { - const char *fmt; - - if (sizeof(res->start) == sizeof(long long)) - fmt = "dim2-%016llx"; - else if (sizeof(res->start) == sizeof(long)) - fmt = "dim2-%016lx"; - else - fmt = "dim2-%016x"; - - snprintf(dev->name, sizeof(dev->name), fmt, res->start); - } - - dev->most_iface.interface = ITYPE_MEDIALB_DIM2; - dev->most_iface.description = dev->name; - dev->most_iface.num_channels = DMA_CHANNELS; - dev->most_iface.channel_vector = dev->capabilities; - dev->most_iface.configure = configure_channel; - dev->most_iface.enqueue = enqueue; - dev->most_iface.poison_channel = poison_channel; - dev->most_iface.request_netinfo = request_netinfo; - - kobj = most_register_interface(&dev->most_iface); - if (IS_ERR(kobj)) { - ret = PTR_ERR(kobj); - dev_err(&pdev->dev, "failed to register MOST interface\n"); - goto err_stop_thread; - } - - ret = dim2_sysfs_probe(&dev->bus, kobj); - if (ret) - goto err_unreg_iface; - - ret = startup_dim(pdev); - if (ret) { - dev_err(&pdev->dev, "failed to initialize DIM2\n"); - goto err_destroy_bus; - } - - return 0; - -err_destroy_bus: - dim2_sysfs_destroy(&dev->bus); -err_unreg_iface: - most_deregister_interface(&dev->most_iface); -err_stop_thread: - kthread_stop(dev->netinfo_task); - - return ret; -} - -/** - * dim2_remove - dim2 remove handler - * @pdev: platform device structure - * - * Unregister the interface from mostcore - */ -static int dim2_remove(struct platform_device *pdev) -{ - struct dim2_hdm *dev = platform_get_drvdata(pdev); - struct dim2_platform_data *pdata = pdev->dev.platform_data; - unsigned long flags; - - spin_lock_irqsave(&dim_lock, flags); - dim_shutdown(); - spin_unlock_irqrestore(&dim_lock, flags); - - if (pdata && pdata->destroy) - pdata->destroy(pdata); - - dim2_sysfs_destroy(&dev->bus); - most_deregister_interface(&dev->most_iface); - kthread_stop(dev->netinfo_task); - - /* - * break link to local platform_device_id struct - * to prevent crash by unload platform device module - */ - pdev->id_entry = NULL; - - return 0; -} - -static const struct platform_device_id dim2_id[] = { - { "medialb_dim2" }, - { }, /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(platform, dim2_id); - -static struct platform_driver dim2_driver = { - .probe = dim2_probe, - .remove = dim2_remove, - .id_table = dim2_id, - .driver = { - .name = "hdm_dim2", - }, -}; - -module_platform_driver(dim2_driver); - -MODULE_AUTHOR("Jain Roy Ambi "); -MODULE_AUTHOR("Andrey Shvetsov "); -MODULE_DESCRIPTION("MediaLB DIM2 Hardware Dependent Module"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/most/hdm-dim2/dim2_hdm.h b/drivers/staging/most/hdm-dim2/dim2_hdm.h deleted file mode 100644 index 5f380b648bd7..000000000000 --- a/drivers/staging/most/hdm-dim2/dim2_hdm.h +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * dim2_hdm.h - MediaLB DIM2 HDM Header - * - * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG - */ - -#ifndef DIM2_HDM_H -#define DIM2_HDM_H - -struct device; - -/* platform dependent data for dim2 interface */ -struct dim2_platform_data { - int (*init)(struct dim2_platform_data *pd, void __iomem *io_base, - int clk_speed); - void (*destroy)(struct dim2_platform_data *pd); - void *priv; -}; - -#endif /* DIM2_HDM_H */ diff --git a/drivers/staging/most/hdm-dim2/dim2_reg.h b/drivers/staging/most/hdm-dim2/dim2_reg.h deleted file mode 100644 index 2b2fca4f6451..000000000000 --- a/drivers/staging/most/hdm-dim2/dim2_reg.h +++ /dev/null @@ -1,157 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * dim2_reg.h - Definitions for registers of DIM2 - * (MediaLB, Device Interface Macro IP, OS62420) - * - * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG - */ - -#ifndef DIM2_OS62420_H -#define DIM2_OS62420_H - -#include - -struct dim2_regs { - /* 0x00 */ u32 MLBC0; - /* 0x01 */ u32 rsvd0[1]; - /* 0x02 */ u32 MLBPC0; - /* 0x03 */ u32 MS0; - /* 0x04 */ u32 rsvd1[1]; - /* 0x05 */ u32 MS1; - /* 0x06 */ u32 rsvd2[2]; - /* 0x08 */ u32 MSS; - /* 0x09 */ u32 MSD; - /* 0x0A */ u32 rsvd3[1]; - /* 0x0B */ u32 MIEN; - /* 0x0C */ u32 rsvd4[1]; - /* 0x0D */ u32 MLBPC2; - /* 0x0E */ u32 MLBPC1; - /* 0x0F */ u32 MLBC1; - /* 0x10 */ u32 rsvd5[0x10]; - /* 0x20 */ u32 HCTL; - /* 0x21 */ u32 rsvd6[1]; - /* 0x22 */ u32 HCMR0; - /* 0x23 */ u32 HCMR1; - /* 0x24 */ u32 HCER0; - /* 0x25 */ u32 HCER1; - /* 0x26 */ u32 HCBR0; - /* 0x27 */ u32 HCBR1; - /* 0x28 */ u32 rsvd7[8]; - /* 0x30 */ u32 MDAT0; - /* 0x31 */ u32 MDAT1; - /* 0x32 */ u32 MDAT2; - /* 0x33 */ u32 MDAT3; - /* 0x34 */ u32 MDWE0; - /* 0x35 */ u32 MDWE1; - /* 0x36 */ u32 MDWE2; - /* 0x37 */ u32 MDWE3; - /* 0x38 */ u32 MCTL; - /* 0x39 */ u32 MADR; - /* 0x3A */ u32 rsvd8[0xB6]; - /* 0xF0 */ u32 ACTL; - /* 0xF1 */ u32 rsvd9[3]; - /* 0xF4 */ u32 ACSR0; - /* 0xF5 */ u32 ACSR1; - /* 0xF6 */ u32 ACMR0; - /* 0xF7 */ u32 ACMR1; -}; - -#define DIM2_MASK(n) (~((~(u32)0) << (n))) - -enum { - MLBC0_MLBLK_BIT = 7, - - MLBC0_MLBPEN_BIT = 5, - - MLBC0_MLBCLK_SHIFT = 2, - MLBC0_MLBCLK_VAL_256FS = 0, - MLBC0_MLBCLK_VAL_512FS = 1, - MLBC0_MLBCLK_VAL_1024FS = 2, - MLBC0_MLBCLK_VAL_2048FS = 3, - - MLBC0_FCNT_SHIFT = 15, - MLBC0_FCNT_MASK = 7, - MLBC0_FCNT_MAX_VAL = 6, - - MLBC0_MLBEN_BIT = 0, - - MIEN_CTX_BREAK_BIT = 29, - MIEN_CTX_PE_BIT = 28, - MIEN_CTX_DONE_BIT = 27, - - MIEN_CRX_BREAK_BIT = 26, - MIEN_CRX_PE_BIT = 25, - MIEN_CRX_DONE_BIT = 24, - - MIEN_ATX_BREAK_BIT = 22, - MIEN_ATX_PE_BIT = 21, - MIEN_ATX_DONE_BIT = 20, - - MIEN_ARX_BREAK_BIT = 19, - MIEN_ARX_PE_BIT = 18, - MIEN_ARX_DONE_BIT = 17, - - MIEN_SYNC_PE_BIT = 16, - - MIEN_ISOC_BUFO_BIT = 1, - MIEN_ISOC_PE_BIT = 0, - - MLBC1_NDA_SHIFT = 8, - MLBC1_NDA_MASK = 0xFF, - - MLBC1_CLKMERR_BIT = 7, - MLBC1_LOCKERR_BIT = 6, - - ACTL_DMA_MODE_BIT = 2, - ACTL_DMA_MODE_VAL_DMA_MODE_0 = 0, - ACTL_DMA_MODE_VAL_DMA_MODE_1 = 1, - ACTL_SCE_BIT = 0, - - HCTL_EN_BIT = 15 -}; - -enum { - CDT0_RPC_SHIFT = 16 + 11, - CDT0_RPC_MASK = DIM2_MASK(5), - - CDT1_BS_ISOC_SHIFT = 0, - CDT1_BS_ISOC_MASK = DIM2_MASK(9), - - CDT3_BD_SHIFT = 0, - CDT3_BD_MASK = DIM2_MASK(12), - CDT3_BD_ISOC_MASK = DIM2_MASK(13), - CDT3_BA_SHIFT = 16, - - ADT0_CE_BIT = 15, - ADT0_LE_BIT = 14, - ADT0_PG_BIT = 13, - - ADT1_RDY_BIT = 15, - ADT1_DNE_BIT = 14, - ADT1_ERR_BIT = 13, - ADT1_PS_BIT = 12, - ADT1_MEP_BIT = 11, - ADT1_BD_SHIFT = 0, - ADT1_CTRL_ASYNC_BD_MASK = DIM2_MASK(11), - ADT1_ISOC_SYNC_BD_MASK = DIM2_MASK(13), - - CAT_FCE_BIT = 14, - CAT_MFE_BIT = 14, - - CAT_MT_BIT = 13, - - CAT_RNW_BIT = 12, - - CAT_CE_BIT = 11, - - CAT_CT_SHIFT = 8, - CAT_CT_VAL_SYNC = 0, - CAT_CT_VAL_CONTROL = 1, - CAT_CT_VAL_ASYNC = 2, - CAT_CT_VAL_ISOC = 3, - - CAT_CL_SHIFT = 0, - CAT_CL_MASK = DIM2_MASK(6) -}; - -#endif /* DIM2_OS62420_H */ diff --git a/drivers/staging/most/hdm-dim2/dim2_sysfs.c b/drivers/staging/most/hdm-dim2/dim2_sysfs.c deleted file mode 100644 index 3a2ad355cab1..000000000000 --- a/drivers/staging/most/hdm-dim2/dim2_sysfs.c +++ /dev/null @@ -1,109 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * dim2_sysfs.c - MediaLB sysfs information - * - * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG - */ - -/* Author: Andrey Shvetsov */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include "dim2_sysfs.h" - -struct bus_attr { - struct attribute attr; - ssize_t (*show)(struct medialb_bus *bus, char *buf); - ssize_t (*store)(struct medialb_bus *bus, const char *buf, - size_t count); -}; - -static ssize_t state_show(struct medialb_bus *bus, char *buf) -{ - bool state = dim2_sysfs_get_state_cb(); - - return sprintf(buf, "%s\n", state ? "locked" : ""); -} - -static struct bus_attr state_attr = __ATTR_RO(state); - -static struct attribute *bus_default_attrs[] = { - &state_attr.attr, - NULL, -}; - -static const struct attribute_group bus_attr_group = { - .attrs = bus_default_attrs, -}; - -static void bus_kobj_release(struct kobject *kobj) -{ -} - -static ssize_t bus_kobj_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - struct medialb_bus *bus = - container_of(kobj, struct medialb_bus, kobj_group); - struct bus_attr *xattr = container_of(attr, struct bus_attr, attr); - - if (!xattr->show) - return -EIO; - - return xattr->show(bus, buf); -} - -static ssize_t bus_kobj_attr_store(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t count) -{ - struct medialb_bus *bus = - container_of(kobj, struct medialb_bus, kobj_group); - struct bus_attr *xattr = container_of(attr, struct bus_attr, attr); - - if (!xattr->store) - return -EIO; - - return xattr->store(bus, buf, count); -} - -static struct sysfs_ops const bus_kobj_sysfs_ops = { - .show = bus_kobj_attr_show, - .store = bus_kobj_attr_store, -}; - -static struct kobj_type bus_ktype = { - .release = bus_kobj_release, - .sysfs_ops = &bus_kobj_sysfs_ops, -}; - -int dim2_sysfs_probe(struct medialb_bus *bus, struct kobject *parent_kobj) -{ - int err; - - kobject_init(&bus->kobj_group, &bus_ktype); - err = kobject_add(&bus->kobj_group, parent_kobj, "bus"); - if (err) { - pr_err("kobject_add() failed: %d\n", err); - goto err_kobject_add; - } - - err = sysfs_create_group(&bus->kobj_group, &bus_attr_group); - if (err) { - pr_err("sysfs_create_group() failed: %d\n", err); - goto err_create_group; - } - - return 0; - -err_create_group: - kobject_put(&bus->kobj_group); - -err_kobject_add: - return err; -} - -void dim2_sysfs_destroy(struct medialb_bus *bus) -{ - kobject_put(&bus->kobj_group); -} diff --git a/drivers/staging/most/hdm-dim2/dim2_sysfs.h b/drivers/staging/most/hdm-dim2/dim2_sysfs.h deleted file mode 100644 index e46dc4ba3946..000000000000 --- a/drivers/staging/most/hdm-dim2/dim2_sysfs.h +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * dim2_sysfs.h - MediaLB sysfs information - * - * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG - */ - -/* Author: Andrey Shvetsov */ - -#ifndef DIM2_SYSFS_H -#define DIM2_SYSFS_H - -#include - -struct medialb_bus { - struct kobject kobj_group; -}; - -struct dim2_hdm; - -int dim2_sysfs_probe(struct medialb_bus *bus, struct kobject *parent_kobj); -void dim2_sysfs_destroy(struct medialb_bus *bus); - -/* - * callback, - * must deliver MediaLB state as true if locked or false if unlocked - */ -bool dim2_sysfs_get_state_cb(void); - -#endif /* DIM2_SYSFS_H */ -- cgit From 9c7201bfd94e30cfabfa86d31e7b698a1a007d94 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:39 +0100 Subject: staging: most: net: rename module This patch renames the folder and the source file of the networking module. It is needed to clean up the directory layout of the driver. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/Kconfig | 2 +- drivers/staging/most/aim-network/Kconfig | 13 - drivers/staging/most/aim-network/networking.c | 561 -------------------------- drivers/staging/most/net/Kconfig | 13 + drivers/staging/most/net/Makefile | 4 + drivers/staging/most/net/net.c | 561 ++++++++++++++++++++++++++ 6 files changed, 579 insertions(+), 575 deletions(-) delete mode 100644 drivers/staging/most/aim-network/Kconfig delete mode 100644 drivers/staging/most/aim-network/networking.c create mode 100644 drivers/staging/most/net/Kconfig create mode 100644 drivers/staging/most/net/Makefile create mode 100644 drivers/staging/most/net/net.c (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/Kconfig b/drivers/staging/most/Kconfig index 88a415b8d07a..68a9c89db22a 100644 --- a/drivers/staging/most/Kconfig +++ b/drivers/staging/most/Kconfig @@ -19,7 +19,7 @@ if MOST source "drivers/staging/most/cdev/Kconfig" -source "drivers/staging/most/aim-network/Kconfig" +source "drivers/staging/most/net/Kconfig" source "drivers/staging/most/aim-sound/Kconfig" diff --git a/drivers/staging/most/aim-network/Kconfig b/drivers/staging/most/aim-network/Kconfig deleted file mode 100644 index 4c66b24cf73c..000000000000 --- a/drivers/staging/most/aim-network/Kconfig +++ /dev/null @@ -1,13 +0,0 @@ -# -# MOST Networking configuration -# - -config AIM_NETWORK - tristate "Networking AIM" - depends on NET - - ---help--- - Say Y here if you want to commumicate via a networking device. - - To compile this driver as a module, choose M here: the - module will be called aim_network. diff --git a/drivers/staging/most/aim-network/networking.c b/drivers/staging/most/aim-network/networking.c deleted file mode 100644 index 5e082d7d0e82..000000000000 --- a/drivers/staging/most/aim-network/networking.c +++ /dev/null @@ -1,561 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Networking AIM - Networking Application Interface Module for MostCore - * - * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include "most/core.h" - -#define MEP_HDR_LEN 8 -#define MDP_HDR_LEN 16 -#define MAMAC_DATA_LEN (1024 - MDP_HDR_LEN) - -#define PMHL 5 - -#define PMS_TELID_UNSEGM_MAMAC 0x0A -#define PMS_FIFONO_MDP 0x01 -#define PMS_FIFONO_MEP 0x04 -#define PMS_MSGTYPE_DATA 0x04 -#define PMS_DEF_PRIO 0 -#define MEP_DEF_RETRY 15 - -#define PMS_FIFONO_MASK 0x07 -#define PMS_FIFONO_SHIFT 3 -#define PMS_RETRY_SHIFT 4 -#define PMS_TELID_MASK 0x0F -#define PMS_TELID_SHIFT 4 - -#define HB(value) ((u8)((u16)(value) >> 8)) -#define LB(value) ((u8)(value)) - -#define EXTRACT_BIT_SET(bitset_name, value) \ - (((value) >> bitset_name##_SHIFT) & bitset_name##_MASK) - -#define PMS_IS_MEP(buf, len) \ - ((len) > MEP_HDR_LEN && \ - EXTRACT_BIT_SET(PMS_FIFONO, (buf)[3]) == PMS_FIFONO_MEP) - -#define PMS_IS_MAMAC(buf, len) \ - ((len) > MDP_HDR_LEN && \ - EXTRACT_BIT_SET(PMS_FIFONO, (buf)[3]) == PMS_FIFONO_MDP && \ - EXTRACT_BIT_SET(PMS_TELID, (buf)[14]) == PMS_TELID_UNSEGM_MAMAC) - -struct net_dev_channel { - bool linked; - int ch_id; -}; - -struct net_dev_context { - struct most_interface *iface; - bool is_mamac; - struct net_device *dev; - struct net_dev_channel rx; - struct net_dev_channel tx; - struct list_head list; -}; - -static struct list_head net_devices = LIST_HEAD_INIT(net_devices); -static struct mutex probe_disc_mt; /* ch->linked = true, most_nd_open */ -static struct spinlock list_lock; /* list_head, ch->linked = false, dev_hold */ -static struct most_aim aim; - -static int skb_to_mamac(const struct sk_buff *skb, struct mbo *mbo) -{ - u8 *buff = mbo->virt_address; - const u8 broadcast[] = { 0x03, 0xFF }; - const u8 *dest_addr = skb->data + 4; - const u8 *eth_type = skb->data + 12; - unsigned int payload_len = skb->len - ETH_HLEN; - unsigned int mdp_len = payload_len + MDP_HDR_LEN; - - if (mbo->buffer_length < mdp_len) { - pr_err("drop: too small buffer! (%d for %d)\n", - mbo->buffer_length, mdp_len); - return -EINVAL; - } - - if (skb->len < ETH_HLEN) { - pr_err("drop: too small packet! (%d)\n", skb->len); - return -EINVAL; - } - - if (dest_addr[0] == 0xFF && dest_addr[1] == 0xFF) - dest_addr = broadcast; - - *buff++ = HB(mdp_len - 2); - *buff++ = LB(mdp_len - 2); - - *buff++ = PMHL; - *buff++ = (PMS_FIFONO_MDP << PMS_FIFONO_SHIFT) | PMS_MSGTYPE_DATA; - *buff++ = PMS_DEF_PRIO; - *buff++ = dest_addr[0]; - *buff++ = dest_addr[1]; - *buff++ = 0x00; - - *buff++ = HB(payload_len + 6); - *buff++ = LB(payload_len + 6); - - /* end of FPH here */ - - *buff++ = eth_type[0]; - *buff++ = eth_type[1]; - *buff++ = 0; - *buff++ = 0; - - *buff++ = PMS_TELID_UNSEGM_MAMAC << 4 | HB(payload_len); - *buff++ = LB(payload_len); - - memcpy(buff, skb->data + ETH_HLEN, payload_len); - mbo->buffer_length = mdp_len; - return 0; -} - -static int skb_to_mep(const struct sk_buff *skb, struct mbo *mbo) -{ - u8 *buff = mbo->virt_address; - unsigned int mep_len = skb->len + MEP_HDR_LEN; - - if (mbo->buffer_length < mep_len) { - pr_err("drop: too small buffer! (%d for %d)\n", - mbo->buffer_length, mep_len); - return -EINVAL; - } - - *buff++ = HB(mep_len - 2); - *buff++ = LB(mep_len - 2); - - *buff++ = PMHL; - *buff++ = (PMS_FIFONO_MEP << PMS_FIFONO_SHIFT) | PMS_MSGTYPE_DATA; - *buff++ = (MEP_DEF_RETRY << PMS_RETRY_SHIFT) | PMS_DEF_PRIO; - *buff++ = 0; - *buff++ = 0; - *buff++ = 0; - - memcpy(buff, skb->data, skb->len); - mbo->buffer_length = mep_len; - return 0; -} - -static int most_nd_set_mac_address(struct net_device *dev, void *p) -{ - struct net_dev_context *nd = netdev_priv(dev); - int err = eth_mac_addr(dev, p); - - if (err) - return err; - - nd->is_mamac = - (dev->dev_addr[0] == 0 && dev->dev_addr[1] == 0 && - dev->dev_addr[2] == 0 && dev->dev_addr[3] == 0); - - /* - * Set default MTU for the given packet type. - * It is still possible to change MTU using ip tools afterwards. - */ - dev->mtu = nd->is_mamac ? MAMAC_DATA_LEN : ETH_DATA_LEN; - - return 0; -} - -static void on_netinfo(struct most_interface *iface, - unsigned char link_stat, unsigned char *mac_addr); - -static int most_nd_open(struct net_device *dev) -{ - struct net_dev_context *nd = netdev_priv(dev); - int ret = 0; - - mutex_lock(&probe_disc_mt); - - if (most_start_channel(nd->iface, nd->rx.ch_id, &aim)) { - netdev_err(dev, "most_start_channel() failed\n"); - ret = -EBUSY; - goto unlock; - } - - if (most_start_channel(nd->iface, nd->tx.ch_id, &aim)) { - netdev_err(dev, "most_start_channel() failed\n"); - most_stop_channel(nd->iface, nd->rx.ch_id, &aim); - ret = -EBUSY; - goto unlock; - } - - netif_carrier_off(dev); - if (is_valid_ether_addr(dev->dev_addr)) - netif_dormant_off(dev); - else - netif_dormant_on(dev); - netif_wake_queue(dev); - if (nd->iface->request_netinfo) - nd->iface->request_netinfo(nd->iface, nd->tx.ch_id, on_netinfo); - -unlock: - mutex_unlock(&probe_disc_mt); - return ret; -} - -static int most_nd_stop(struct net_device *dev) -{ - struct net_dev_context *nd = netdev_priv(dev); - - netif_stop_queue(dev); - if (nd->iface->request_netinfo) - nd->iface->request_netinfo(nd->iface, nd->tx.ch_id, NULL); - most_stop_channel(nd->iface, nd->rx.ch_id, &aim); - most_stop_channel(nd->iface, nd->tx.ch_id, &aim); - - return 0; -} - -static netdev_tx_t most_nd_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct net_dev_context *nd = netdev_priv(dev); - struct mbo *mbo; - int ret; - - mbo = most_get_mbo(nd->iface, nd->tx.ch_id, &aim); - - if (!mbo) { - netif_stop_queue(dev); - dev->stats.tx_fifo_errors++; - return NETDEV_TX_BUSY; - } - - if (nd->is_mamac) - ret = skb_to_mamac(skb, mbo); - else - ret = skb_to_mep(skb, mbo); - - if (ret) { - most_put_mbo(mbo); - dev->stats.tx_dropped++; - kfree_skb(skb); - return NETDEV_TX_OK; - } - - most_submit_mbo(mbo); - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - kfree_skb(skb); - return NETDEV_TX_OK; -} - -static const struct net_device_ops most_nd_ops = { - .ndo_open = most_nd_open, - .ndo_stop = most_nd_stop, - .ndo_start_xmit = most_nd_start_xmit, - .ndo_set_mac_address = most_nd_set_mac_address, -}; - -static void most_nd_setup(struct net_device *dev) -{ - ether_setup(dev); - dev->netdev_ops = &most_nd_ops; -} - -static struct net_dev_context *get_net_dev(struct most_interface *iface) -{ - struct net_dev_context *nd; - - list_for_each_entry(nd, &net_devices, list) - if (nd->iface == iface) - return nd; - return NULL; -} - -static struct net_dev_context *get_net_dev_hold(struct most_interface *iface) -{ - struct net_dev_context *nd; - unsigned long flags; - - spin_lock_irqsave(&list_lock, flags); - nd = get_net_dev(iface); - if (nd && nd->rx.linked && nd->tx.linked) - dev_hold(nd->dev); - else - nd = NULL; - spin_unlock_irqrestore(&list_lock, flags); - return nd; -} - -static int aim_probe_channel(struct most_interface *iface, int channel_idx, - struct most_channel_config *ccfg, - struct kobject *parent, char *name) -{ - struct net_dev_context *nd; - struct net_dev_channel *ch; - struct net_device *dev; - unsigned long flags; - int ret = 0; - - if (!iface) - return -EINVAL; - - if (ccfg->data_type != MOST_CH_ASYNC) - return -EINVAL; - - mutex_lock(&probe_disc_mt); - nd = get_net_dev(iface); - if (!nd) { - dev = alloc_netdev(sizeof(struct net_dev_context), "meth%d", - NET_NAME_UNKNOWN, most_nd_setup); - if (!dev) { - ret = -ENOMEM; - goto unlock; - } - - nd = netdev_priv(dev); - nd->iface = iface; - nd->dev = dev; - - spin_lock_irqsave(&list_lock, flags); - list_add(&nd->list, &net_devices); - spin_unlock_irqrestore(&list_lock, flags); - - ch = ccfg->direction == MOST_CH_TX ? &nd->tx : &nd->rx; - } else { - ch = ccfg->direction == MOST_CH_TX ? &nd->tx : &nd->rx; - if (ch->linked) { - pr_err("direction is allocated\n"); - ret = -EINVAL; - goto unlock; - } - - if (register_netdev(nd->dev)) { - pr_err("register_netdev() failed\n"); - ret = -EINVAL; - goto unlock; - } - } - ch->ch_id = channel_idx; - ch->linked = true; - -unlock: - mutex_unlock(&probe_disc_mt); - return ret; -} - -static int aim_disconnect_channel(struct most_interface *iface, - int channel_idx) -{ - struct net_dev_context *nd; - struct net_dev_channel *ch; - unsigned long flags; - int ret = 0; - - mutex_lock(&probe_disc_mt); - nd = get_net_dev(iface); - if (!nd) { - ret = -EINVAL; - goto unlock; - } - - if (nd->rx.linked && channel_idx == nd->rx.ch_id) { - ch = &nd->rx; - } else if (nd->tx.linked && channel_idx == nd->tx.ch_id) { - ch = &nd->tx; - } else { - ret = -EINVAL; - goto unlock; - } - - if (nd->rx.linked && nd->tx.linked) { - spin_lock_irqsave(&list_lock, flags); - ch->linked = false; - spin_unlock_irqrestore(&list_lock, flags); - - /* - * do not call most_stop_channel() here, because channels are - * going to be closed in ndo_stop() after unregister_netdev() - */ - unregister_netdev(nd->dev); - } else { - spin_lock_irqsave(&list_lock, flags); - list_del(&nd->list); - spin_unlock_irqrestore(&list_lock, flags); - - free_netdev(nd->dev); - } - -unlock: - mutex_unlock(&probe_disc_mt); - return ret; -} - -static int aim_resume_tx_channel(struct most_interface *iface, - int channel_idx) -{ - struct net_dev_context *nd; - - nd = get_net_dev_hold(iface); - if (!nd) - return 0; - - if (nd->tx.ch_id != channel_idx) - goto put_nd; - - netif_wake_queue(nd->dev); - -put_nd: - dev_put(nd->dev); - return 0; -} - -static int aim_rx_data(struct mbo *mbo) -{ - const u32 zero = 0; - struct net_dev_context *nd; - char *buf = mbo->virt_address; - u32 len = mbo->processed_length; - struct sk_buff *skb; - struct net_device *dev; - unsigned int skb_len; - int ret = 0; - - nd = get_net_dev_hold(mbo->ifp); - if (!nd) - return -EIO; - - if (nd->rx.ch_id != mbo->hdm_channel_id) { - ret = -EIO; - goto put_nd; - } - - dev = nd->dev; - - if (nd->is_mamac) { - if (!PMS_IS_MAMAC(buf, len)) { - ret = -EIO; - goto put_nd; - } - - skb = dev_alloc_skb(len - MDP_HDR_LEN + 2 * ETH_ALEN + 2); - } else { - if (!PMS_IS_MEP(buf, len)) { - ret = -EIO; - goto put_nd; - } - - skb = dev_alloc_skb(len - MEP_HDR_LEN); - } - - if (!skb) { - dev->stats.rx_dropped++; - pr_err_once("drop packet: no memory for skb\n"); - goto out; - } - - skb->dev = dev; - - if (nd->is_mamac) { - /* dest */ - ether_addr_copy(skb_put(skb, ETH_ALEN), dev->dev_addr); - - /* src */ - skb_put_data(skb, &zero, 4); - skb_put_data(skb, buf + 5, 2); - - /* eth type */ - skb_put_data(skb, buf + 10, 2); - - buf += MDP_HDR_LEN; - len -= MDP_HDR_LEN; - } else { - buf += MEP_HDR_LEN; - len -= MEP_HDR_LEN; - } - - skb_put_data(skb, buf, len); - skb->protocol = eth_type_trans(skb, dev); - skb_len = skb->len; - if (netif_rx(skb) == NET_RX_SUCCESS) { - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb_len; - } else { - dev->stats.rx_dropped++; - } - -out: - most_put_mbo(mbo); - -put_nd: - dev_put(nd->dev); - return ret; -} - -static struct most_aim aim = { - .name = "networking", - .probe_channel = aim_probe_channel, - .disconnect_channel = aim_disconnect_channel, - .tx_completion = aim_resume_tx_channel, - .rx_completion = aim_rx_data, -}; - -static int __init most_net_init(void) -{ - spin_lock_init(&list_lock); - mutex_init(&probe_disc_mt); - return most_register_aim(&aim); -} - -static void __exit most_net_exit(void) -{ - most_deregister_aim(&aim); -} - -/** - * on_netinfo - callback for HDM to be informed about HW's MAC - * @param iface - most interface instance - * @param link_stat - link status - * @param mac_addr - MAC address - */ -static void on_netinfo(struct most_interface *iface, - unsigned char link_stat, unsigned char *mac_addr) -{ - struct net_dev_context *nd; - struct net_device *dev; - const u8 *m = mac_addr; - - nd = get_net_dev_hold(iface); - if (!nd) - return; - - dev = nd->dev; - - if (link_stat) - netif_carrier_on(dev); - else - netif_carrier_off(dev); - - if (m && is_valid_ether_addr(m)) { - if (!is_valid_ether_addr(dev->dev_addr)) { - netdev_info(dev, "set mac %02x-%02x-%02x-%02x-%02x-%02x\n", - m[0], m[1], m[2], m[3], m[4], m[5]); - ether_addr_copy(dev->dev_addr, m); - netif_dormant_off(dev); - } else if (!ether_addr_equal(dev->dev_addr, m)) { - netdev_warn(dev, "reject mac %02x-%02x-%02x-%02x-%02x-%02x\n", - m[0], m[1], m[2], m[3], m[4], m[5]); - } - } - - dev_put(nd->dev); -} - -module_init(most_net_init); -module_exit(most_net_exit); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Andrey Shvetsov "); -MODULE_DESCRIPTION("Networking Application Interface Module for MostCore"); diff --git a/drivers/staging/most/net/Kconfig b/drivers/staging/most/net/Kconfig new file mode 100644 index 000000000000..795330ba94ef --- /dev/null +++ b/drivers/staging/most/net/Kconfig @@ -0,0 +1,13 @@ +# +# MOST Networking configuration +# + +config MOST_NET + tristate "Net" + depends on NET + + ---help--- + Say Y here if you want to commumicate via a networking device. + + To compile this driver as a module, choose M here: the + module will be called most_net. diff --git a/drivers/staging/most/net/Makefile b/drivers/staging/most/net/Makefile new file mode 100644 index 000000000000..54500aa77be8 --- /dev/null +++ b/drivers/staging/most/net/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_MOST_NET) += most_net.o + +most_net-objs := net.o +ccflags-y += -Idrivers/staging/ diff --git a/drivers/staging/most/net/net.c b/drivers/staging/most/net/net.c new file mode 100644 index 000000000000..3cbd403aa516 --- /dev/null +++ b/drivers/staging/most/net/net.c @@ -0,0 +1,561 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Networking AIM - Networking Application Interface Module for MostCore + * + * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include "most/core.h" + +#define MEP_HDR_LEN 8 +#define MDP_HDR_LEN 16 +#define MAMAC_DATA_LEN (1024 - MDP_HDR_LEN) + +#define PMHL 5 + +#define PMS_TELID_UNSEGM_MAMAC 0x0A +#define PMS_FIFONO_MDP 0x01 +#define PMS_FIFONO_MEP 0x04 +#define PMS_MSGTYPE_DATA 0x04 +#define PMS_DEF_PRIO 0 +#define MEP_DEF_RETRY 15 + +#define PMS_FIFONO_MASK 0x07 +#define PMS_FIFONO_SHIFT 3 +#define PMS_RETRY_SHIFT 4 +#define PMS_TELID_MASK 0x0F +#define PMS_TELID_SHIFT 4 + +#define HB(value) ((u8)((u16)(value) >> 8)) +#define LB(value) ((u8)(value)) + +#define EXTRACT_BIT_SET(bitset_name, value) \ + (((value) >> bitset_name##_SHIFT) & bitset_name##_MASK) + +#define PMS_IS_MEP(buf, len) \ + ((len) > MEP_HDR_LEN && \ + EXTRACT_BIT_SET(PMS_FIFONO, (buf)[3]) == PMS_FIFONO_MEP) + +#define PMS_IS_MAMAC(buf, len) \ + ((len) > MDP_HDR_LEN && \ + EXTRACT_BIT_SET(PMS_FIFONO, (buf)[3]) == PMS_FIFONO_MDP && \ + EXTRACT_BIT_SET(PMS_TELID, (buf)[14]) == PMS_TELID_UNSEGM_MAMAC) + +struct net_dev_channel { + bool linked; + int ch_id; +}; + +struct net_dev_context { + struct most_interface *iface; + bool is_mamac; + struct net_device *dev; + struct net_dev_channel rx; + struct net_dev_channel tx; + struct list_head list; +}; + +static struct list_head net_devices = LIST_HEAD_INIT(net_devices); +static struct mutex probe_disc_mt; /* ch->linked = true, most_nd_open */ +static struct spinlock list_lock; /* list_head, ch->linked = false, dev_hold */ +static struct most_aim aim; + +static int skb_to_mamac(const struct sk_buff *skb, struct mbo *mbo) +{ + u8 *buff = mbo->virt_address; + const u8 broadcast[] = { 0x03, 0xFF }; + const u8 *dest_addr = skb->data + 4; + const u8 *eth_type = skb->data + 12; + unsigned int payload_len = skb->len - ETH_HLEN; + unsigned int mdp_len = payload_len + MDP_HDR_LEN; + + if (mbo->buffer_length < mdp_len) { + pr_err("drop: too small buffer! (%d for %d)\n", + mbo->buffer_length, mdp_len); + return -EINVAL; + } + + if (skb->len < ETH_HLEN) { + pr_err("drop: too small packet! (%d)\n", skb->len); + return -EINVAL; + } + + if (dest_addr[0] == 0xFF && dest_addr[1] == 0xFF) + dest_addr = broadcast; + + *buff++ = HB(mdp_len - 2); + *buff++ = LB(mdp_len - 2); + + *buff++ = PMHL; + *buff++ = (PMS_FIFONO_MDP << PMS_FIFONO_SHIFT) | PMS_MSGTYPE_DATA; + *buff++ = PMS_DEF_PRIO; + *buff++ = dest_addr[0]; + *buff++ = dest_addr[1]; + *buff++ = 0x00; + + *buff++ = HB(payload_len + 6); + *buff++ = LB(payload_len + 6); + + /* end of FPH here */ + + *buff++ = eth_type[0]; + *buff++ = eth_type[1]; + *buff++ = 0; + *buff++ = 0; + + *buff++ = PMS_TELID_UNSEGM_MAMAC << 4 | HB(payload_len); + *buff++ = LB(payload_len); + + memcpy(buff, skb->data + ETH_HLEN, payload_len); + mbo->buffer_length = mdp_len; + return 0; +} + +static int skb_to_mep(const struct sk_buff *skb, struct mbo *mbo) +{ + u8 *buff = mbo->virt_address; + unsigned int mep_len = skb->len + MEP_HDR_LEN; + + if (mbo->buffer_length < mep_len) { + pr_err("drop: too small buffer! (%d for %d)\n", + mbo->buffer_length, mep_len); + return -EINVAL; + } + + *buff++ = HB(mep_len - 2); + *buff++ = LB(mep_len - 2); + + *buff++ = PMHL; + *buff++ = (PMS_FIFONO_MEP << PMS_FIFONO_SHIFT) | PMS_MSGTYPE_DATA; + *buff++ = (MEP_DEF_RETRY << PMS_RETRY_SHIFT) | PMS_DEF_PRIO; + *buff++ = 0; + *buff++ = 0; + *buff++ = 0; + + memcpy(buff, skb->data, skb->len); + mbo->buffer_length = mep_len; + return 0; +} + +static int most_nd_set_mac_address(struct net_device *dev, void *p) +{ + struct net_dev_context *nd = netdev_priv(dev); + int err = eth_mac_addr(dev, p); + + if (err) + return err; + + nd->is_mamac = + (dev->dev_addr[0] == 0 && dev->dev_addr[1] == 0 && + dev->dev_addr[2] == 0 && dev->dev_addr[3] == 0); + + /* + * Set default MTU for the given packet type. + * It is still possible to change MTU using ip tools afterwards. + */ + dev->mtu = nd->is_mamac ? MAMAC_DATA_LEN : ETH_DATA_LEN; + + return 0; +} + +static void on_netinfo(struct most_interface *iface, + unsigned char link_stat, unsigned char *mac_addr); + +static int most_nd_open(struct net_device *dev) +{ + struct net_dev_context *nd = netdev_priv(dev); + int ret = 0; + + mutex_lock(&probe_disc_mt); + + if (most_start_channel(nd->iface, nd->rx.ch_id, &aim)) { + netdev_err(dev, "most_start_channel() failed\n"); + ret = -EBUSY; + goto unlock; + } + + if (most_start_channel(nd->iface, nd->tx.ch_id, &aim)) { + netdev_err(dev, "most_start_channel() failed\n"); + most_stop_channel(nd->iface, nd->rx.ch_id, &aim); + ret = -EBUSY; + goto unlock; + } + + netif_carrier_off(dev); + if (is_valid_ether_addr(dev->dev_addr)) + netif_dormant_off(dev); + else + netif_dormant_on(dev); + netif_wake_queue(dev); + if (nd->iface->request_netinfo) + nd->iface->request_netinfo(nd->iface, nd->tx.ch_id, on_netinfo); + +unlock: + mutex_unlock(&probe_disc_mt); + return ret; +} + +static int most_nd_stop(struct net_device *dev) +{ + struct net_dev_context *nd = netdev_priv(dev); + + netif_stop_queue(dev); + if (nd->iface->request_netinfo) + nd->iface->request_netinfo(nd->iface, nd->tx.ch_id, NULL); + most_stop_channel(nd->iface, nd->rx.ch_id, &aim); + most_stop_channel(nd->iface, nd->tx.ch_id, &aim); + + return 0; +} + +static netdev_tx_t most_nd_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct net_dev_context *nd = netdev_priv(dev); + struct mbo *mbo; + int ret; + + mbo = most_get_mbo(nd->iface, nd->tx.ch_id, &aim); + + if (!mbo) { + netif_stop_queue(dev); + dev->stats.tx_fifo_errors++; + return NETDEV_TX_BUSY; + } + + if (nd->is_mamac) + ret = skb_to_mamac(skb, mbo); + else + ret = skb_to_mep(skb, mbo); + + if (ret) { + most_put_mbo(mbo); + dev->stats.tx_dropped++; + kfree_skb(skb); + return NETDEV_TX_OK; + } + + most_submit_mbo(mbo); + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + kfree_skb(skb); + return NETDEV_TX_OK; +} + +static const struct net_device_ops most_nd_ops = { + .ndo_open = most_nd_open, + .ndo_stop = most_nd_stop, + .ndo_start_xmit = most_nd_start_xmit, + .ndo_set_mac_address = most_nd_set_mac_address, +}; + +static void most_nd_setup(struct net_device *dev) +{ + ether_setup(dev); + dev->netdev_ops = &most_nd_ops; +} + +static struct net_dev_context *get_net_dev(struct most_interface *iface) +{ + struct net_dev_context *nd; + + list_for_each_entry(nd, &net_devices, list) + if (nd->iface == iface) + return nd; + return NULL; +} + +static struct net_dev_context *get_net_dev_hold(struct most_interface *iface) +{ + struct net_dev_context *nd; + unsigned long flags; + + spin_lock_irqsave(&list_lock, flags); + nd = get_net_dev(iface); + if (nd && nd->rx.linked && nd->tx.linked) + dev_hold(nd->dev); + else + nd = NULL; + spin_unlock_irqrestore(&list_lock, flags); + return nd; +} + +static int aim_probe_channel(struct most_interface *iface, int channel_idx, + struct most_channel_config *ccfg, + struct kobject *parent, char *name) +{ + struct net_dev_context *nd; + struct net_dev_channel *ch; + struct net_device *dev; + unsigned long flags; + int ret = 0; + + if (!iface) + return -EINVAL; + + if (ccfg->data_type != MOST_CH_ASYNC) + return -EINVAL; + + mutex_lock(&probe_disc_mt); + nd = get_net_dev(iface); + if (!nd) { + dev = alloc_netdev(sizeof(struct net_dev_context), "meth%d", + NET_NAME_UNKNOWN, most_nd_setup); + if (!dev) { + ret = -ENOMEM; + goto unlock; + } + + nd = netdev_priv(dev); + nd->iface = iface; + nd->dev = dev; + + spin_lock_irqsave(&list_lock, flags); + list_add(&nd->list, &net_devices); + spin_unlock_irqrestore(&list_lock, flags); + + ch = ccfg->direction == MOST_CH_TX ? &nd->tx : &nd->rx; + } else { + ch = ccfg->direction == MOST_CH_TX ? &nd->tx : &nd->rx; + if (ch->linked) { + pr_err("direction is allocated\n"); + ret = -EINVAL; + goto unlock; + } + + if (register_netdev(nd->dev)) { + pr_err("register_netdev() failed\n"); + ret = -EINVAL; + goto unlock; + } + } + ch->ch_id = channel_idx; + ch->linked = true; + +unlock: + mutex_unlock(&probe_disc_mt); + return ret; +} + +static int aim_disconnect_channel(struct most_interface *iface, + int channel_idx) +{ + struct net_dev_context *nd; + struct net_dev_channel *ch; + unsigned long flags; + int ret = 0; + + mutex_lock(&probe_disc_mt); + nd = get_net_dev(iface); + if (!nd) { + ret = -EINVAL; + goto unlock; + } + + if (nd->rx.linked && channel_idx == nd->rx.ch_id) { + ch = &nd->rx; + } else if (nd->tx.linked && channel_idx == nd->tx.ch_id) { + ch = &nd->tx; + } else { + ret = -EINVAL; + goto unlock; + } + + if (nd->rx.linked && nd->tx.linked) { + spin_lock_irqsave(&list_lock, flags); + ch->linked = false; + spin_unlock_irqrestore(&list_lock, flags); + + /* + * do not call most_stop_channel() here, because channels are + * going to be closed in ndo_stop() after unregister_netdev() + */ + unregister_netdev(nd->dev); + } else { + spin_lock_irqsave(&list_lock, flags); + list_del(&nd->list); + spin_unlock_irqrestore(&list_lock, flags); + + free_netdev(nd->dev); + } + +unlock: + mutex_unlock(&probe_disc_mt); + return ret; +} + +static int aim_resume_tx_channel(struct most_interface *iface, + int channel_idx) +{ + struct net_dev_context *nd; + + nd = get_net_dev_hold(iface); + if (!nd) + return 0; + + if (nd->tx.ch_id != channel_idx) + goto put_nd; + + netif_wake_queue(nd->dev); + +put_nd: + dev_put(nd->dev); + return 0; +} + +static int aim_rx_data(struct mbo *mbo) +{ + const u32 zero = 0; + struct net_dev_context *nd; + char *buf = mbo->virt_address; + u32 len = mbo->processed_length; + struct sk_buff *skb; + struct net_device *dev; + unsigned int skb_len; + int ret = 0; + + nd = get_net_dev_hold(mbo->ifp); + if (!nd) + return -EIO; + + if (nd->rx.ch_id != mbo->hdm_channel_id) { + ret = -EIO; + goto put_nd; + } + + dev = nd->dev; + + if (nd->is_mamac) { + if (!PMS_IS_MAMAC(buf, len)) { + ret = -EIO; + goto put_nd; + } + + skb = dev_alloc_skb(len - MDP_HDR_LEN + 2 * ETH_ALEN + 2); + } else { + if (!PMS_IS_MEP(buf, len)) { + ret = -EIO; + goto put_nd; + } + + skb = dev_alloc_skb(len - MEP_HDR_LEN); + } + + if (!skb) { + dev->stats.rx_dropped++; + pr_err_once("drop packet: no memory for skb\n"); + goto out; + } + + skb->dev = dev; + + if (nd->is_mamac) { + /* dest */ + ether_addr_copy(skb_put(skb, ETH_ALEN), dev->dev_addr); + + /* src */ + skb_put_data(skb, &zero, 4); + skb_put_data(skb, buf + 5, 2); + + /* eth type */ + skb_put_data(skb, buf + 10, 2); + + buf += MDP_HDR_LEN; + len -= MDP_HDR_LEN; + } else { + buf += MEP_HDR_LEN; + len -= MEP_HDR_LEN; + } + + skb_put_data(skb, buf, len); + skb->protocol = eth_type_trans(skb, dev); + skb_len = skb->len; + if (netif_rx(skb) == NET_RX_SUCCESS) { + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb_len; + } else { + dev->stats.rx_dropped++; + } + +out: + most_put_mbo(mbo); + +put_nd: + dev_put(nd->dev); + return ret; +} + +static struct most_aim aim = { + .name = "net", + .probe_channel = aim_probe_channel, + .disconnect_channel = aim_disconnect_channel, + .tx_completion = aim_resume_tx_channel, + .rx_completion = aim_rx_data, +}; + +static int __init most_net_init(void) +{ + spin_lock_init(&list_lock); + mutex_init(&probe_disc_mt); + return most_register_aim(&aim); +} + +static void __exit most_net_exit(void) +{ + most_deregister_aim(&aim); +} + +/** + * on_netinfo - callback for HDM to be informed about HW's MAC + * @param iface - most interface instance + * @param link_stat - link status + * @param mac_addr - MAC address + */ +static void on_netinfo(struct most_interface *iface, + unsigned char link_stat, unsigned char *mac_addr) +{ + struct net_dev_context *nd; + struct net_device *dev; + const u8 *m = mac_addr; + + nd = get_net_dev_hold(iface); + if (!nd) + return; + + dev = nd->dev; + + if (link_stat) + netif_carrier_on(dev); + else + netif_carrier_off(dev); + + if (m && is_valid_ether_addr(m)) { + if (!is_valid_ether_addr(dev->dev_addr)) { + netdev_info(dev, "set mac %02x-%02x-%02x-%02x-%02x-%02x\n", + m[0], m[1], m[2], m[3], m[4], m[5]); + ether_addr_copy(dev->dev_addr, m); + netif_dormant_off(dev); + } else if (!ether_addr_equal(dev->dev_addr, m)) { + netdev_warn(dev, "reject mac %02x-%02x-%02x-%02x-%02x-%02x\n", + m[0], m[1], m[2], m[3], m[4], m[5]); + } + } + + dev_put(nd->dev); +} + +module_init(most_net_init); +module_exit(most_net_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Andrey Shvetsov "); +MODULE_DESCRIPTION("Networking Application Interface Module for MostCore"); -- cgit From 88c975497da8a16e07dc6ac2e5d51b187014fe42 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:40 +0100 Subject: staging: most: sound: rename module This patch renames the folder of the sound module. It is needed to clean up the directory layout of the driver. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/Kconfig | 2 +- drivers/staging/most/aim-sound/Kconfig | 13 - drivers/staging/most/aim-sound/sound.c | 759 --------------------------------- drivers/staging/most/sound/Kconfig | 13 + drivers/staging/most/sound/Makefile | 4 + drivers/staging/most/sound/sound.c | 759 +++++++++++++++++++++++++++++++++ 6 files changed, 777 insertions(+), 773 deletions(-) delete mode 100644 drivers/staging/most/aim-sound/Kconfig delete mode 100644 drivers/staging/most/aim-sound/sound.c create mode 100644 drivers/staging/most/sound/Kconfig create mode 100644 drivers/staging/most/sound/Makefile create mode 100644 drivers/staging/most/sound/sound.c (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/Kconfig b/drivers/staging/most/Kconfig index 68a9c89db22a..3f127116476c 100644 --- a/drivers/staging/most/Kconfig +++ b/drivers/staging/most/Kconfig @@ -21,7 +21,7 @@ source "drivers/staging/most/cdev/Kconfig" source "drivers/staging/most/net/Kconfig" -source "drivers/staging/most/aim-sound/Kconfig" +source "drivers/staging/most/sound/Kconfig" source "drivers/staging/most/aim-v4l2/Kconfig" diff --git a/drivers/staging/most/aim-sound/Kconfig b/drivers/staging/most/aim-sound/Kconfig deleted file mode 100644 index 3194c219ff14..000000000000 --- a/drivers/staging/most/aim-sound/Kconfig +++ /dev/null @@ -1,13 +0,0 @@ -# -# MOST ALSA configuration -# - -config AIM_SOUND - tristate "ALSA AIM" - depends on SND - select SND_PCM - ---help--- - Say Y here if you want to commumicate via ALSA/sound devices. - - To compile this driver as a module, choose M here: the - module will be called aim_sound. diff --git a/drivers/staging/most/aim-sound/sound.c b/drivers/staging/most/aim-sound/sound.c deleted file mode 100644 index 5826f710c925..000000000000 --- a/drivers/staging/most/aim-sound/sound.c +++ /dev/null @@ -1,759 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * sound.c - Audio Application Interface Module for Mostcore - * - * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRIVER_NAME "sound" - -static struct list_head dev_list; -static struct most_aim audio_aim; - -/** - * struct channel - private structure to keep channel specific data - * @substream: stores the substream structure - * @iface: interface for which the channel belongs to - * @cfg: channel configuration - * @card: registered sound card - * @list: list for private use - * @id: channel index - * @period_pos: current period position (ring buffer) - * @buffer_pos: current buffer position (ring buffer) - * @is_stream_running: identifies whether a stream is running or not - * @opened: set when the stream is opened - * @playback_task: playback thread - * @playback_waitq: waitq used by playback thread - */ -struct channel { - struct snd_pcm_substream *substream; - struct snd_pcm_hardware pcm_hardware; - struct most_interface *iface; - struct most_channel_config *cfg; - struct snd_card *card; - struct list_head list; - int id; - unsigned int period_pos; - unsigned int buffer_pos; - bool is_stream_running; - - struct task_struct *playback_task; - wait_queue_head_t playback_waitq; - - void (*copy_fn)(void *alsa, void *most, unsigned int bytes); -}; - -#define MOST_PCM_INFO (SNDRV_PCM_INFO_MMAP | \ - SNDRV_PCM_INFO_MMAP_VALID | \ - SNDRV_PCM_INFO_BATCH | \ - SNDRV_PCM_INFO_INTERLEAVED | \ - SNDRV_PCM_INFO_BLOCK_TRANSFER) - -#define swap16(val) ( \ - (((u16)(val) << 8) & (u16)0xFF00) | \ - (((u16)(val) >> 8) & (u16)0x00FF)) - -#define swap32(val) ( \ - (((u32)(val) << 24) & (u32)0xFF000000) | \ - (((u32)(val) << 8) & (u32)0x00FF0000) | \ - (((u32)(val) >> 8) & (u32)0x0000FF00) | \ - (((u32)(val) >> 24) & (u32)0x000000FF)) - -static void swap_copy16(u16 *dest, const u16 *source, unsigned int bytes) -{ - unsigned int i = 0; - - while (i < (bytes / 2)) { - dest[i] = swap16(source[i]); - i++; - } -} - -static void swap_copy24(u8 *dest, const u8 *source, unsigned int bytes) -{ - unsigned int i = 0; - - while (i < bytes - 2) { - dest[i] = source[i + 2]; - dest[i + 1] = source[i + 1]; - dest[i + 2] = source[i]; - i += 3; - } -} - -static void swap_copy32(u32 *dest, const u32 *source, unsigned int bytes) -{ - unsigned int i = 0; - - while (i < bytes / 4) { - dest[i] = swap32(source[i]); - i++; - } -} - -static void alsa_to_most_memcpy(void *alsa, void *most, unsigned int bytes) -{ - memcpy(most, alsa, bytes); -} - -static void alsa_to_most_copy16(void *alsa, void *most, unsigned int bytes) -{ - swap_copy16(most, alsa, bytes); -} - -static void alsa_to_most_copy24(void *alsa, void *most, unsigned int bytes) -{ - swap_copy24(most, alsa, bytes); -} - -static void alsa_to_most_copy32(void *alsa, void *most, unsigned int bytes) -{ - swap_copy32(most, alsa, bytes); -} - -static void most_to_alsa_memcpy(void *alsa, void *most, unsigned int bytes) -{ - memcpy(alsa, most, bytes); -} - -static void most_to_alsa_copy16(void *alsa, void *most, unsigned int bytes) -{ - swap_copy16(alsa, most, bytes); -} - -static void most_to_alsa_copy24(void *alsa, void *most, unsigned int bytes) -{ - swap_copy24(alsa, most, bytes); -} - -static void most_to_alsa_copy32(void *alsa, void *most, unsigned int bytes) -{ - swap_copy32(alsa, most, bytes); -} - -/** - * get_channel - get pointer to channel - * @iface: interface structure - * @channel_id: channel ID - * - * This traverses the channel list and returns the channel matching the - * ID and interface. - * - * Returns pointer to channel on success or NULL otherwise. - */ -static struct channel *get_channel(struct most_interface *iface, - int channel_id) -{ - struct channel *channel, *tmp; - - list_for_each_entry_safe(channel, tmp, &dev_list, list) { - if ((channel->iface == iface) && (channel->id == channel_id)) - return channel; - } - - return NULL; -} - -/** - * copy_data - implements data copying function - * @channel: channel - * @mbo: MBO from core - * - * Copy data from/to ring buffer to/from MBO and update the buffer position - */ -static bool copy_data(struct channel *channel, struct mbo *mbo) -{ - struct snd_pcm_runtime *const runtime = channel->substream->runtime; - unsigned int const frame_bytes = channel->cfg->subbuffer_size; - unsigned int const buffer_size = runtime->buffer_size; - unsigned int frames; - unsigned int fr0; - - if (channel->cfg->direction & MOST_CH_RX) - frames = mbo->processed_length / frame_bytes; - else - frames = mbo->buffer_length / frame_bytes; - fr0 = min(buffer_size - channel->buffer_pos, frames); - - channel->copy_fn(runtime->dma_area + channel->buffer_pos * frame_bytes, - mbo->virt_address, - fr0 * frame_bytes); - - if (frames > fr0) { - /* wrap around at end of ring buffer */ - channel->copy_fn(runtime->dma_area, - mbo->virt_address + fr0 * frame_bytes, - (frames - fr0) * frame_bytes); - } - - channel->buffer_pos += frames; - if (channel->buffer_pos >= buffer_size) - channel->buffer_pos -= buffer_size; - channel->period_pos += frames; - if (channel->period_pos >= runtime->period_size) { - channel->period_pos -= runtime->period_size; - return true; - } - - return false; -} - -/** - * playback_thread - function implements the playback thread - * @data: private data - * - * Thread which does the playback functionality in a loop. It waits for a free - * MBO from mostcore for a particular channel and copy the data from ring buffer - * to MBO. Submit the MBO back to mostcore, after copying the data. - * - * Returns 0 on success or error code otherwise. - */ -static int playback_thread(void *data) -{ - struct channel *const channel = data; - - while (!kthread_should_stop()) { - struct mbo *mbo = NULL; - bool period_elapsed = false; - - wait_event_interruptible( - channel->playback_waitq, - kthread_should_stop() || - (channel->is_stream_running && - (mbo = most_get_mbo(channel->iface, channel->id, - &audio_aim)))); - if (!mbo) - continue; - - if (channel->is_stream_running) - period_elapsed = copy_data(channel, mbo); - else - memset(mbo->virt_address, 0, mbo->buffer_length); - - most_submit_mbo(mbo); - if (period_elapsed) - snd_pcm_period_elapsed(channel->substream); - } - - return 0; -} - -/** - * pcm_open - implements open callback function for PCM middle layer - * @substream: pointer to ALSA PCM substream - * - * This is called when a PCM substream is opened. At least, the function should - * initialize the runtime->hw record. - * - * Returns 0 on success or error code otherwise. - */ -static int pcm_open(struct snd_pcm_substream *substream) -{ - struct channel *channel = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - struct most_channel_config *cfg = channel->cfg; - - channel->substream = substream; - - if (cfg->direction == MOST_CH_TX) { - channel->playback_task = kthread_run(playback_thread, channel, - "most_audio_playback"); - if (IS_ERR(channel->playback_task)) { - pr_err("Couldn't start thread\n"); - return PTR_ERR(channel->playback_task); - } - } - - if (most_start_channel(channel->iface, channel->id, &audio_aim)) { - pr_err("most_start_channel() failed!\n"); - if (cfg->direction == MOST_CH_TX) - kthread_stop(channel->playback_task); - return -EBUSY; - } - - runtime->hw = channel->pcm_hardware; - return 0; -} - -/** - * pcm_close - implements close callback function for PCM middle layer - * @substream: sub-stream pointer - * - * Obviously, this is called when a PCM substream is closed. Any private - * instance for a PCM substream allocated in the open callback will be - * released here. - * - * Returns 0 on success or error code otherwise. - */ -static int pcm_close(struct snd_pcm_substream *substream) -{ - struct channel *channel = substream->private_data; - - if (channel->cfg->direction == MOST_CH_TX) - kthread_stop(channel->playback_task); - most_stop_channel(channel->iface, channel->id, &audio_aim); - - return 0; -} - -/** - * pcm_hw_params - implements hw_params callback function for PCM middle layer - * @substream: sub-stream pointer - * @hw_params: contains the hardware parameters set by the application - * - * This is called when the hardware parameters is set by the application, that - * is, once when the buffer size, the period size, the format, etc. are defined - * for the PCM substream. Many hardware setups should be done is this callback, - * including the allocation of buffers. - * - * Returns 0 on success or error code otherwise. - */ -static int pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct channel *channel = substream->private_data; - - if ((params_channels(hw_params) > channel->pcm_hardware.channels_max) || - (params_channels(hw_params) < channel->pcm_hardware.channels_min)) { - pr_err("Requested number of channels not supported.\n"); - return -EINVAL; - } - return snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); -} - -/** - * pcm_hw_free - implements hw_free callback function for PCM middle layer - * @substream: substream pointer - * - * This is called to release the resources allocated via hw_params. - * This function will be always called before the close callback is called. - * - * Returns 0 on success or error code otherwise. - */ -static int pcm_hw_free(struct snd_pcm_substream *substream) -{ - return snd_pcm_lib_free_vmalloc_buffer(substream); -} - -/** - * pcm_prepare - implements prepare callback function for PCM middle layer - * @substream: substream pointer - * - * This callback is called when the PCM is "prepared". Format rate, sample rate, - * etc., can be set here. This callback can be called many times at each setup. - * - * Returns 0 on success or error code otherwise. - */ -static int pcm_prepare(struct snd_pcm_substream *substream) -{ - struct channel *channel = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - struct most_channel_config *cfg = channel->cfg; - int width = snd_pcm_format_physical_width(runtime->format); - - channel->copy_fn = NULL; - - if (cfg->direction == MOST_CH_TX) { - if (snd_pcm_format_big_endian(runtime->format) || width == 8) - channel->copy_fn = alsa_to_most_memcpy; - else if (width == 16) - channel->copy_fn = alsa_to_most_copy16; - else if (width == 24) - channel->copy_fn = alsa_to_most_copy24; - else if (width == 32) - channel->copy_fn = alsa_to_most_copy32; - } else { - if (snd_pcm_format_big_endian(runtime->format) || width == 8) - channel->copy_fn = most_to_alsa_memcpy; - else if (width == 16) - channel->copy_fn = most_to_alsa_copy16; - else if (width == 24) - channel->copy_fn = most_to_alsa_copy24; - else if (width == 32) - channel->copy_fn = most_to_alsa_copy32; - } - - if (!channel->copy_fn) { - pr_err("unsupported format\n"); - return -EINVAL; - } - - channel->period_pos = 0; - channel->buffer_pos = 0; - - return 0; -} - -/** - * pcm_trigger - implements trigger callback function for PCM middle layer - * @substream: substream pointer - * @cmd: action to perform - * - * This is called when the PCM is started, stopped or paused. The action will be - * specified in the second argument, SNDRV_PCM_TRIGGER_XXX - * - * Returns 0 on success or error code otherwise. - */ -static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct channel *channel = substream->private_data; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - channel->is_stream_running = true; - wake_up_interruptible(&channel->playback_waitq); - return 0; - - case SNDRV_PCM_TRIGGER_STOP: - channel->is_stream_running = false; - return 0; - - default: - pr_info("%s(), invalid\n", __func__); - return -EINVAL; - } - return 0; -} - -/** - * pcm_pointer - implements pointer callback function for PCM middle layer - * @substream: substream pointer - * - * This callback is called when the PCM middle layer inquires the current - * hardware position on the buffer. The position must be returned in frames, - * ranging from 0 to buffer_size-1. - */ -static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream) -{ - struct channel *channel = substream->private_data; - - return channel->buffer_pos; -} - -/** - * Initialization of struct snd_pcm_ops - */ -static const struct snd_pcm_ops pcm_ops = { - .open = pcm_open, - .close = pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_hw_params, - .hw_free = pcm_hw_free, - .prepare = pcm_prepare, - .trigger = pcm_trigger, - .pointer = pcm_pointer, - .page = snd_pcm_lib_get_vmalloc_page, - .mmap = snd_pcm_lib_mmap_vmalloc, -}; - -static int split_arg_list(char *buf, char **card_name, char **pcm_format) -{ - *card_name = strsep(&buf, "."); - if (!*card_name) - return -EIO; - *pcm_format = strsep(&buf, ".\n"); - if (!*pcm_format) - return -EIO; - return 0; -} - -static int audio_set_hw_params(struct snd_pcm_hardware *pcm_hw, - char *pcm_format, - struct most_channel_config *cfg) -{ - pcm_hw->info = MOST_PCM_INFO; - pcm_hw->rates = SNDRV_PCM_RATE_48000; - pcm_hw->rate_min = 48000; - pcm_hw->rate_max = 48000; - pcm_hw->buffer_bytes_max = cfg->num_buffers * cfg->buffer_size; - pcm_hw->period_bytes_min = cfg->buffer_size; - pcm_hw->period_bytes_max = cfg->buffer_size; - pcm_hw->periods_min = 1; - pcm_hw->periods_max = cfg->num_buffers; - - if (!strcmp(pcm_format, "1x8")) { - if (cfg->subbuffer_size != 1) - goto error; - pr_info("PCM format is 8-bit mono\n"); - pcm_hw->channels_min = 1; - pcm_hw->channels_max = 1; - pcm_hw->formats = SNDRV_PCM_FMTBIT_S8; - } else if (!strcmp(pcm_format, "2x16")) { - if (cfg->subbuffer_size != 4) - goto error; - pr_info("PCM format is 16-bit stereo\n"); - pcm_hw->channels_min = 2; - pcm_hw->channels_max = 2; - pcm_hw->formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S16_BE; - } else if (!strcmp(pcm_format, "2x24")) { - if (cfg->subbuffer_size != 6) - goto error; - pr_info("PCM format is 24-bit stereo\n"); - pcm_hw->channels_min = 2; - pcm_hw->channels_max = 2; - pcm_hw->formats = SNDRV_PCM_FMTBIT_S24_3LE | - SNDRV_PCM_FMTBIT_S24_3BE; - } else if (!strcmp(pcm_format, "2x32")) { - if (cfg->subbuffer_size != 8) - goto error; - pr_info("PCM format is 32-bit stereo\n"); - pcm_hw->channels_min = 2; - pcm_hw->channels_max = 2; - pcm_hw->formats = SNDRV_PCM_FMTBIT_S32_LE | - SNDRV_PCM_FMTBIT_S32_BE; - } else if (!strcmp(pcm_format, "6x16")) { - if (cfg->subbuffer_size != 12) - goto error; - pr_info("PCM format is 16-bit 5.1 multi channel\n"); - pcm_hw->channels_min = 6; - pcm_hw->channels_max = 6; - pcm_hw->formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S16_BE; - } else { - pr_err("PCM format %s not supported\n", pcm_format); - return -EIO; - } - return 0; -error: - pr_err("Audio resolution doesn't fit subbuffer size\n"); - return -EINVAL; -} - -/** - * audio_probe_channel - probe function of the driver module - * @iface: pointer to interface instance - * @channel_id: channel index/ID - * @cfg: pointer to actual channel configuration - * @parent: pointer to kobject (needed for sysfs hook-up) - * @arg_list: string that provides the name of the device to be created in /dev - * plus the desired audio resolution - * - * Creates sound card, pcm device, sets pcm ops and registers sound card. - * - * Returns 0 on success or error code otherwise. - */ -static int audio_probe_channel(struct most_interface *iface, int channel_id, - struct most_channel_config *cfg, - struct kobject *parent, char *arg_list) -{ - struct channel *channel; - struct snd_card *card; - struct snd_pcm *pcm; - int playback_count = 0; - int capture_count = 0; - int ret; - int direction; - char *card_name; - char *pcm_format; - - if (!iface) - return -EINVAL; - - if (cfg->data_type != MOST_CH_SYNC) { - pr_err("Incompatible channel type\n"); - return -EINVAL; - } - - if (get_channel(iface, channel_id)) { - pr_err("channel (%s:%d) is already linked\n", - iface->description, channel_id); - return -EINVAL; - } - - if (cfg->direction == MOST_CH_TX) { - playback_count = 1; - direction = SNDRV_PCM_STREAM_PLAYBACK; - } else { - capture_count = 1; - direction = SNDRV_PCM_STREAM_CAPTURE; - } - - ret = split_arg_list(arg_list, &card_name, &pcm_format); - if (ret < 0) { - pr_info("PCM format missing\n"); - return ret; - } - - ret = snd_card_new(NULL, -1, card_name, THIS_MODULE, - sizeof(*channel), &card); - if (ret < 0) - return ret; - - channel = card->private_data; - channel->card = card; - channel->cfg = cfg; - channel->iface = iface; - channel->id = channel_id; - init_waitqueue_head(&channel->playback_waitq); - - ret = audio_set_hw_params(&channel->pcm_hardware, pcm_format, cfg); - if (ret) - goto err_free_card; - - snprintf(card->driver, sizeof(card->driver), "%s", DRIVER_NAME); - snprintf(card->shortname, sizeof(card->shortname), "Microchip MOST:%d", - card->number); - snprintf(card->longname, sizeof(card->longname), "%s at %s, ch %d", - card->shortname, iface->description, channel_id); - - ret = snd_pcm_new(card, card_name, 0, playback_count, - capture_count, &pcm); - if (ret < 0) - goto err_free_card; - - pcm->private_data = channel; - - snd_pcm_set_ops(pcm, direction, &pcm_ops); - - ret = snd_card_register(card); - if (ret < 0) - goto err_free_card; - - list_add_tail(&channel->list, &dev_list); - - return 0; - -err_free_card: - snd_card_free(card); - return ret; -} - -/** - * audio_disconnect_channel - function to disconnect a channel - * @iface: pointer to interface instance - * @channel_id: channel index - * - * This frees allocated memory and removes the sound card from ALSA - * - * Returns 0 on success or error code otherwise. - */ -static int audio_disconnect_channel(struct most_interface *iface, - int channel_id) -{ - struct channel *channel; - - channel = get_channel(iface, channel_id); - if (!channel) { - pr_err("sound_disconnect_channel(), invalid channel %d\n", - channel_id); - return -EINVAL; - } - - list_del(&channel->list); - snd_card_free(channel->card); - - return 0; -} - -/** - * audio_rx_completion - completion handler for rx channels - * @mbo: pointer to buffer object that has completed - * - * This searches for the channel this MBO belongs to and copy the data from MBO - * to ring buffer - * - * Returns 0 on success or error code otherwise. - */ -static int audio_rx_completion(struct mbo *mbo) -{ - struct channel *channel = get_channel(mbo->ifp, mbo->hdm_channel_id); - bool period_elapsed = false; - - if (!channel) { - pr_err("sound_rx_completion(), invalid channel %d\n", - mbo->hdm_channel_id); - return -EINVAL; - } - - if (channel->is_stream_running) - period_elapsed = copy_data(channel, mbo); - - most_put_mbo(mbo); - - if (period_elapsed) - snd_pcm_period_elapsed(channel->substream); - - return 0; -} - -/** - * audio_tx_completion - completion handler for tx channels - * @iface: pointer to interface instance - * @channel_id: channel index/ID - * - * This searches the channel that belongs to this combination of interface - * pointer and channel ID and wakes a process sitting in the wait queue of - * this channel. - * - * Returns 0 on success or error code otherwise. - */ -static int audio_tx_completion(struct most_interface *iface, int channel_id) -{ - struct channel *channel = get_channel(iface, channel_id); - - if (!channel) { - pr_err("sound_tx_completion(), invalid channel %d\n", - channel_id); - return -EINVAL; - } - - wake_up_interruptible(&channel->playback_waitq); - - return 0; -} - -/** - * Initialization of the struct most_aim - */ -static struct most_aim audio_aim = { - .name = DRIVER_NAME, - .probe_channel = audio_probe_channel, - .disconnect_channel = audio_disconnect_channel, - .rx_completion = audio_rx_completion, - .tx_completion = audio_tx_completion, -}; - -static int __init audio_init(void) -{ - pr_info("init()\n"); - - INIT_LIST_HEAD(&dev_list); - - return most_register_aim(&audio_aim); -} - -static void __exit audio_exit(void) -{ - struct channel *channel, *tmp; - - pr_info("exit()\n"); - - list_for_each_entry_safe(channel, tmp, &dev_list, list) { - list_del(&channel->list); - snd_card_free(channel->card); - } - - most_deregister_aim(&audio_aim); -} - -module_init(audio_init); -module_exit(audio_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Christian Gromm "); -MODULE_DESCRIPTION("Audio Application Interface Module for MostCore"); diff --git a/drivers/staging/most/sound/Kconfig b/drivers/staging/most/sound/Kconfig new file mode 100644 index 000000000000..115262a58a42 --- /dev/null +++ b/drivers/staging/most/sound/Kconfig @@ -0,0 +1,13 @@ +# +# MOST ALSA configuration +# + +config MOST_SOUND + tristate "Sound" + depends on SND + select SND_PCM + ---help--- + Say Y here if you want to commumicate via ALSA/sound devices. + + To compile this driver as a module, choose M here: the + module will be called most_sound. diff --git a/drivers/staging/most/sound/Makefile b/drivers/staging/most/sound/Makefile new file mode 100644 index 000000000000..eee8774e38cb --- /dev/null +++ b/drivers/staging/most/sound/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_MOST_SOUND) += most_sound.o + +most_sound-objs := sound.o +ccflags-y += -Idrivers/staging/ diff --git a/drivers/staging/most/sound/sound.c b/drivers/staging/most/sound/sound.c new file mode 100644 index 000000000000..5826f710c925 --- /dev/null +++ b/drivers/staging/most/sound/sound.c @@ -0,0 +1,759 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * sound.c - Audio Application Interface Module for Mostcore + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "sound" + +static struct list_head dev_list; +static struct most_aim audio_aim; + +/** + * struct channel - private structure to keep channel specific data + * @substream: stores the substream structure + * @iface: interface for which the channel belongs to + * @cfg: channel configuration + * @card: registered sound card + * @list: list for private use + * @id: channel index + * @period_pos: current period position (ring buffer) + * @buffer_pos: current buffer position (ring buffer) + * @is_stream_running: identifies whether a stream is running or not + * @opened: set when the stream is opened + * @playback_task: playback thread + * @playback_waitq: waitq used by playback thread + */ +struct channel { + struct snd_pcm_substream *substream; + struct snd_pcm_hardware pcm_hardware; + struct most_interface *iface; + struct most_channel_config *cfg; + struct snd_card *card; + struct list_head list; + int id; + unsigned int period_pos; + unsigned int buffer_pos; + bool is_stream_running; + + struct task_struct *playback_task; + wait_queue_head_t playback_waitq; + + void (*copy_fn)(void *alsa, void *most, unsigned int bytes); +}; + +#define MOST_PCM_INFO (SNDRV_PCM_INFO_MMAP | \ + SNDRV_PCM_INFO_MMAP_VALID | \ + SNDRV_PCM_INFO_BATCH | \ + SNDRV_PCM_INFO_INTERLEAVED | \ + SNDRV_PCM_INFO_BLOCK_TRANSFER) + +#define swap16(val) ( \ + (((u16)(val) << 8) & (u16)0xFF00) | \ + (((u16)(val) >> 8) & (u16)0x00FF)) + +#define swap32(val) ( \ + (((u32)(val) << 24) & (u32)0xFF000000) | \ + (((u32)(val) << 8) & (u32)0x00FF0000) | \ + (((u32)(val) >> 8) & (u32)0x0000FF00) | \ + (((u32)(val) >> 24) & (u32)0x000000FF)) + +static void swap_copy16(u16 *dest, const u16 *source, unsigned int bytes) +{ + unsigned int i = 0; + + while (i < (bytes / 2)) { + dest[i] = swap16(source[i]); + i++; + } +} + +static void swap_copy24(u8 *dest, const u8 *source, unsigned int bytes) +{ + unsigned int i = 0; + + while (i < bytes - 2) { + dest[i] = source[i + 2]; + dest[i + 1] = source[i + 1]; + dest[i + 2] = source[i]; + i += 3; + } +} + +static void swap_copy32(u32 *dest, const u32 *source, unsigned int bytes) +{ + unsigned int i = 0; + + while (i < bytes / 4) { + dest[i] = swap32(source[i]); + i++; + } +} + +static void alsa_to_most_memcpy(void *alsa, void *most, unsigned int bytes) +{ + memcpy(most, alsa, bytes); +} + +static void alsa_to_most_copy16(void *alsa, void *most, unsigned int bytes) +{ + swap_copy16(most, alsa, bytes); +} + +static void alsa_to_most_copy24(void *alsa, void *most, unsigned int bytes) +{ + swap_copy24(most, alsa, bytes); +} + +static void alsa_to_most_copy32(void *alsa, void *most, unsigned int bytes) +{ + swap_copy32(most, alsa, bytes); +} + +static void most_to_alsa_memcpy(void *alsa, void *most, unsigned int bytes) +{ + memcpy(alsa, most, bytes); +} + +static void most_to_alsa_copy16(void *alsa, void *most, unsigned int bytes) +{ + swap_copy16(alsa, most, bytes); +} + +static void most_to_alsa_copy24(void *alsa, void *most, unsigned int bytes) +{ + swap_copy24(alsa, most, bytes); +} + +static void most_to_alsa_copy32(void *alsa, void *most, unsigned int bytes) +{ + swap_copy32(alsa, most, bytes); +} + +/** + * get_channel - get pointer to channel + * @iface: interface structure + * @channel_id: channel ID + * + * This traverses the channel list and returns the channel matching the + * ID and interface. + * + * Returns pointer to channel on success or NULL otherwise. + */ +static struct channel *get_channel(struct most_interface *iface, + int channel_id) +{ + struct channel *channel, *tmp; + + list_for_each_entry_safe(channel, tmp, &dev_list, list) { + if ((channel->iface == iface) && (channel->id == channel_id)) + return channel; + } + + return NULL; +} + +/** + * copy_data - implements data copying function + * @channel: channel + * @mbo: MBO from core + * + * Copy data from/to ring buffer to/from MBO and update the buffer position + */ +static bool copy_data(struct channel *channel, struct mbo *mbo) +{ + struct snd_pcm_runtime *const runtime = channel->substream->runtime; + unsigned int const frame_bytes = channel->cfg->subbuffer_size; + unsigned int const buffer_size = runtime->buffer_size; + unsigned int frames; + unsigned int fr0; + + if (channel->cfg->direction & MOST_CH_RX) + frames = mbo->processed_length / frame_bytes; + else + frames = mbo->buffer_length / frame_bytes; + fr0 = min(buffer_size - channel->buffer_pos, frames); + + channel->copy_fn(runtime->dma_area + channel->buffer_pos * frame_bytes, + mbo->virt_address, + fr0 * frame_bytes); + + if (frames > fr0) { + /* wrap around at end of ring buffer */ + channel->copy_fn(runtime->dma_area, + mbo->virt_address + fr0 * frame_bytes, + (frames - fr0) * frame_bytes); + } + + channel->buffer_pos += frames; + if (channel->buffer_pos >= buffer_size) + channel->buffer_pos -= buffer_size; + channel->period_pos += frames; + if (channel->period_pos >= runtime->period_size) { + channel->period_pos -= runtime->period_size; + return true; + } + + return false; +} + +/** + * playback_thread - function implements the playback thread + * @data: private data + * + * Thread which does the playback functionality in a loop. It waits for a free + * MBO from mostcore for a particular channel and copy the data from ring buffer + * to MBO. Submit the MBO back to mostcore, after copying the data. + * + * Returns 0 on success or error code otherwise. + */ +static int playback_thread(void *data) +{ + struct channel *const channel = data; + + while (!kthread_should_stop()) { + struct mbo *mbo = NULL; + bool period_elapsed = false; + + wait_event_interruptible( + channel->playback_waitq, + kthread_should_stop() || + (channel->is_stream_running && + (mbo = most_get_mbo(channel->iface, channel->id, + &audio_aim)))); + if (!mbo) + continue; + + if (channel->is_stream_running) + period_elapsed = copy_data(channel, mbo); + else + memset(mbo->virt_address, 0, mbo->buffer_length); + + most_submit_mbo(mbo); + if (period_elapsed) + snd_pcm_period_elapsed(channel->substream); + } + + return 0; +} + +/** + * pcm_open - implements open callback function for PCM middle layer + * @substream: pointer to ALSA PCM substream + * + * This is called when a PCM substream is opened. At least, the function should + * initialize the runtime->hw record. + * + * Returns 0 on success or error code otherwise. + */ +static int pcm_open(struct snd_pcm_substream *substream) +{ + struct channel *channel = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct most_channel_config *cfg = channel->cfg; + + channel->substream = substream; + + if (cfg->direction == MOST_CH_TX) { + channel->playback_task = kthread_run(playback_thread, channel, + "most_audio_playback"); + if (IS_ERR(channel->playback_task)) { + pr_err("Couldn't start thread\n"); + return PTR_ERR(channel->playback_task); + } + } + + if (most_start_channel(channel->iface, channel->id, &audio_aim)) { + pr_err("most_start_channel() failed!\n"); + if (cfg->direction == MOST_CH_TX) + kthread_stop(channel->playback_task); + return -EBUSY; + } + + runtime->hw = channel->pcm_hardware; + return 0; +} + +/** + * pcm_close - implements close callback function for PCM middle layer + * @substream: sub-stream pointer + * + * Obviously, this is called when a PCM substream is closed. Any private + * instance for a PCM substream allocated in the open callback will be + * released here. + * + * Returns 0 on success or error code otherwise. + */ +static int pcm_close(struct snd_pcm_substream *substream) +{ + struct channel *channel = substream->private_data; + + if (channel->cfg->direction == MOST_CH_TX) + kthread_stop(channel->playback_task); + most_stop_channel(channel->iface, channel->id, &audio_aim); + + return 0; +} + +/** + * pcm_hw_params - implements hw_params callback function for PCM middle layer + * @substream: sub-stream pointer + * @hw_params: contains the hardware parameters set by the application + * + * This is called when the hardware parameters is set by the application, that + * is, once when the buffer size, the period size, the format, etc. are defined + * for the PCM substream. Many hardware setups should be done is this callback, + * including the allocation of buffers. + * + * Returns 0 on success or error code otherwise. + */ +static int pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct channel *channel = substream->private_data; + + if ((params_channels(hw_params) > channel->pcm_hardware.channels_max) || + (params_channels(hw_params) < channel->pcm_hardware.channels_min)) { + pr_err("Requested number of channels not supported.\n"); + return -EINVAL; + } + return snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); +} + +/** + * pcm_hw_free - implements hw_free callback function for PCM middle layer + * @substream: substream pointer + * + * This is called to release the resources allocated via hw_params. + * This function will be always called before the close callback is called. + * + * Returns 0 on success or error code otherwise. + */ +static int pcm_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +/** + * pcm_prepare - implements prepare callback function for PCM middle layer + * @substream: substream pointer + * + * This callback is called when the PCM is "prepared". Format rate, sample rate, + * etc., can be set here. This callback can be called many times at each setup. + * + * Returns 0 on success or error code otherwise. + */ +static int pcm_prepare(struct snd_pcm_substream *substream) +{ + struct channel *channel = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct most_channel_config *cfg = channel->cfg; + int width = snd_pcm_format_physical_width(runtime->format); + + channel->copy_fn = NULL; + + if (cfg->direction == MOST_CH_TX) { + if (snd_pcm_format_big_endian(runtime->format) || width == 8) + channel->copy_fn = alsa_to_most_memcpy; + else if (width == 16) + channel->copy_fn = alsa_to_most_copy16; + else if (width == 24) + channel->copy_fn = alsa_to_most_copy24; + else if (width == 32) + channel->copy_fn = alsa_to_most_copy32; + } else { + if (snd_pcm_format_big_endian(runtime->format) || width == 8) + channel->copy_fn = most_to_alsa_memcpy; + else if (width == 16) + channel->copy_fn = most_to_alsa_copy16; + else if (width == 24) + channel->copy_fn = most_to_alsa_copy24; + else if (width == 32) + channel->copy_fn = most_to_alsa_copy32; + } + + if (!channel->copy_fn) { + pr_err("unsupported format\n"); + return -EINVAL; + } + + channel->period_pos = 0; + channel->buffer_pos = 0; + + return 0; +} + +/** + * pcm_trigger - implements trigger callback function for PCM middle layer + * @substream: substream pointer + * @cmd: action to perform + * + * This is called when the PCM is started, stopped or paused. The action will be + * specified in the second argument, SNDRV_PCM_TRIGGER_XXX + * + * Returns 0 on success or error code otherwise. + */ +static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct channel *channel = substream->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + channel->is_stream_running = true; + wake_up_interruptible(&channel->playback_waitq); + return 0; + + case SNDRV_PCM_TRIGGER_STOP: + channel->is_stream_running = false; + return 0; + + default: + pr_info("%s(), invalid\n", __func__); + return -EINVAL; + } + return 0; +} + +/** + * pcm_pointer - implements pointer callback function for PCM middle layer + * @substream: substream pointer + * + * This callback is called when the PCM middle layer inquires the current + * hardware position on the buffer. The position must be returned in frames, + * ranging from 0 to buffer_size-1. + */ +static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream) +{ + struct channel *channel = substream->private_data; + + return channel->buffer_pos; +} + +/** + * Initialization of struct snd_pcm_ops + */ +static const struct snd_pcm_ops pcm_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_hw_params, + .hw_free = pcm_hw_free, + .prepare = pcm_prepare, + .trigger = pcm_trigger, + .pointer = pcm_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, +}; + +static int split_arg_list(char *buf, char **card_name, char **pcm_format) +{ + *card_name = strsep(&buf, "."); + if (!*card_name) + return -EIO; + *pcm_format = strsep(&buf, ".\n"); + if (!*pcm_format) + return -EIO; + return 0; +} + +static int audio_set_hw_params(struct snd_pcm_hardware *pcm_hw, + char *pcm_format, + struct most_channel_config *cfg) +{ + pcm_hw->info = MOST_PCM_INFO; + pcm_hw->rates = SNDRV_PCM_RATE_48000; + pcm_hw->rate_min = 48000; + pcm_hw->rate_max = 48000; + pcm_hw->buffer_bytes_max = cfg->num_buffers * cfg->buffer_size; + pcm_hw->period_bytes_min = cfg->buffer_size; + pcm_hw->period_bytes_max = cfg->buffer_size; + pcm_hw->periods_min = 1; + pcm_hw->periods_max = cfg->num_buffers; + + if (!strcmp(pcm_format, "1x8")) { + if (cfg->subbuffer_size != 1) + goto error; + pr_info("PCM format is 8-bit mono\n"); + pcm_hw->channels_min = 1; + pcm_hw->channels_max = 1; + pcm_hw->formats = SNDRV_PCM_FMTBIT_S8; + } else if (!strcmp(pcm_format, "2x16")) { + if (cfg->subbuffer_size != 4) + goto error; + pr_info("PCM format is 16-bit stereo\n"); + pcm_hw->channels_min = 2; + pcm_hw->channels_max = 2; + pcm_hw->formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S16_BE; + } else if (!strcmp(pcm_format, "2x24")) { + if (cfg->subbuffer_size != 6) + goto error; + pr_info("PCM format is 24-bit stereo\n"); + pcm_hw->channels_min = 2; + pcm_hw->channels_max = 2; + pcm_hw->formats = SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S24_3BE; + } else if (!strcmp(pcm_format, "2x32")) { + if (cfg->subbuffer_size != 8) + goto error; + pr_info("PCM format is 32-bit stereo\n"); + pcm_hw->channels_min = 2; + pcm_hw->channels_max = 2; + pcm_hw->formats = SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE; + } else if (!strcmp(pcm_format, "6x16")) { + if (cfg->subbuffer_size != 12) + goto error; + pr_info("PCM format is 16-bit 5.1 multi channel\n"); + pcm_hw->channels_min = 6; + pcm_hw->channels_max = 6; + pcm_hw->formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S16_BE; + } else { + pr_err("PCM format %s not supported\n", pcm_format); + return -EIO; + } + return 0; +error: + pr_err("Audio resolution doesn't fit subbuffer size\n"); + return -EINVAL; +} + +/** + * audio_probe_channel - probe function of the driver module + * @iface: pointer to interface instance + * @channel_id: channel index/ID + * @cfg: pointer to actual channel configuration + * @parent: pointer to kobject (needed for sysfs hook-up) + * @arg_list: string that provides the name of the device to be created in /dev + * plus the desired audio resolution + * + * Creates sound card, pcm device, sets pcm ops and registers sound card. + * + * Returns 0 on success or error code otherwise. + */ +static int audio_probe_channel(struct most_interface *iface, int channel_id, + struct most_channel_config *cfg, + struct kobject *parent, char *arg_list) +{ + struct channel *channel; + struct snd_card *card; + struct snd_pcm *pcm; + int playback_count = 0; + int capture_count = 0; + int ret; + int direction; + char *card_name; + char *pcm_format; + + if (!iface) + return -EINVAL; + + if (cfg->data_type != MOST_CH_SYNC) { + pr_err("Incompatible channel type\n"); + return -EINVAL; + } + + if (get_channel(iface, channel_id)) { + pr_err("channel (%s:%d) is already linked\n", + iface->description, channel_id); + return -EINVAL; + } + + if (cfg->direction == MOST_CH_TX) { + playback_count = 1; + direction = SNDRV_PCM_STREAM_PLAYBACK; + } else { + capture_count = 1; + direction = SNDRV_PCM_STREAM_CAPTURE; + } + + ret = split_arg_list(arg_list, &card_name, &pcm_format); + if (ret < 0) { + pr_info("PCM format missing\n"); + return ret; + } + + ret = snd_card_new(NULL, -1, card_name, THIS_MODULE, + sizeof(*channel), &card); + if (ret < 0) + return ret; + + channel = card->private_data; + channel->card = card; + channel->cfg = cfg; + channel->iface = iface; + channel->id = channel_id; + init_waitqueue_head(&channel->playback_waitq); + + ret = audio_set_hw_params(&channel->pcm_hardware, pcm_format, cfg); + if (ret) + goto err_free_card; + + snprintf(card->driver, sizeof(card->driver), "%s", DRIVER_NAME); + snprintf(card->shortname, sizeof(card->shortname), "Microchip MOST:%d", + card->number); + snprintf(card->longname, sizeof(card->longname), "%s at %s, ch %d", + card->shortname, iface->description, channel_id); + + ret = snd_pcm_new(card, card_name, 0, playback_count, + capture_count, &pcm); + if (ret < 0) + goto err_free_card; + + pcm->private_data = channel; + + snd_pcm_set_ops(pcm, direction, &pcm_ops); + + ret = snd_card_register(card); + if (ret < 0) + goto err_free_card; + + list_add_tail(&channel->list, &dev_list); + + return 0; + +err_free_card: + snd_card_free(card); + return ret; +} + +/** + * audio_disconnect_channel - function to disconnect a channel + * @iface: pointer to interface instance + * @channel_id: channel index + * + * This frees allocated memory and removes the sound card from ALSA + * + * Returns 0 on success or error code otherwise. + */ +static int audio_disconnect_channel(struct most_interface *iface, + int channel_id) +{ + struct channel *channel; + + channel = get_channel(iface, channel_id); + if (!channel) { + pr_err("sound_disconnect_channel(), invalid channel %d\n", + channel_id); + return -EINVAL; + } + + list_del(&channel->list); + snd_card_free(channel->card); + + return 0; +} + +/** + * audio_rx_completion - completion handler for rx channels + * @mbo: pointer to buffer object that has completed + * + * This searches for the channel this MBO belongs to and copy the data from MBO + * to ring buffer + * + * Returns 0 on success or error code otherwise. + */ +static int audio_rx_completion(struct mbo *mbo) +{ + struct channel *channel = get_channel(mbo->ifp, mbo->hdm_channel_id); + bool period_elapsed = false; + + if (!channel) { + pr_err("sound_rx_completion(), invalid channel %d\n", + mbo->hdm_channel_id); + return -EINVAL; + } + + if (channel->is_stream_running) + period_elapsed = copy_data(channel, mbo); + + most_put_mbo(mbo); + + if (period_elapsed) + snd_pcm_period_elapsed(channel->substream); + + return 0; +} + +/** + * audio_tx_completion - completion handler for tx channels + * @iface: pointer to interface instance + * @channel_id: channel index/ID + * + * This searches the channel that belongs to this combination of interface + * pointer and channel ID and wakes a process sitting in the wait queue of + * this channel. + * + * Returns 0 on success or error code otherwise. + */ +static int audio_tx_completion(struct most_interface *iface, int channel_id) +{ + struct channel *channel = get_channel(iface, channel_id); + + if (!channel) { + pr_err("sound_tx_completion(), invalid channel %d\n", + channel_id); + return -EINVAL; + } + + wake_up_interruptible(&channel->playback_waitq); + + return 0; +} + +/** + * Initialization of the struct most_aim + */ +static struct most_aim audio_aim = { + .name = DRIVER_NAME, + .probe_channel = audio_probe_channel, + .disconnect_channel = audio_disconnect_channel, + .rx_completion = audio_rx_completion, + .tx_completion = audio_tx_completion, +}; + +static int __init audio_init(void) +{ + pr_info("init()\n"); + + INIT_LIST_HEAD(&dev_list); + + return most_register_aim(&audio_aim); +} + +static void __exit audio_exit(void) +{ + struct channel *channel, *tmp; + + pr_info("exit()\n"); + + list_for_each_entry_safe(channel, tmp, &dev_list, list) { + list_del(&channel->list); + snd_card_free(channel->card); + } + + most_deregister_aim(&audio_aim); +} + +module_init(audio_init); +module_exit(audio_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Christian Gromm "); +MODULE_DESCRIPTION("Audio Application Interface Module for MostCore"); -- cgit From 6e01fc7775ffb59750f9563f96418fad5886ac16 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:41 +0100 Subject: staging: most: usb: rename module This patch renames the folder of the usb module. It is needed to clean up the directory layout of the driver. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/Kconfig | 2 +- drivers/staging/most/hdm-usb/Kconfig | 14 - drivers/staging/most/hdm-usb/hdm_usb.c | 1300 -------------------------------- drivers/staging/most/usb/Kconfig | 13 + drivers/staging/most/usb/Makefile | 4 + drivers/staging/most/usb/usb.c | 1300 ++++++++++++++++++++++++++++++++ 6 files changed, 1318 insertions(+), 1315 deletions(-) delete mode 100644 drivers/staging/most/hdm-usb/Kconfig delete mode 100644 drivers/staging/most/hdm-usb/hdm_usb.c create mode 100644 drivers/staging/most/usb/Kconfig create mode 100644 drivers/staging/most/usb/Makefile create mode 100644 drivers/staging/most/usb/usb.c (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/Kconfig b/drivers/staging/most/Kconfig index 3f127116476c..86d4f12596bb 100644 --- a/drivers/staging/most/Kconfig +++ b/drivers/staging/most/Kconfig @@ -29,6 +29,6 @@ source "drivers/staging/most/dim2/Kconfig" source "drivers/staging/most/i2c/Kconfig" -source "drivers/staging/most/hdm-usb/Kconfig" +source "drivers/staging/most/usb/Kconfig" endif diff --git a/drivers/staging/most/hdm-usb/Kconfig b/drivers/staging/most/hdm-usb/Kconfig deleted file mode 100644 index 487f1f34776c..000000000000 --- a/drivers/staging/most/hdm-usb/Kconfig +++ /dev/null @@ -1,14 +0,0 @@ -# -# MOST USB configuration -# - -config HDM_USB - tristate "USB HDM" - depends on USB && NET - - ---help--- - Say Y here if you want to connect via USB to network tranceiver. - This device driver depends on the networking AIM. - - To compile this driver as a module, choose M here: the - module will be called hdm_usb. diff --git a/drivers/staging/most/hdm-usb/hdm_usb.c b/drivers/staging/most/hdm-usb/hdm_usb.c deleted file mode 100644 index f036614a1da6..000000000000 --- a/drivers/staging/most/hdm-usb/hdm_usb.c +++ /dev/null @@ -1,1300 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * hdm_usb.c - Hardware dependent module for USB - * - * Copyright (C) 2013-2015 Microchip Technology Germany II GmbH & Co. KG - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "most/core.h" - -#define USB_MTU 512 -#define NO_ISOCHRONOUS_URB 0 -#define AV_PACKETS_PER_XACT 2 -#define BUF_CHAIN_SIZE 0xFFFF -#define MAX_NUM_ENDPOINTS 30 -#define MAX_SUFFIX_LEN 10 -#define MAX_STRING_LEN 80 -#define MAX_BUF_SIZE 0xFFFF - -#define USB_VENDOR_ID_SMSC 0x0424 /* VID: SMSC */ -#define USB_DEV_ID_BRDG 0xC001 /* PID: USB Bridge */ -#define USB_DEV_ID_OS81118 0xCF18 /* PID: USB OS81118 */ -#define USB_DEV_ID_OS81119 0xCF19 /* PID: USB OS81119 */ -#define USB_DEV_ID_OS81210 0xCF30 /* PID: USB OS81210 */ -/* DRCI Addresses */ -#define DRCI_REG_NI_STATE 0x0100 -#define DRCI_REG_PACKET_BW 0x0101 -#define DRCI_REG_NODE_ADDR 0x0102 -#define DRCI_REG_NODE_POS 0x0103 -#define DRCI_REG_MEP_FILTER 0x0140 -#define DRCI_REG_HASH_TBL0 0x0141 -#define DRCI_REG_HASH_TBL1 0x0142 -#define DRCI_REG_HASH_TBL2 0x0143 -#define DRCI_REG_HASH_TBL3 0x0144 -#define DRCI_REG_HW_ADDR_HI 0x0145 -#define DRCI_REG_HW_ADDR_MI 0x0146 -#define DRCI_REG_HW_ADDR_LO 0x0147 -#define DRCI_REG_BASE 0x1100 -#define DRCI_COMMAND 0x02 -#define DRCI_READ_REQ 0xA0 -#define DRCI_WRITE_REQ 0xA1 - -/** - * struct most_dci_obj - Direct Communication Interface - * @kobj:position in sysfs - * @usb_device: pointer to the usb device - * @reg_addr: register address for arbitrary DCI access - */ -struct most_dci_obj { - struct kobject kobj; - struct usb_device *usb_device; - u16 reg_addr; -}; - -#define to_dci_obj(p) container_of(p, struct most_dci_obj, kobj) - -struct most_dev; - -struct clear_hold_work { - struct work_struct ws; - struct most_dev *mdev; - unsigned int channel; - int pipe; -}; - -#define to_clear_hold_work(w) container_of(w, struct clear_hold_work, ws) - -/** - * struct most_dev - holds all usb interface specific stuff - * @parent: parent object in sysfs - * @usb_device: pointer to usb device - * @iface: hardware interface - * @cap: channel capabilities - * @conf: channel configuration - * @dci: direct communication interface of hardware - * @ep_address: endpoint address table - * @description: device description - * @suffix: suffix for channel name - * @channel_lock: synchronize channel access - * @padding_active: indicates channel uses padding - * @is_channel_healthy: health status table of each channel - * @busy_urbs: list of anchored items - * @io_mutex: synchronize I/O with disconnect - * @link_stat_timer: timer for link status reports - * @poll_work_obj: work for polling link status - */ -struct most_dev { - struct kobject *parent; - struct usb_device *usb_device; - struct most_interface iface; - struct most_channel_capability *cap; - struct most_channel_config *conf; - struct most_dci_obj *dci; - u8 *ep_address; - char description[MAX_STRING_LEN]; - char suffix[MAX_NUM_ENDPOINTS][MAX_SUFFIX_LEN]; - spinlock_t channel_lock[MAX_NUM_ENDPOINTS]; /* sync channel access */ - bool padding_active[MAX_NUM_ENDPOINTS]; - bool is_channel_healthy[MAX_NUM_ENDPOINTS]; - struct clear_hold_work clear_work[MAX_NUM_ENDPOINTS]; - struct usb_anchor *busy_urbs; - struct mutex io_mutex; - struct timer_list link_stat_timer; - struct work_struct poll_work_obj; - void (*on_netinfo)(struct most_interface *, unsigned char, - unsigned char *); -}; - -#define to_mdev(d) container_of(d, struct most_dev, iface) -#define to_mdev_from_work(w) container_of(w, struct most_dev, poll_work_obj) - -static void wq_clear_halt(struct work_struct *wq_obj); -static void wq_netinfo(struct work_struct *wq_obj); - -/** - * drci_rd_reg - read a DCI register - * @dev: usb device - * @reg: register address - * @buf: buffer to store data - * - * This is reads data from INIC's direct register communication interface - */ -static inline int drci_rd_reg(struct usb_device *dev, u16 reg, u16 *buf) -{ - int retval; - __le16 *dma_buf = kzalloc(sizeof(*dma_buf), GFP_KERNEL); - u8 req_type = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE; - - if (!dma_buf) - return -ENOMEM; - - retval = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - DRCI_READ_REQ, req_type, - 0x0000, - reg, dma_buf, sizeof(*dma_buf), 5 * HZ); - *buf = le16_to_cpu(*dma_buf); - kfree(dma_buf); - - return retval; -} - -/** - * drci_wr_reg - write a DCI register - * @dev: usb device - * @reg: register address - * @data: data to write - * - * This is writes data to INIC's direct register communication interface - */ -static inline int drci_wr_reg(struct usb_device *dev, u16 reg, u16 data) -{ - return usb_control_msg(dev, - usb_sndctrlpipe(dev, 0), - DRCI_WRITE_REQ, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - data, - reg, - NULL, - 0, - 5 * HZ); -} - -static inline int start_sync_ep(struct usb_device *usb_dev, u16 ep) -{ - return drci_wr_reg(usb_dev, DRCI_REG_BASE + DRCI_COMMAND + ep * 16, 1); -} - -/** - * get_stream_frame_size - calculate frame size of current configuration - * @cfg: channel configuration - */ -static unsigned int get_stream_frame_size(struct most_channel_config *cfg) -{ - unsigned int frame_size = 0; - unsigned int sub_size = cfg->subbuffer_size; - - if (!sub_size) { - pr_warn("Misconfig: Subbuffer size zero.\n"); - return frame_size; - } - switch (cfg->data_type) { - case MOST_CH_ISOC: - frame_size = AV_PACKETS_PER_XACT * sub_size; - break; - case MOST_CH_SYNC: - if (cfg->packets_per_xact == 0) { - pr_warn("Misconfig: Packets per XACT zero\n"); - frame_size = 0; - } else if (cfg->packets_per_xact == 0xFF) { - frame_size = (USB_MTU / sub_size) * sub_size; - } else { - frame_size = cfg->packets_per_xact * sub_size; - } - break; - default: - pr_warn("Query frame size of non-streaming channel\n"); - break; - } - return frame_size; -} - -/** - * hdm_poison_channel - mark buffers of this channel as invalid - * @iface: pointer to the interface - * @channel: channel ID - * - * This unlinks all URBs submitted to the HCD, - * calls the associated completion function of the core and removes - * them from the list. - * - * Returns 0 on success or error code otherwise. - */ -static int hdm_poison_channel(struct most_interface *iface, int channel) -{ - struct most_dev *mdev = to_mdev(iface); - unsigned long flags; - spinlock_t *lock; /* temp. lock */ - - if (unlikely(!iface)) { - dev_warn(&mdev->usb_device->dev, "Poison: Bad interface.\n"); - return -EIO; - } - if (unlikely(channel < 0 || channel >= iface->num_channels)) { - dev_warn(&mdev->usb_device->dev, "Channel ID out of range.\n"); - return -ECHRNG; - } - - lock = mdev->channel_lock + channel; - spin_lock_irqsave(lock, flags); - mdev->is_channel_healthy[channel] = false; - spin_unlock_irqrestore(lock, flags); - - cancel_work_sync(&mdev->clear_work[channel].ws); - - mutex_lock(&mdev->io_mutex); - usb_kill_anchored_urbs(&mdev->busy_urbs[channel]); - if (mdev->padding_active[channel]) - mdev->padding_active[channel] = false; - - if (mdev->conf[channel].data_type == MOST_CH_ASYNC) { - del_timer_sync(&mdev->link_stat_timer); - cancel_work_sync(&mdev->poll_work_obj); - } - mutex_unlock(&mdev->io_mutex); - return 0; -} - -/** - * hdm_add_padding - add padding bytes - * @mdev: most device - * @channel: channel ID - * @mbo: buffer object - * - * This inserts the INIC hardware specific padding bytes into a streaming - * channel's buffer - */ -static int hdm_add_padding(struct most_dev *mdev, int channel, struct mbo *mbo) -{ - struct most_channel_config *conf = &mdev->conf[channel]; - unsigned int frame_size = get_stream_frame_size(conf); - unsigned int j, num_frames; - - if (!frame_size) - return -EIO; - num_frames = mbo->buffer_length / frame_size; - - if (num_frames < 1) { - dev_err(&mdev->usb_device->dev, - "Missed minimal transfer unit.\n"); - return -EIO; - } - - for (j = num_frames - 1; j > 0; j--) - memmove(mbo->virt_address + j * USB_MTU, - mbo->virt_address + j * frame_size, - frame_size); - mbo->buffer_length = num_frames * USB_MTU; - return 0; -} - -/** - * hdm_remove_padding - remove padding bytes - * @mdev: most device - * @channel: channel ID - * @mbo: buffer object - * - * This takes the INIC hardware specific padding bytes off a streaming - * channel's buffer. - */ -static int hdm_remove_padding(struct most_dev *mdev, int channel, - struct mbo *mbo) -{ - struct most_channel_config *const conf = &mdev->conf[channel]; - unsigned int frame_size = get_stream_frame_size(conf); - unsigned int j, num_frames; - - if (!frame_size) - return -EIO; - num_frames = mbo->processed_length / USB_MTU; - - for (j = 1; j < num_frames; j++) - memmove(mbo->virt_address + frame_size * j, - mbo->virt_address + USB_MTU * j, - frame_size); - - mbo->processed_length = frame_size * num_frames; - return 0; -} - -/** - * hdm_write_completion - completion function for submitted Tx URBs - * @urb: the URB that has been completed - * - * This checks the status of the completed URB. In case the URB has been - * unlinked before, it is immediately freed. On any other error the MBO - * transfer flag is set. On success it frees allocated resources and calls - * the completion function. - * - * Context: interrupt! - */ -static void hdm_write_completion(struct urb *urb) -{ - struct mbo *mbo = urb->context; - struct most_dev *mdev = to_mdev(mbo->ifp); - unsigned int channel = mbo->hdm_channel_id; - struct device *dev = &mdev->usb_device->dev; - spinlock_t *lock = mdev->channel_lock + channel; - unsigned long flags; - - spin_lock_irqsave(lock, flags); - - mbo->processed_length = 0; - mbo->status = MBO_E_INVAL; - if (likely(mdev->is_channel_healthy[channel])) { - switch (urb->status) { - case 0: - case -ESHUTDOWN: - mbo->processed_length = urb->actual_length; - mbo->status = MBO_SUCCESS; - break; - case -EPIPE: - dev_warn(dev, "Broken OUT pipe detected\n"); - mdev->is_channel_healthy[channel] = false; - mdev->clear_work[channel].pipe = urb->pipe; - schedule_work(&mdev->clear_work[channel].ws); - break; - case -ENODEV: - case -EPROTO: - mbo->status = MBO_E_CLOSE; - break; - } - } - - spin_unlock_irqrestore(lock, flags); - - if (likely(mbo->complete)) - mbo->complete(mbo); - usb_free_urb(urb); -} - -/** - * hdm_read_completion - completion function for submitted Rx URBs - * @urb: the URB that has been completed - * - * This checks the status of the completed URB. In case the URB has been - * unlinked before it is immediately freed. On any other error the MBO transfer - * flag is set. On success it frees allocated resources, removes - * padding bytes -if necessary- and calls the completion function. - * - * Context: interrupt! - * - * ************************************************************************** - * Error codes returned by in urb->status - * or in iso_frame_desc[n].status (for ISO) - * ************************************************************************* - * - * USB device drivers may only test urb status values in completion handlers. - * This is because otherwise there would be a race between HCDs updating - * these values on one CPU, and device drivers testing them on another CPU. - * - * A transfer's actual_length may be positive even when an error has been - * reported. That's because transfers often involve several packets, so that - * one or more packets could finish before an error stops further endpoint I/O. - * - * For isochronous URBs, the urb status value is non-zero only if the URB is - * unlinked, the device is removed, the host controller is disabled or the total - * transferred length is less than the requested length and the URB_SHORT_NOT_OK - * flag is set. Completion handlers for isochronous URBs should only see - * urb->status set to zero, -ENOENT, -ECONNRESET, -ESHUTDOWN, or -EREMOTEIO. - * Individual frame descriptor status fields may report more status codes. - * - * - * 0 Transfer completed successfully - * - * -ENOENT URB was synchronously unlinked by usb_unlink_urb - * - * -EINPROGRESS URB still pending, no results yet - * (That is, if drivers see this it's a bug.) - * - * -EPROTO (*, **) a) bitstuff error - * b) no response packet received within the - * prescribed bus turn-around time - * c) unknown USB error - * - * -EILSEQ (*, **) a) CRC mismatch - * b) no response packet received within the - * prescribed bus turn-around time - * c) unknown USB error - * - * Note that often the controller hardware does not - * distinguish among cases a), b), and c), so a - * driver cannot tell whether there was a protocol - * error, a failure to respond (often caused by - * device disconnect), or some other fault. - * - * -ETIME (**) No response packet received within the prescribed - * bus turn-around time. This error may instead be - * reported as -EPROTO or -EILSEQ. - * - * -ETIMEDOUT Synchronous USB message functions use this code - * to indicate timeout expired before the transfer - * completed, and no other error was reported by HC. - * - * -EPIPE (**) Endpoint stalled. For non-control endpoints, - * reset this status with usb_clear_halt(). - * - * -ECOMM During an IN transfer, the host controller - * received data from an endpoint faster than it - * could be written to system memory - * - * -ENOSR During an OUT transfer, the host controller - * could not retrieve data from system memory fast - * enough to keep up with the USB data rate - * - * -EOVERFLOW (*) The amount of data returned by the endpoint was - * greater than either the max packet size of the - * endpoint or the remaining buffer size. "Babble". - * - * -EREMOTEIO The data read from the endpoint did not fill the - * specified buffer, and URB_SHORT_NOT_OK was set in - * urb->transfer_flags. - * - * -ENODEV Device was removed. Often preceded by a burst of - * other errors, since the hub driver doesn't detect - * device removal events immediately. - * - * -EXDEV ISO transfer only partially completed - * (only set in iso_frame_desc[n].status, not urb->status) - * - * -EINVAL ISO madness, if this happens: Log off and go home - * - * -ECONNRESET URB was asynchronously unlinked by usb_unlink_urb - * - * -ESHUTDOWN The device or host controller has been disabled due - * to some problem that could not be worked around, - * such as a physical disconnect. - * - * - * (*) Error codes like -EPROTO, -EILSEQ and -EOVERFLOW normally indicate - * hardware problems such as bad devices (including firmware) or cables. - * - * (**) This is also one of several codes that different kinds of host - * controller use to indicate a transfer has failed because of device - * disconnect. In the interval before the hub driver starts disconnect - * processing, devices may receive such fault reports for every request. - * - * See - */ -static void hdm_read_completion(struct urb *urb) -{ - struct mbo *mbo = urb->context; - struct most_dev *mdev = to_mdev(mbo->ifp); - unsigned int channel = mbo->hdm_channel_id; - struct device *dev = &mdev->usb_device->dev; - spinlock_t *lock = mdev->channel_lock + channel; - unsigned long flags; - - spin_lock_irqsave(lock, flags); - - mbo->processed_length = 0; - mbo->status = MBO_E_INVAL; - if (likely(mdev->is_channel_healthy[channel])) { - switch (urb->status) { - case 0: - case -ESHUTDOWN: - mbo->processed_length = urb->actual_length; - mbo->status = MBO_SUCCESS; - if (mdev->padding_active[channel] && - hdm_remove_padding(mdev, channel, mbo)) { - mbo->processed_length = 0; - mbo->status = MBO_E_INVAL; - } - break; - case -EPIPE: - dev_warn(dev, "Broken IN pipe detected\n"); - mdev->is_channel_healthy[channel] = false; - mdev->clear_work[channel].pipe = urb->pipe; - schedule_work(&mdev->clear_work[channel].ws); - break; - case -ENODEV: - case -EPROTO: - mbo->status = MBO_E_CLOSE; - break; - case -EOVERFLOW: - dev_warn(dev, "Babble on IN pipe detected\n"); - break; - } - } - - spin_unlock_irqrestore(lock, flags); - - if (likely(mbo->complete)) - mbo->complete(mbo); - usb_free_urb(urb); -} - -/** - * hdm_enqueue - receive a buffer to be used for data transfer - * @iface: interface to enqueue to - * @channel: ID of the channel - * @mbo: pointer to the buffer object - * - * This allocates a new URB and fills it according to the channel - * that is being used for transmission of data. Before the URB is - * submitted it is stored in the private anchor list. - * - * Returns 0 on success. On any error the URB is freed and a error code - * is returned. - * - * Context: Could in _some_ cases be interrupt! - */ -static int hdm_enqueue(struct most_interface *iface, int channel, - struct mbo *mbo) -{ - struct most_dev *mdev; - struct most_channel_config *conf; - struct device *dev; - int retval = 0; - struct urb *urb; - unsigned long length; - void *virt_address; - - if (unlikely(!iface || !mbo)) - return -EIO; - if (unlikely(iface->num_channels <= channel || channel < 0)) - return -ECHRNG; - - mdev = to_mdev(iface); - conf = &mdev->conf[channel]; - dev = &mdev->usb_device->dev; - - if (!mdev->usb_device) - return -ENODEV; - - urb = usb_alloc_urb(NO_ISOCHRONOUS_URB, GFP_ATOMIC); - if (!urb) - return -ENOMEM; - - if ((conf->direction & MOST_CH_TX) && mdev->padding_active[channel] && - hdm_add_padding(mdev, channel, mbo)) { - retval = -EIO; - goto _error; - } - - urb->transfer_dma = mbo->bus_address; - virt_address = mbo->virt_address; - length = mbo->buffer_length; - - if (conf->direction & MOST_CH_TX) { - usb_fill_bulk_urb(urb, mdev->usb_device, - usb_sndbulkpipe(mdev->usb_device, - mdev->ep_address[channel]), - virt_address, - length, - hdm_write_completion, - mbo); - if (conf->data_type != MOST_CH_ISOC) - urb->transfer_flags |= URB_ZERO_PACKET; - } else { - usb_fill_bulk_urb(urb, mdev->usb_device, - usb_rcvbulkpipe(mdev->usb_device, - mdev->ep_address[channel]), - virt_address, - length + conf->extra_len, - hdm_read_completion, - mbo); - } - urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - - usb_anchor_urb(urb, &mdev->busy_urbs[channel]); - - retval = usb_submit_urb(urb, GFP_KERNEL); - if (retval) { - dev_err(dev, "URB submit failed with error %d.\n", retval); - goto _error_1; - } - return 0; - -_error_1: - usb_unanchor_urb(urb); -_error: - usb_free_urb(urb); - return retval; -} - -/** - * hdm_configure_channel - receive channel configuration from core - * @iface: interface - * @channel: channel ID - * @conf: structure that holds the configuration information - * - * The attached network interface controller (NIC) supports a padding mode - * to avoid short packets on USB, hence increasing the performance due to a - * lower interrupt load. This mode is default for synchronous data and can - * be switched on for isochronous data. In case padding is active the - * driver needs to know the frame size of the payload in order to calculate - * the number of bytes it needs to pad when transmitting or to cut off when - * receiving data. - * - */ -static int hdm_configure_channel(struct most_interface *iface, int channel, - struct most_channel_config *conf) -{ - unsigned int num_frames; - unsigned int frame_size; - struct most_dev *mdev = to_mdev(iface); - struct device *dev = &mdev->usb_device->dev; - - mdev->is_channel_healthy[channel] = true; - mdev->clear_work[channel].channel = channel; - mdev->clear_work[channel].mdev = mdev; - INIT_WORK(&mdev->clear_work[channel].ws, wq_clear_halt); - - if (unlikely(!iface || !conf)) { - dev_err(dev, "Bad interface or config pointer.\n"); - return -EINVAL; - } - if (unlikely(channel < 0 || channel >= iface->num_channels)) { - dev_err(dev, "Channel ID out of range.\n"); - return -EINVAL; - } - if (!conf->num_buffers || !conf->buffer_size) { - dev_err(dev, "Misconfig: buffer size or #buffers zero.\n"); - return -EINVAL; - } - - if (conf->data_type != MOST_CH_SYNC && - !(conf->data_type == MOST_CH_ISOC && - conf->packets_per_xact != 0xFF)) { - mdev->padding_active[channel] = false; - /* - * Since the NIC's padding mode is not going to be - * used, we can skip the frame size calculations and - * move directly on to exit. - */ - goto exit; - } - - mdev->padding_active[channel] = true; - - frame_size = get_stream_frame_size(conf); - if (frame_size == 0 || frame_size > USB_MTU) { - dev_warn(dev, "Misconfig: frame size wrong\n"); - return -EINVAL; - } - - num_frames = conf->buffer_size / frame_size; - - if (conf->buffer_size % frame_size) { - u16 old_size = conf->buffer_size; - - conf->buffer_size = num_frames * frame_size; - dev_warn(dev, "%s: fixed buffer size (%d -> %d)\n", - mdev->suffix[channel], old_size, conf->buffer_size); - } - - /* calculate extra length to comply w/ HW padding */ - conf->extra_len = num_frames * (USB_MTU - frame_size); - -exit: - mdev->conf[channel] = *conf; - if (conf->data_type == MOST_CH_ASYNC) { - u16 ep = mdev->ep_address[channel]; - - if (start_sync_ep(mdev->usb_device, ep) < 0) - dev_warn(dev, "sync for ep%02x failed", ep); - } - return 0; -} - -/** - * hdm_request_netinfo - request network information - * @iface: pointer to interface - * @channel: channel ID - * - * This is used as trigger to set up the link status timer that - * polls for the NI state of the INIC every 2 seconds. - * - */ -static void hdm_request_netinfo(struct most_interface *iface, int channel, - void (*on_netinfo)(struct most_interface *, - unsigned char, - unsigned char *)) -{ - struct most_dev *mdev; - - BUG_ON(!iface); - mdev = to_mdev(iface); - mdev->on_netinfo = on_netinfo; - if (!on_netinfo) - return; - - mdev->link_stat_timer.expires = jiffies + HZ; - mod_timer(&mdev->link_stat_timer, mdev->link_stat_timer.expires); -} - -/** - * link_stat_timer_handler - schedule work obtaining mac address and link status - * @data: pointer to USB device instance - * - * The handler runs in interrupt context. That's why we need to defer the - * tasks to a work queue. - */ -static void link_stat_timer_handler(struct timer_list *t) -{ - struct most_dev *mdev = from_timer(mdev, t, link_stat_timer); - - schedule_work(&mdev->poll_work_obj); - mdev->link_stat_timer.expires = jiffies + (2 * HZ); - add_timer(&mdev->link_stat_timer); -} - -/** - * wq_netinfo - work queue function to deliver latest networking information - * @wq_obj: object that holds data for our deferred work to do - * - * This retrieves the network interface status of the USB INIC - */ -static void wq_netinfo(struct work_struct *wq_obj) -{ - struct most_dev *mdev = to_mdev_from_work(wq_obj); - struct usb_device *usb_device = mdev->usb_device; - struct device *dev = &usb_device->dev; - u16 hi, mi, lo, link; - u8 hw_addr[6]; - - if (drci_rd_reg(usb_device, DRCI_REG_HW_ADDR_HI, &hi) < 0) { - dev_err(dev, "Vendor request 'hw_addr_hi' failed\n"); - return; - } - - if (drci_rd_reg(usb_device, DRCI_REG_HW_ADDR_MI, &mi) < 0) { - dev_err(dev, "Vendor request 'hw_addr_mid' failed\n"); - return; - } - - if (drci_rd_reg(usb_device, DRCI_REG_HW_ADDR_LO, &lo) < 0) { - dev_err(dev, "Vendor request 'hw_addr_low' failed\n"); - return; - } - - if (drci_rd_reg(usb_device, DRCI_REG_NI_STATE, &link) < 0) { - dev_err(dev, "Vendor request 'link status' failed\n"); - return; - } - - hw_addr[0] = hi >> 8; - hw_addr[1] = hi; - hw_addr[2] = mi >> 8; - hw_addr[3] = mi; - hw_addr[4] = lo >> 8; - hw_addr[5] = lo; - - if (mdev->on_netinfo) - mdev->on_netinfo(&mdev->iface, link, hw_addr); -} - -/** - * wq_clear_halt - work queue function - * @wq_obj: work_struct object to execute - * - * This sends a clear_halt to the given USB pipe. - */ -static void wq_clear_halt(struct work_struct *wq_obj) -{ - struct clear_hold_work *clear_work = to_clear_hold_work(wq_obj); - struct most_dev *mdev = clear_work->mdev; - unsigned int channel = clear_work->channel; - int pipe = clear_work->pipe; - - mutex_lock(&mdev->io_mutex); - most_stop_enqueue(&mdev->iface, channel); - usb_kill_anchored_urbs(&mdev->busy_urbs[channel]); - if (usb_clear_halt(mdev->usb_device, pipe)) - dev_warn(&mdev->usb_device->dev, "Failed to reset endpoint.\n"); - - mdev->is_channel_healthy[channel] = true; - most_resume_enqueue(&mdev->iface, channel); - mutex_unlock(&mdev->io_mutex); -} - -/** - * hdm_usb_fops - file operation table for USB driver - */ -static const struct file_operations hdm_usb_fops = { - .owner = THIS_MODULE, -}; - -/** - * usb_device_id - ID table for HCD device probing - */ -static const struct usb_device_id usbid[] = { - { USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_BRDG), }, - { USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_OS81118), }, - { USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_OS81119), }, - { USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_OS81210), }, - { } /* Terminating entry */ -}; - -#define MOST_DCI_RO_ATTR(_name) \ - struct most_dci_attribute most_dci_attr_##_name = \ - __ATTR(_name, 0444, show_value, NULL) - -#define MOST_DCI_ATTR(_name) \ - struct most_dci_attribute most_dci_attr_##_name = \ - __ATTR(_name, 0644, show_value, store_value) - -#define MOST_DCI_WO_ATTR(_name) \ - struct most_dci_attribute most_dci_attr_##_name = \ - __ATTR(_name, 0200, NULL, store_value) - -/** - * struct most_dci_attribute - to access the attributes of a dci object - * @attr: attributes of a dci object - * @show: pointer to the show function - * @store: pointer to the store function - */ -struct most_dci_attribute { - struct attribute attr; - ssize_t (*show)(struct most_dci_obj *d, - struct most_dci_attribute *attr, - char *buf); - ssize_t (*store)(struct most_dci_obj *d, - struct most_dci_attribute *attr, - const char *buf, - size_t count); -}; - -#define to_dci_attr(a) container_of(a, struct most_dci_attribute, attr) - -/** - * dci_attr_show - show function for dci object - * @kobj: pointer to kobject - * @attr: pointer to attribute struct - * @buf: buffer - */ -static ssize_t dci_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - struct most_dci_attribute *dci_attr = to_dci_attr(attr); - struct most_dci_obj *dci_obj = to_dci_obj(kobj); - - if (!dci_attr->show) - return -EIO; - - return dci_attr->show(dci_obj, dci_attr, buf); -} - -/** - * dci_attr_store - store function for dci object - * @kobj: pointer to kobject - * @attr: pointer to attribute struct - * @buf: buffer - * @len: length of buffer - */ -static ssize_t dci_attr_store(struct kobject *kobj, - struct attribute *attr, - const char *buf, - size_t len) -{ - struct most_dci_attribute *dci_attr = to_dci_attr(attr); - struct most_dci_obj *dci_obj = to_dci_obj(kobj); - - if (!dci_attr->store) - return -EIO; - - return dci_attr->store(dci_obj, dci_attr, buf, len); -} - -static const struct sysfs_ops most_dci_sysfs_ops = { - .show = dci_attr_show, - .store = dci_attr_store, -}; - -/** - * most_dci_release - release function for dci object - * @kobj: pointer to kobject - * - * This frees the memory allocated for the dci object - */ -static void most_dci_release(struct kobject *kobj) -{ - struct most_dci_obj *dci_obj = to_dci_obj(kobj); - - kfree(dci_obj); -} - -struct regs { - const char *name; - u16 reg; -}; - -static const struct regs ro_regs[] = { - { "ni_state", DRCI_REG_NI_STATE }, - { "packet_bandwidth", DRCI_REG_PACKET_BW }, - { "node_address", DRCI_REG_NODE_ADDR }, - { "node_position", DRCI_REG_NODE_POS }, -}; - -static const struct regs rw_regs[] = { - { "mep_filter", DRCI_REG_MEP_FILTER }, - { "mep_hash0", DRCI_REG_HASH_TBL0 }, - { "mep_hash1", DRCI_REG_HASH_TBL1 }, - { "mep_hash2", DRCI_REG_HASH_TBL2 }, - { "mep_hash3", DRCI_REG_HASH_TBL3 }, - { "mep_eui48_hi", DRCI_REG_HW_ADDR_HI }, - { "mep_eui48_mi", DRCI_REG_HW_ADDR_MI }, - { "mep_eui48_lo", DRCI_REG_HW_ADDR_LO }, -}; - -static int get_stat_reg_addr(const struct regs *regs, int size, - const char *name, u16 *reg_addr) -{ - int i; - - for (i = 0; i < size; i++) { - if (!strcmp(name, regs[i].name)) { - *reg_addr = regs[i].reg; - return 0; - } - } - return -EFAULT; -} - -#define get_static_reg_addr(regs, name, reg_addr) \ - get_stat_reg_addr(regs, ARRAY_SIZE(regs), name, reg_addr) - -static ssize_t show_value(struct most_dci_obj *dci_obj, - struct most_dci_attribute *attr, char *buf) -{ - const char *name = attr->attr.name; - u16 val; - u16 reg_addr; - int err; - - if (!strcmp(name, "arb_address")) - return snprintf(buf, PAGE_SIZE, "%04x\n", dci_obj->reg_addr); - - if (!strcmp(name, "arb_value")) - reg_addr = dci_obj->reg_addr; - else if (get_static_reg_addr(ro_regs, name, ®_addr) && - get_static_reg_addr(rw_regs, name, ®_addr)) - return -EFAULT; - - err = drci_rd_reg(dci_obj->usb_device, reg_addr, &val); - if (err < 0) - return err; - - return snprintf(buf, PAGE_SIZE, "%04x\n", val); -} - -static ssize_t store_value(struct most_dci_obj *dci_obj, - struct most_dci_attribute *attr, - const char *buf, size_t count) -{ - u16 val; - u16 reg_addr; - const char *name = attr->attr.name; - struct usb_device *usb_dev = dci_obj->usb_device; - int err = kstrtou16(buf, 16, &val); - - if (err) - return err; - - if (!strcmp(name, "arb_address")) { - dci_obj->reg_addr = val; - return count; - } - - if (!strcmp(name, "arb_value")) - err = drci_wr_reg(usb_dev, dci_obj->reg_addr, val); - else if (!strcmp(name, "sync_ep")) - err = start_sync_ep(usb_dev, val); - else if (!get_static_reg_addr(rw_regs, name, ®_addr)) - err = drci_wr_reg(usb_dev, reg_addr, val); - else - return -EFAULT; - - if (err < 0) - return err; - - return count; -} - -static MOST_DCI_RO_ATTR(ni_state); -static MOST_DCI_RO_ATTR(packet_bandwidth); -static MOST_DCI_RO_ATTR(node_address); -static MOST_DCI_RO_ATTR(node_position); -static MOST_DCI_WO_ATTR(sync_ep); -static MOST_DCI_ATTR(mep_filter); -static MOST_DCI_ATTR(mep_hash0); -static MOST_DCI_ATTR(mep_hash1); -static MOST_DCI_ATTR(mep_hash2); -static MOST_DCI_ATTR(mep_hash3); -static MOST_DCI_ATTR(mep_eui48_hi); -static MOST_DCI_ATTR(mep_eui48_mi); -static MOST_DCI_ATTR(mep_eui48_lo); -static MOST_DCI_ATTR(arb_address); -static MOST_DCI_ATTR(arb_value); - -/** - * most_dci_def_attrs - array of default attribute files of the dci object - */ -static struct attribute *most_dci_def_attrs[] = { - &most_dci_attr_ni_state.attr, - &most_dci_attr_packet_bandwidth.attr, - &most_dci_attr_node_address.attr, - &most_dci_attr_node_position.attr, - &most_dci_attr_sync_ep.attr, - &most_dci_attr_mep_filter.attr, - &most_dci_attr_mep_hash0.attr, - &most_dci_attr_mep_hash1.attr, - &most_dci_attr_mep_hash2.attr, - &most_dci_attr_mep_hash3.attr, - &most_dci_attr_mep_eui48_hi.attr, - &most_dci_attr_mep_eui48_mi.attr, - &most_dci_attr_mep_eui48_lo.attr, - &most_dci_attr_arb_address.attr, - &most_dci_attr_arb_value.attr, - NULL, -}; - -/** - * DCI ktype - */ -static struct kobj_type most_dci_ktype = { - .sysfs_ops = &most_dci_sysfs_ops, - .release = most_dci_release, - .default_attrs = most_dci_def_attrs, -}; - -/** - * create_most_dci_obj - allocates a dci object - * @parent: parent kobject - * - * This creates a dci object and registers it with sysfs. - * Returns a pointer to the object or NULL when something went wrong. - */ -static struct -most_dci_obj *create_most_dci_obj(struct kobject *parent) -{ - struct most_dci_obj *most_dci = kzalloc(sizeof(*most_dci), GFP_KERNEL); - int retval; - - if (!most_dci) - return NULL; - - retval = kobject_init_and_add(&most_dci->kobj, &most_dci_ktype, parent, - "dci"); - if (retval) { - kobject_put(&most_dci->kobj); - return NULL; - } - return most_dci; -} - -/** - * destroy_most_dci_obj - DCI object release function - * @p: pointer to dci object - */ -static void destroy_most_dci_obj(struct most_dci_obj *p) -{ - kobject_put(&p->kobj); -} - -/** - * hdm_probe - probe function of USB device driver - * @interface: Interface of the attached USB device - * @id: Pointer to the USB ID table. - * - * This allocates and initializes the device instance, adds the new - * entry to the internal list, scans the USB descriptors and registers - * the interface with the core. - * Additionally, the DCI objects are created and the hardware is sync'd. - * - * Return 0 on success. In case of an error a negative number is returned. - */ -static int -hdm_probe(struct usb_interface *interface, const struct usb_device_id *id) -{ - struct usb_host_interface *usb_iface_desc = interface->cur_altsetting; - struct usb_device *usb_dev = interface_to_usbdev(interface); - struct device *dev = &usb_dev->dev; - struct most_dev *mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); - unsigned int i; - unsigned int num_endpoints; - struct most_channel_capability *tmp_cap; - struct usb_endpoint_descriptor *ep_desc; - int ret = 0; - - if (!mdev) - goto exit_ENOMEM; - - usb_set_intfdata(interface, mdev); - num_endpoints = usb_iface_desc->desc.bNumEndpoints; - mutex_init(&mdev->io_mutex); - INIT_WORK(&mdev->poll_work_obj, wq_netinfo); - timer_setup(&mdev->link_stat_timer, link_stat_timer_handler, 0); - - mdev->usb_device = usb_dev; - mdev->link_stat_timer.expires = jiffies + (2 * HZ); - - mdev->iface.mod = hdm_usb_fops.owner; - mdev->iface.interface = ITYPE_USB; - mdev->iface.configure = hdm_configure_channel; - mdev->iface.request_netinfo = hdm_request_netinfo; - mdev->iface.enqueue = hdm_enqueue; - mdev->iface.poison_channel = hdm_poison_channel; - mdev->iface.description = mdev->description; - mdev->iface.num_channels = num_endpoints; - - snprintf(mdev->description, sizeof(mdev->description), - "usb_device %d-%s:%d.%d", - usb_dev->bus->busnum, - usb_dev->devpath, - usb_dev->config->desc.bConfigurationValue, - usb_iface_desc->desc.bInterfaceNumber); - - mdev->conf = kcalloc(num_endpoints, sizeof(*mdev->conf), GFP_KERNEL); - if (!mdev->conf) - goto exit_free; - - mdev->cap = kcalloc(num_endpoints, sizeof(*mdev->cap), GFP_KERNEL); - if (!mdev->cap) - goto exit_free1; - - mdev->iface.channel_vector = mdev->cap; - mdev->iface.priv = NULL; - - mdev->ep_address = - kcalloc(num_endpoints, sizeof(*mdev->ep_address), GFP_KERNEL); - if (!mdev->ep_address) - goto exit_free2; - - mdev->busy_urbs = - kcalloc(num_endpoints, sizeof(*mdev->busy_urbs), GFP_KERNEL); - if (!mdev->busy_urbs) - goto exit_free3; - - tmp_cap = mdev->cap; - for (i = 0; i < num_endpoints; i++) { - ep_desc = &usb_iface_desc->endpoint[i].desc; - mdev->ep_address[i] = ep_desc->bEndpointAddress; - mdev->padding_active[i] = false; - mdev->is_channel_healthy[i] = true; - - snprintf(&mdev->suffix[i][0], MAX_SUFFIX_LEN, "ep%02x", - mdev->ep_address[i]); - - tmp_cap->name_suffix = &mdev->suffix[i][0]; - tmp_cap->buffer_size_packet = MAX_BUF_SIZE; - tmp_cap->buffer_size_streaming = MAX_BUF_SIZE; - tmp_cap->num_buffers_packet = BUF_CHAIN_SIZE; - tmp_cap->num_buffers_streaming = BUF_CHAIN_SIZE; - tmp_cap->data_type = MOST_CH_CONTROL | MOST_CH_ASYNC | - MOST_CH_ISOC | MOST_CH_SYNC; - if (usb_endpoint_dir_in(ep_desc)) - tmp_cap->direction = MOST_CH_RX; - else - tmp_cap->direction = MOST_CH_TX; - tmp_cap++; - init_usb_anchor(&mdev->busy_urbs[i]); - spin_lock_init(&mdev->channel_lock[i]); - } - dev_notice(dev, "claimed gadget: Vendor=%4.4x ProdID=%4.4x Bus=%02x Device=%02x\n", - le16_to_cpu(usb_dev->descriptor.idVendor), - le16_to_cpu(usb_dev->descriptor.idProduct), - usb_dev->bus->busnum, - usb_dev->devnum); - - dev_notice(dev, "device path: /sys/bus/usb/devices/%d-%s:%d.%d\n", - usb_dev->bus->busnum, - usb_dev->devpath, - usb_dev->config->desc.bConfigurationValue, - usb_iface_desc->desc.bInterfaceNumber); - - mdev->parent = most_register_interface(&mdev->iface); - if (IS_ERR(mdev->parent)) { - ret = PTR_ERR(mdev->parent); - goto exit_free4; - } - - mutex_lock(&mdev->io_mutex); - if (le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81118 || - le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81119 || - le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81210) { - /* this increments the reference count of the instance - * object of the core - */ - mdev->dci = create_most_dci_obj(mdev->parent); - if (!mdev->dci) { - mutex_unlock(&mdev->io_mutex); - most_deregister_interface(&mdev->iface); - ret = -ENOMEM; - goto exit_free4; - } - - kobject_uevent(&mdev->dci->kobj, KOBJ_ADD); - mdev->dci->usb_device = mdev->usb_device; - } - mutex_unlock(&mdev->io_mutex); - return 0; - -exit_free4: - kfree(mdev->busy_urbs); -exit_free3: - kfree(mdev->ep_address); -exit_free2: - kfree(mdev->cap); -exit_free1: - kfree(mdev->conf); -exit_free: - kfree(mdev); -exit_ENOMEM: - if (ret == 0 || ret == -ENOMEM) { - ret = -ENOMEM; - dev_err(dev, "out of memory\n"); - } - return ret; -} - -/** - * hdm_disconnect - disconnect function of USB device driver - * @interface: Interface of the attached USB device - * - * This deregisters the interface with the core, removes the kernel timer - * and frees resources. - * - * Context: hub kernel thread - */ -static void hdm_disconnect(struct usb_interface *interface) -{ - struct most_dev *mdev = usb_get_intfdata(interface); - - mutex_lock(&mdev->io_mutex); - usb_set_intfdata(interface, NULL); - mdev->usb_device = NULL; - mutex_unlock(&mdev->io_mutex); - - del_timer_sync(&mdev->link_stat_timer); - cancel_work_sync(&mdev->poll_work_obj); - - destroy_most_dci_obj(mdev->dci); - most_deregister_interface(&mdev->iface); - - kfree(mdev->busy_urbs); - kfree(mdev->cap); - kfree(mdev->conf); - kfree(mdev->ep_address); - kfree(mdev); -} - -static struct usb_driver hdm_usb = { - .name = "hdm_usb", - .id_table = usbid, - .probe = hdm_probe, - .disconnect = hdm_disconnect, -}; - -module_usb_driver(hdm_usb); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Christian Gromm "); -MODULE_DESCRIPTION("HDM_4_USB"); diff --git a/drivers/staging/most/usb/Kconfig b/drivers/staging/most/usb/Kconfig new file mode 100644 index 000000000000..ebbdb573a9a6 --- /dev/null +++ b/drivers/staging/most/usb/Kconfig @@ -0,0 +1,13 @@ +# +# MOST USB configuration +# + +config MOST_USB + tristate "USB" + depends on USB && NET + ---help--- + Say Y here if you want to connect via USB to network tranceiver. + This device driver depends on the networking AIM. + + To compile this driver as a module, choose M here: the + module will be called most_usb. diff --git a/drivers/staging/most/usb/Makefile b/drivers/staging/most/usb/Makefile new file mode 100644 index 000000000000..18d28cba4fbf --- /dev/null +++ b/drivers/staging/most/usb/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_MOST_USB) += most_usb.o + +most_usb-objs := usb.o +ccflags-y += -Idrivers/staging/ diff --git a/drivers/staging/most/usb/usb.c b/drivers/staging/most/usb/usb.c new file mode 100644 index 000000000000..b2d2abe15efe --- /dev/null +++ b/drivers/staging/most/usb/usb.c @@ -0,0 +1,1300 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * usb.c - Hardware dependent module for USB + * + * Copyright (C) 2013-2015 Microchip Technology Germany II GmbH & Co. KG + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "most/core.h" + +#define USB_MTU 512 +#define NO_ISOCHRONOUS_URB 0 +#define AV_PACKETS_PER_XACT 2 +#define BUF_CHAIN_SIZE 0xFFFF +#define MAX_NUM_ENDPOINTS 30 +#define MAX_SUFFIX_LEN 10 +#define MAX_STRING_LEN 80 +#define MAX_BUF_SIZE 0xFFFF + +#define USB_VENDOR_ID_SMSC 0x0424 /* VID: SMSC */ +#define USB_DEV_ID_BRDG 0xC001 /* PID: USB Bridge */ +#define USB_DEV_ID_OS81118 0xCF18 /* PID: USB OS81118 */ +#define USB_DEV_ID_OS81119 0xCF19 /* PID: USB OS81119 */ +#define USB_DEV_ID_OS81210 0xCF30 /* PID: USB OS81210 */ +/* DRCI Addresses */ +#define DRCI_REG_NI_STATE 0x0100 +#define DRCI_REG_PACKET_BW 0x0101 +#define DRCI_REG_NODE_ADDR 0x0102 +#define DRCI_REG_NODE_POS 0x0103 +#define DRCI_REG_MEP_FILTER 0x0140 +#define DRCI_REG_HASH_TBL0 0x0141 +#define DRCI_REG_HASH_TBL1 0x0142 +#define DRCI_REG_HASH_TBL2 0x0143 +#define DRCI_REG_HASH_TBL3 0x0144 +#define DRCI_REG_HW_ADDR_HI 0x0145 +#define DRCI_REG_HW_ADDR_MI 0x0146 +#define DRCI_REG_HW_ADDR_LO 0x0147 +#define DRCI_REG_BASE 0x1100 +#define DRCI_COMMAND 0x02 +#define DRCI_READ_REQ 0xA0 +#define DRCI_WRITE_REQ 0xA1 + +/** + * struct most_dci_obj - Direct Communication Interface + * @kobj:position in sysfs + * @usb_device: pointer to the usb device + * @reg_addr: register address for arbitrary DCI access + */ +struct most_dci_obj { + struct kobject kobj; + struct usb_device *usb_device; + u16 reg_addr; +}; + +#define to_dci_obj(p) container_of(p, struct most_dci_obj, kobj) + +struct most_dev; + +struct clear_hold_work { + struct work_struct ws; + struct most_dev *mdev; + unsigned int channel; + int pipe; +}; + +#define to_clear_hold_work(w) container_of(w, struct clear_hold_work, ws) + +/** + * struct most_dev - holds all usb interface specific stuff + * @parent: parent object in sysfs + * @usb_device: pointer to usb device + * @iface: hardware interface + * @cap: channel capabilities + * @conf: channel configuration + * @dci: direct communication interface of hardware + * @ep_address: endpoint address table + * @description: device description + * @suffix: suffix for channel name + * @channel_lock: synchronize channel access + * @padding_active: indicates channel uses padding + * @is_channel_healthy: health status table of each channel + * @busy_urbs: list of anchored items + * @io_mutex: synchronize I/O with disconnect + * @link_stat_timer: timer for link status reports + * @poll_work_obj: work for polling link status + */ +struct most_dev { + struct kobject *parent; + struct usb_device *usb_device; + struct most_interface iface; + struct most_channel_capability *cap; + struct most_channel_config *conf; + struct most_dci_obj *dci; + u8 *ep_address; + char description[MAX_STRING_LEN]; + char suffix[MAX_NUM_ENDPOINTS][MAX_SUFFIX_LEN]; + spinlock_t channel_lock[MAX_NUM_ENDPOINTS]; /* sync channel access */ + bool padding_active[MAX_NUM_ENDPOINTS]; + bool is_channel_healthy[MAX_NUM_ENDPOINTS]; + struct clear_hold_work clear_work[MAX_NUM_ENDPOINTS]; + struct usb_anchor *busy_urbs; + struct mutex io_mutex; + struct timer_list link_stat_timer; + struct work_struct poll_work_obj; + void (*on_netinfo)(struct most_interface *, unsigned char, + unsigned char *); +}; + +#define to_mdev(d) container_of(d, struct most_dev, iface) +#define to_mdev_from_work(w) container_of(w, struct most_dev, poll_work_obj) + +static void wq_clear_halt(struct work_struct *wq_obj); +static void wq_netinfo(struct work_struct *wq_obj); + +/** + * drci_rd_reg - read a DCI register + * @dev: usb device + * @reg: register address + * @buf: buffer to store data + * + * This is reads data from INIC's direct register communication interface + */ +static inline int drci_rd_reg(struct usb_device *dev, u16 reg, u16 *buf) +{ + int retval; + __le16 *dma_buf = kzalloc(sizeof(*dma_buf), GFP_KERNEL); + u8 req_type = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE; + + if (!dma_buf) + return -ENOMEM; + + retval = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + DRCI_READ_REQ, req_type, + 0x0000, + reg, dma_buf, sizeof(*dma_buf), 5 * HZ); + *buf = le16_to_cpu(*dma_buf); + kfree(dma_buf); + + return retval; +} + +/** + * drci_wr_reg - write a DCI register + * @dev: usb device + * @reg: register address + * @data: data to write + * + * This is writes data to INIC's direct register communication interface + */ +static inline int drci_wr_reg(struct usb_device *dev, u16 reg, u16 data) +{ + return usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + DRCI_WRITE_REQ, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + data, + reg, + NULL, + 0, + 5 * HZ); +} + +static inline int start_sync_ep(struct usb_device *usb_dev, u16 ep) +{ + return drci_wr_reg(usb_dev, DRCI_REG_BASE + DRCI_COMMAND + ep * 16, 1); +} + +/** + * get_stream_frame_size - calculate frame size of current configuration + * @cfg: channel configuration + */ +static unsigned int get_stream_frame_size(struct most_channel_config *cfg) +{ + unsigned int frame_size = 0; + unsigned int sub_size = cfg->subbuffer_size; + + if (!sub_size) { + pr_warn("Misconfig: Subbuffer size zero.\n"); + return frame_size; + } + switch (cfg->data_type) { + case MOST_CH_ISOC: + frame_size = AV_PACKETS_PER_XACT * sub_size; + break; + case MOST_CH_SYNC: + if (cfg->packets_per_xact == 0) { + pr_warn("Misconfig: Packets per XACT zero\n"); + frame_size = 0; + } else if (cfg->packets_per_xact == 0xFF) { + frame_size = (USB_MTU / sub_size) * sub_size; + } else { + frame_size = cfg->packets_per_xact * sub_size; + } + break; + default: + pr_warn("Query frame size of non-streaming channel\n"); + break; + } + return frame_size; +} + +/** + * hdm_poison_channel - mark buffers of this channel as invalid + * @iface: pointer to the interface + * @channel: channel ID + * + * This unlinks all URBs submitted to the HCD, + * calls the associated completion function of the core and removes + * them from the list. + * + * Returns 0 on success or error code otherwise. + */ +static int hdm_poison_channel(struct most_interface *iface, int channel) +{ + struct most_dev *mdev = to_mdev(iface); + unsigned long flags; + spinlock_t *lock; /* temp. lock */ + + if (unlikely(!iface)) { + dev_warn(&mdev->usb_device->dev, "Poison: Bad interface.\n"); + return -EIO; + } + if (unlikely(channel < 0 || channel >= iface->num_channels)) { + dev_warn(&mdev->usb_device->dev, "Channel ID out of range.\n"); + return -ECHRNG; + } + + lock = mdev->channel_lock + channel; + spin_lock_irqsave(lock, flags); + mdev->is_channel_healthy[channel] = false; + spin_unlock_irqrestore(lock, flags); + + cancel_work_sync(&mdev->clear_work[channel].ws); + + mutex_lock(&mdev->io_mutex); + usb_kill_anchored_urbs(&mdev->busy_urbs[channel]); + if (mdev->padding_active[channel]) + mdev->padding_active[channel] = false; + + if (mdev->conf[channel].data_type == MOST_CH_ASYNC) { + del_timer_sync(&mdev->link_stat_timer); + cancel_work_sync(&mdev->poll_work_obj); + } + mutex_unlock(&mdev->io_mutex); + return 0; +} + +/** + * hdm_add_padding - add padding bytes + * @mdev: most device + * @channel: channel ID + * @mbo: buffer object + * + * This inserts the INIC hardware specific padding bytes into a streaming + * channel's buffer + */ +static int hdm_add_padding(struct most_dev *mdev, int channel, struct mbo *mbo) +{ + struct most_channel_config *conf = &mdev->conf[channel]; + unsigned int frame_size = get_stream_frame_size(conf); + unsigned int j, num_frames; + + if (!frame_size) + return -EIO; + num_frames = mbo->buffer_length / frame_size; + + if (num_frames < 1) { + dev_err(&mdev->usb_device->dev, + "Missed minimal transfer unit.\n"); + return -EIO; + } + + for (j = num_frames - 1; j > 0; j--) + memmove(mbo->virt_address + j * USB_MTU, + mbo->virt_address + j * frame_size, + frame_size); + mbo->buffer_length = num_frames * USB_MTU; + return 0; +} + +/** + * hdm_remove_padding - remove padding bytes + * @mdev: most device + * @channel: channel ID + * @mbo: buffer object + * + * This takes the INIC hardware specific padding bytes off a streaming + * channel's buffer. + */ +static int hdm_remove_padding(struct most_dev *mdev, int channel, + struct mbo *mbo) +{ + struct most_channel_config *const conf = &mdev->conf[channel]; + unsigned int frame_size = get_stream_frame_size(conf); + unsigned int j, num_frames; + + if (!frame_size) + return -EIO; + num_frames = mbo->processed_length / USB_MTU; + + for (j = 1; j < num_frames; j++) + memmove(mbo->virt_address + frame_size * j, + mbo->virt_address + USB_MTU * j, + frame_size); + + mbo->processed_length = frame_size * num_frames; + return 0; +} + +/** + * hdm_write_completion - completion function for submitted Tx URBs + * @urb: the URB that has been completed + * + * This checks the status of the completed URB. In case the URB has been + * unlinked before, it is immediately freed. On any other error the MBO + * transfer flag is set. On success it frees allocated resources and calls + * the completion function. + * + * Context: interrupt! + */ +static void hdm_write_completion(struct urb *urb) +{ + struct mbo *mbo = urb->context; + struct most_dev *mdev = to_mdev(mbo->ifp); + unsigned int channel = mbo->hdm_channel_id; + struct device *dev = &mdev->usb_device->dev; + spinlock_t *lock = mdev->channel_lock + channel; + unsigned long flags; + + spin_lock_irqsave(lock, flags); + + mbo->processed_length = 0; + mbo->status = MBO_E_INVAL; + if (likely(mdev->is_channel_healthy[channel])) { + switch (urb->status) { + case 0: + case -ESHUTDOWN: + mbo->processed_length = urb->actual_length; + mbo->status = MBO_SUCCESS; + break; + case -EPIPE: + dev_warn(dev, "Broken OUT pipe detected\n"); + mdev->is_channel_healthy[channel] = false; + mdev->clear_work[channel].pipe = urb->pipe; + schedule_work(&mdev->clear_work[channel].ws); + break; + case -ENODEV: + case -EPROTO: + mbo->status = MBO_E_CLOSE; + break; + } + } + + spin_unlock_irqrestore(lock, flags); + + if (likely(mbo->complete)) + mbo->complete(mbo); + usb_free_urb(urb); +} + +/** + * hdm_read_completion - completion function for submitted Rx URBs + * @urb: the URB that has been completed + * + * This checks the status of the completed URB. In case the URB has been + * unlinked before it is immediately freed. On any other error the MBO transfer + * flag is set. On success it frees allocated resources, removes + * padding bytes -if necessary- and calls the completion function. + * + * Context: interrupt! + * + * ************************************************************************** + * Error codes returned by in urb->status + * or in iso_frame_desc[n].status (for ISO) + * ************************************************************************* + * + * USB device drivers may only test urb status values in completion handlers. + * This is because otherwise there would be a race between HCDs updating + * these values on one CPU, and device drivers testing them on another CPU. + * + * A transfer's actual_length may be positive even when an error has been + * reported. That's because transfers often involve several packets, so that + * one or more packets could finish before an error stops further endpoint I/O. + * + * For isochronous URBs, the urb status value is non-zero only if the URB is + * unlinked, the device is removed, the host controller is disabled or the total + * transferred length is less than the requested length and the URB_SHORT_NOT_OK + * flag is set. Completion handlers for isochronous URBs should only see + * urb->status set to zero, -ENOENT, -ECONNRESET, -ESHUTDOWN, or -EREMOTEIO. + * Individual frame descriptor status fields may report more status codes. + * + * + * 0 Transfer completed successfully + * + * -ENOENT URB was synchronously unlinked by usb_unlink_urb + * + * -EINPROGRESS URB still pending, no results yet + * (That is, if drivers see this it's a bug.) + * + * -EPROTO (*, **) a) bitstuff error + * b) no response packet received within the + * prescribed bus turn-around time + * c) unknown USB error + * + * -EILSEQ (*, **) a) CRC mismatch + * b) no response packet received within the + * prescribed bus turn-around time + * c) unknown USB error + * + * Note that often the controller hardware does not + * distinguish among cases a), b), and c), so a + * driver cannot tell whether there was a protocol + * error, a failure to respond (often caused by + * device disconnect), or some other fault. + * + * -ETIME (**) No response packet received within the prescribed + * bus turn-around time. This error may instead be + * reported as -EPROTO or -EILSEQ. + * + * -ETIMEDOUT Synchronous USB message functions use this code + * to indicate timeout expired before the transfer + * completed, and no other error was reported by HC. + * + * -EPIPE (**) Endpoint stalled. For non-control endpoints, + * reset this status with usb_clear_halt(). + * + * -ECOMM During an IN transfer, the host controller + * received data from an endpoint faster than it + * could be written to system memory + * + * -ENOSR During an OUT transfer, the host controller + * could not retrieve data from system memory fast + * enough to keep up with the USB data rate + * + * -EOVERFLOW (*) The amount of data returned by the endpoint was + * greater than either the max packet size of the + * endpoint or the remaining buffer size. "Babble". + * + * -EREMOTEIO The data read from the endpoint did not fill the + * specified buffer, and URB_SHORT_NOT_OK was set in + * urb->transfer_flags. + * + * -ENODEV Device was removed. Often preceded by a burst of + * other errors, since the hub driver doesn't detect + * device removal events immediately. + * + * -EXDEV ISO transfer only partially completed + * (only set in iso_frame_desc[n].status, not urb->status) + * + * -EINVAL ISO madness, if this happens: Log off and go home + * + * -ECONNRESET URB was asynchronously unlinked by usb_unlink_urb + * + * -ESHUTDOWN The device or host controller has been disabled due + * to some problem that could not be worked around, + * such as a physical disconnect. + * + * + * (*) Error codes like -EPROTO, -EILSEQ and -EOVERFLOW normally indicate + * hardware problems such as bad devices (including firmware) or cables. + * + * (**) This is also one of several codes that different kinds of host + * controller use to indicate a transfer has failed because of device + * disconnect. In the interval before the hub driver starts disconnect + * processing, devices may receive such fault reports for every request. + * + * See + */ +static void hdm_read_completion(struct urb *urb) +{ + struct mbo *mbo = urb->context; + struct most_dev *mdev = to_mdev(mbo->ifp); + unsigned int channel = mbo->hdm_channel_id; + struct device *dev = &mdev->usb_device->dev; + spinlock_t *lock = mdev->channel_lock + channel; + unsigned long flags; + + spin_lock_irqsave(lock, flags); + + mbo->processed_length = 0; + mbo->status = MBO_E_INVAL; + if (likely(mdev->is_channel_healthy[channel])) { + switch (urb->status) { + case 0: + case -ESHUTDOWN: + mbo->processed_length = urb->actual_length; + mbo->status = MBO_SUCCESS; + if (mdev->padding_active[channel] && + hdm_remove_padding(mdev, channel, mbo)) { + mbo->processed_length = 0; + mbo->status = MBO_E_INVAL; + } + break; + case -EPIPE: + dev_warn(dev, "Broken IN pipe detected\n"); + mdev->is_channel_healthy[channel] = false; + mdev->clear_work[channel].pipe = urb->pipe; + schedule_work(&mdev->clear_work[channel].ws); + break; + case -ENODEV: + case -EPROTO: + mbo->status = MBO_E_CLOSE; + break; + case -EOVERFLOW: + dev_warn(dev, "Babble on IN pipe detected\n"); + break; + } + } + + spin_unlock_irqrestore(lock, flags); + + if (likely(mbo->complete)) + mbo->complete(mbo); + usb_free_urb(urb); +} + +/** + * hdm_enqueue - receive a buffer to be used for data transfer + * @iface: interface to enqueue to + * @channel: ID of the channel + * @mbo: pointer to the buffer object + * + * This allocates a new URB and fills it according to the channel + * that is being used for transmission of data. Before the URB is + * submitted it is stored in the private anchor list. + * + * Returns 0 on success. On any error the URB is freed and a error code + * is returned. + * + * Context: Could in _some_ cases be interrupt! + */ +static int hdm_enqueue(struct most_interface *iface, int channel, + struct mbo *mbo) +{ + struct most_dev *mdev; + struct most_channel_config *conf; + struct device *dev; + int retval = 0; + struct urb *urb; + unsigned long length; + void *virt_address; + + if (unlikely(!iface || !mbo)) + return -EIO; + if (unlikely(iface->num_channels <= channel || channel < 0)) + return -ECHRNG; + + mdev = to_mdev(iface); + conf = &mdev->conf[channel]; + dev = &mdev->usb_device->dev; + + if (!mdev->usb_device) + return -ENODEV; + + urb = usb_alloc_urb(NO_ISOCHRONOUS_URB, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + if ((conf->direction & MOST_CH_TX) && mdev->padding_active[channel] && + hdm_add_padding(mdev, channel, mbo)) { + retval = -EIO; + goto _error; + } + + urb->transfer_dma = mbo->bus_address; + virt_address = mbo->virt_address; + length = mbo->buffer_length; + + if (conf->direction & MOST_CH_TX) { + usb_fill_bulk_urb(urb, mdev->usb_device, + usb_sndbulkpipe(mdev->usb_device, + mdev->ep_address[channel]), + virt_address, + length, + hdm_write_completion, + mbo); + if (conf->data_type != MOST_CH_ISOC) + urb->transfer_flags |= URB_ZERO_PACKET; + } else { + usb_fill_bulk_urb(urb, mdev->usb_device, + usb_rcvbulkpipe(mdev->usb_device, + mdev->ep_address[channel]), + virt_address, + length + conf->extra_len, + hdm_read_completion, + mbo); + } + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + usb_anchor_urb(urb, &mdev->busy_urbs[channel]); + + retval = usb_submit_urb(urb, GFP_KERNEL); + if (retval) { + dev_err(dev, "URB submit failed with error %d.\n", retval); + goto _error_1; + } + return 0; + +_error_1: + usb_unanchor_urb(urb); +_error: + usb_free_urb(urb); + return retval; +} + +/** + * hdm_configure_channel - receive channel configuration from core + * @iface: interface + * @channel: channel ID + * @conf: structure that holds the configuration information + * + * The attached network interface controller (NIC) supports a padding mode + * to avoid short packets on USB, hence increasing the performance due to a + * lower interrupt load. This mode is default for synchronous data and can + * be switched on for isochronous data. In case padding is active the + * driver needs to know the frame size of the payload in order to calculate + * the number of bytes it needs to pad when transmitting or to cut off when + * receiving data. + * + */ +static int hdm_configure_channel(struct most_interface *iface, int channel, + struct most_channel_config *conf) +{ + unsigned int num_frames; + unsigned int frame_size; + struct most_dev *mdev = to_mdev(iface); + struct device *dev = &mdev->usb_device->dev; + + mdev->is_channel_healthy[channel] = true; + mdev->clear_work[channel].channel = channel; + mdev->clear_work[channel].mdev = mdev; + INIT_WORK(&mdev->clear_work[channel].ws, wq_clear_halt); + + if (unlikely(!iface || !conf)) { + dev_err(dev, "Bad interface or config pointer.\n"); + return -EINVAL; + } + if (unlikely(channel < 0 || channel >= iface->num_channels)) { + dev_err(dev, "Channel ID out of range.\n"); + return -EINVAL; + } + if (!conf->num_buffers || !conf->buffer_size) { + dev_err(dev, "Misconfig: buffer size or #buffers zero.\n"); + return -EINVAL; + } + + if (conf->data_type != MOST_CH_SYNC && + !(conf->data_type == MOST_CH_ISOC && + conf->packets_per_xact != 0xFF)) { + mdev->padding_active[channel] = false; + /* + * Since the NIC's padding mode is not going to be + * used, we can skip the frame size calculations and + * move directly on to exit. + */ + goto exit; + } + + mdev->padding_active[channel] = true; + + frame_size = get_stream_frame_size(conf); + if (frame_size == 0 || frame_size > USB_MTU) { + dev_warn(dev, "Misconfig: frame size wrong\n"); + return -EINVAL; + } + + num_frames = conf->buffer_size / frame_size; + + if (conf->buffer_size % frame_size) { + u16 old_size = conf->buffer_size; + + conf->buffer_size = num_frames * frame_size; + dev_warn(dev, "%s: fixed buffer size (%d -> %d)\n", + mdev->suffix[channel], old_size, conf->buffer_size); + } + + /* calculate extra length to comply w/ HW padding */ + conf->extra_len = num_frames * (USB_MTU - frame_size); + +exit: + mdev->conf[channel] = *conf; + if (conf->data_type == MOST_CH_ASYNC) { + u16 ep = mdev->ep_address[channel]; + + if (start_sync_ep(mdev->usb_device, ep) < 0) + dev_warn(dev, "sync for ep%02x failed", ep); + } + return 0; +} + +/** + * hdm_request_netinfo - request network information + * @iface: pointer to interface + * @channel: channel ID + * + * This is used as trigger to set up the link status timer that + * polls for the NI state of the INIC every 2 seconds. + * + */ +static void hdm_request_netinfo(struct most_interface *iface, int channel, + void (*on_netinfo)(struct most_interface *, + unsigned char, + unsigned char *)) +{ + struct most_dev *mdev; + + BUG_ON(!iface); + mdev = to_mdev(iface); + mdev->on_netinfo = on_netinfo; + if (!on_netinfo) + return; + + mdev->link_stat_timer.expires = jiffies + HZ; + mod_timer(&mdev->link_stat_timer, mdev->link_stat_timer.expires); +} + +/** + * link_stat_timer_handler - schedule work obtaining mac address and link status + * @data: pointer to USB device instance + * + * The handler runs in interrupt context. That's why we need to defer the + * tasks to a work queue. + */ +static void link_stat_timer_handler(struct timer_list *t) +{ + struct most_dev *mdev = from_timer(mdev, t, link_stat_timer); + + schedule_work(&mdev->poll_work_obj); + mdev->link_stat_timer.expires = jiffies + (2 * HZ); + add_timer(&mdev->link_stat_timer); +} + +/** + * wq_netinfo - work queue function to deliver latest networking information + * @wq_obj: object that holds data for our deferred work to do + * + * This retrieves the network interface status of the USB INIC + */ +static void wq_netinfo(struct work_struct *wq_obj) +{ + struct most_dev *mdev = to_mdev_from_work(wq_obj); + struct usb_device *usb_device = mdev->usb_device; + struct device *dev = &usb_device->dev; + u16 hi, mi, lo, link; + u8 hw_addr[6]; + + if (drci_rd_reg(usb_device, DRCI_REG_HW_ADDR_HI, &hi) < 0) { + dev_err(dev, "Vendor request 'hw_addr_hi' failed\n"); + return; + } + + if (drci_rd_reg(usb_device, DRCI_REG_HW_ADDR_MI, &mi) < 0) { + dev_err(dev, "Vendor request 'hw_addr_mid' failed\n"); + return; + } + + if (drci_rd_reg(usb_device, DRCI_REG_HW_ADDR_LO, &lo) < 0) { + dev_err(dev, "Vendor request 'hw_addr_low' failed\n"); + return; + } + + if (drci_rd_reg(usb_device, DRCI_REG_NI_STATE, &link) < 0) { + dev_err(dev, "Vendor request 'link status' failed\n"); + return; + } + + hw_addr[0] = hi >> 8; + hw_addr[1] = hi; + hw_addr[2] = mi >> 8; + hw_addr[3] = mi; + hw_addr[4] = lo >> 8; + hw_addr[5] = lo; + + if (mdev->on_netinfo) + mdev->on_netinfo(&mdev->iface, link, hw_addr); +} + +/** + * wq_clear_halt - work queue function + * @wq_obj: work_struct object to execute + * + * This sends a clear_halt to the given USB pipe. + */ +static void wq_clear_halt(struct work_struct *wq_obj) +{ + struct clear_hold_work *clear_work = to_clear_hold_work(wq_obj); + struct most_dev *mdev = clear_work->mdev; + unsigned int channel = clear_work->channel; + int pipe = clear_work->pipe; + + mutex_lock(&mdev->io_mutex); + most_stop_enqueue(&mdev->iface, channel); + usb_kill_anchored_urbs(&mdev->busy_urbs[channel]); + if (usb_clear_halt(mdev->usb_device, pipe)) + dev_warn(&mdev->usb_device->dev, "Failed to reset endpoint.\n"); + + mdev->is_channel_healthy[channel] = true; + most_resume_enqueue(&mdev->iface, channel); + mutex_unlock(&mdev->io_mutex); +} + +/** + * hdm_usb_fops - file operation table for USB driver + */ +static const struct file_operations hdm_usb_fops = { + .owner = THIS_MODULE, +}; + +/** + * usb_device_id - ID table for HCD device probing + */ +static const struct usb_device_id usbid[] = { + { USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_BRDG), }, + { USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_OS81118), }, + { USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_OS81119), }, + { USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_OS81210), }, + { } /* Terminating entry */ +}; + +#define MOST_DCI_RO_ATTR(_name) \ + struct most_dci_attribute most_dci_attr_##_name = \ + __ATTR(_name, 0444, show_value, NULL) + +#define MOST_DCI_ATTR(_name) \ + struct most_dci_attribute most_dci_attr_##_name = \ + __ATTR(_name, 0644, show_value, store_value) + +#define MOST_DCI_WO_ATTR(_name) \ + struct most_dci_attribute most_dci_attr_##_name = \ + __ATTR(_name, 0200, NULL, store_value) + +/** + * struct most_dci_attribute - to access the attributes of a dci object + * @attr: attributes of a dci object + * @show: pointer to the show function + * @store: pointer to the store function + */ +struct most_dci_attribute { + struct attribute attr; + ssize_t (*show)(struct most_dci_obj *d, + struct most_dci_attribute *attr, + char *buf); + ssize_t (*store)(struct most_dci_obj *d, + struct most_dci_attribute *attr, + const char *buf, + size_t count); +}; + +#define to_dci_attr(a) container_of(a, struct most_dci_attribute, attr) + +/** + * dci_attr_show - show function for dci object + * @kobj: pointer to kobject + * @attr: pointer to attribute struct + * @buf: buffer + */ +static ssize_t dci_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct most_dci_attribute *dci_attr = to_dci_attr(attr); + struct most_dci_obj *dci_obj = to_dci_obj(kobj); + + if (!dci_attr->show) + return -EIO; + + return dci_attr->show(dci_obj, dci_attr, buf); +} + +/** + * dci_attr_store - store function for dci object + * @kobj: pointer to kobject + * @attr: pointer to attribute struct + * @buf: buffer + * @len: length of buffer + */ +static ssize_t dci_attr_store(struct kobject *kobj, + struct attribute *attr, + const char *buf, + size_t len) +{ + struct most_dci_attribute *dci_attr = to_dci_attr(attr); + struct most_dci_obj *dci_obj = to_dci_obj(kobj); + + if (!dci_attr->store) + return -EIO; + + return dci_attr->store(dci_obj, dci_attr, buf, len); +} + +static const struct sysfs_ops most_dci_sysfs_ops = { + .show = dci_attr_show, + .store = dci_attr_store, +}; + +/** + * most_dci_release - release function for dci object + * @kobj: pointer to kobject + * + * This frees the memory allocated for the dci object + */ +static void most_dci_release(struct kobject *kobj) +{ + struct most_dci_obj *dci_obj = to_dci_obj(kobj); + + kfree(dci_obj); +} + +struct regs { + const char *name; + u16 reg; +}; + +static const struct regs ro_regs[] = { + { "ni_state", DRCI_REG_NI_STATE }, + { "packet_bandwidth", DRCI_REG_PACKET_BW }, + { "node_address", DRCI_REG_NODE_ADDR }, + { "node_position", DRCI_REG_NODE_POS }, +}; + +static const struct regs rw_regs[] = { + { "mep_filter", DRCI_REG_MEP_FILTER }, + { "mep_hash0", DRCI_REG_HASH_TBL0 }, + { "mep_hash1", DRCI_REG_HASH_TBL1 }, + { "mep_hash2", DRCI_REG_HASH_TBL2 }, + { "mep_hash3", DRCI_REG_HASH_TBL3 }, + { "mep_eui48_hi", DRCI_REG_HW_ADDR_HI }, + { "mep_eui48_mi", DRCI_REG_HW_ADDR_MI }, + { "mep_eui48_lo", DRCI_REG_HW_ADDR_LO }, +}; + +static int get_stat_reg_addr(const struct regs *regs, int size, + const char *name, u16 *reg_addr) +{ + int i; + + for (i = 0; i < size; i++) { + if (!strcmp(name, regs[i].name)) { + *reg_addr = regs[i].reg; + return 0; + } + } + return -EFAULT; +} + +#define get_static_reg_addr(regs, name, reg_addr) \ + get_stat_reg_addr(regs, ARRAY_SIZE(regs), name, reg_addr) + +static ssize_t show_value(struct most_dci_obj *dci_obj, + struct most_dci_attribute *attr, char *buf) +{ + const char *name = attr->attr.name; + u16 val; + u16 reg_addr; + int err; + + if (!strcmp(name, "arb_address")) + return snprintf(buf, PAGE_SIZE, "%04x\n", dci_obj->reg_addr); + + if (!strcmp(name, "arb_value")) + reg_addr = dci_obj->reg_addr; + else if (get_static_reg_addr(ro_regs, name, ®_addr) && + get_static_reg_addr(rw_regs, name, ®_addr)) + return -EFAULT; + + err = drci_rd_reg(dci_obj->usb_device, reg_addr, &val); + if (err < 0) + return err; + + return snprintf(buf, PAGE_SIZE, "%04x\n", val); +} + +static ssize_t store_value(struct most_dci_obj *dci_obj, + struct most_dci_attribute *attr, + const char *buf, size_t count) +{ + u16 val; + u16 reg_addr; + const char *name = attr->attr.name; + struct usb_device *usb_dev = dci_obj->usb_device; + int err = kstrtou16(buf, 16, &val); + + if (err) + return err; + + if (!strcmp(name, "arb_address")) { + dci_obj->reg_addr = val; + return count; + } + + if (!strcmp(name, "arb_value")) + err = drci_wr_reg(usb_dev, dci_obj->reg_addr, val); + else if (!strcmp(name, "sync_ep")) + err = start_sync_ep(usb_dev, val); + else if (!get_static_reg_addr(rw_regs, name, ®_addr)) + err = drci_wr_reg(usb_dev, reg_addr, val); + else + return -EFAULT; + + if (err < 0) + return err; + + return count; +} + +static MOST_DCI_RO_ATTR(ni_state); +static MOST_DCI_RO_ATTR(packet_bandwidth); +static MOST_DCI_RO_ATTR(node_address); +static MOST_DCI_RO_ATTR(node_position); +static MOST_DCI_WO_ATTR(sync_ep); +static MOST_DCI_ATTR(mep_filter); +static MOST_DCI_ATTR(mep_hash0); +static MOST_DCI_ATTR(mep_hash1); +static MOST_DCI_ATTR(mep_hash2); +static MOST_DCI_ATTR(mep_hash3); +static MOST_DCI_ATTR(mep_eui48_hi); +static MOST_DCI_ATTR(mep_eui48_mi); +static MOST_DCI_ATTR(mep_eui48_lo); +static MOST_DCI_ATTR(arb_address); +static MOST_DCI_ATTR(arb_value); + +/** + * most_dci_def_attrs - array of default attribute files of the dci object + */ +static struct attribute *most_dci_def_attrs[] = { + &most_dci_attr_ni_state.attr, + &most_dci_attr_packet_bandwidth.attr, + &most_dci_attr_node_address.attr, + &most_dci_attr_node_position.attr, + &most_dci_attr_sync_ep.attr, + &most_dci_attr_mep_filter.attr, + &most_dci_attr_mep_hash0.attr, + &most_dci_attr_mep_hash1.attr, + &most_dci_attr_mep_hash2.attr, + &most_dci_attr_mep_hash3.attr, + &most_dci_attr_mep_eui48_hi.attr, + &most_dci_attr_mep_eui48_mi.attr, + &most_dci_attr_mep_eui48_lo.attr, + &most_dci_attr_arb_address.attr, + &most_dci_attr_arb_value.attr, + NULL, +}; + +/** + * DCI ktype + */ +static struct kobj_type most_dci_ktype = { + .sysfs_ops = &most_dci_sysfs_ops, + .release = most_dci_release, + .default_attrs = most_dci_def_attrs, +}; + +/** + * create_most_dci_obj - allocates a dci object + * @parent: parent kobject + * + * This creates a dci object and registers it with sysfs. + * Returns a pointer to the object or NULL when something went wrong. + */ +static struct +most_dci_obj *create_most_dci_obj(struct kobject *parent) +{ + struct most_dci_obj *most_dci = kzalloc(sizeof(*most_dci), GFP_KERNEL); + int retval; + + if (!most_dci) + return NULL; + + retval = kobject_init_and_add(&most_dci->kobj, &most_dci_ktype, parent, + "dci"); + if (retval) { + kobject_put(&most_dci->kobj); + return NULL; + } + return most_dci; +} + +/** + * destroy_most_dci_obj - DCI object release function + * @p: pointer to dci object + */ +static void destroy_most_dci_obj(struct most_dci_obj *p) +{ + kobject_put(&p->kobj); +} + +/** + * hdm_probe - probe function of USB device driver + * @interface: Interface of the attached USB device + * @id: Pointer to the USB ID table. + * + * This allocates and initializes the device instance, adds the new + * entry to the internal list, scans the USB descriptors and registers + * the interface with the core. + * Additionally, the DCI objects are created and the hardware is sync'd. + * + * Return 0 on success. In case of an error a negative number is returned. + */ +static int +hdm_probe(struct usb_interface *interface, const struct usb_device_id *id) +{ + struct usb_host_interface *usb_iface_desc = interface->cur_altsetting; + struct usb_device *usb_dev = interface_to_usbdev(interface); + struct device *dev = &usb_dev->dev; + struct most_dev *mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); + unsigned int i; + unsigned int num_endpoints; + struct most_channel_capability *tmp_cap; + struct usb_endpoint_descriptor *ep_desc; + int ret = 0; + + if (!mdev) + goto exit_ENOMEM; + + usb_set_intfdata(interface, mdev); + num_endpoints = usb_iface_desc->desc.bNumEndpoints; + mutex_init(&mdev->io_mutex); + INIT_WORK(&mdev->poll_work_obj, wq_netinfo); + timer_setup(&mdev->link_stat_timer, link_stat_timer_handler, 0); + + mdev->usb_device = usb_dev; + mdev->link_stat_timer.expires = jiffies + (2 * HZ); + + mdev->iface.mod = hdm_usb_fops.owner; + mdev->iface.interface = ITYPE_USB; + mdev->iface.configure = hdm_configure_channel; + mdev->iface.request_netinfo = hdm_request_netinfo; + mdev->iface.enqueue = hdm_enqueue; + mdev->iface.poison_channel = hdm_poison_channel; + mdev->iface.description = mdev->description; + mdev->iface.num_channels = num_endpoints; + + snprintf(mdev->description, sizeof(mdev->description), + "usb_device %d-%s:%d.%d", + usb_dev->bus->busnum, + usb_dev->devpath, + usb_dev->config->desc.bConfigurationValue, + usb_iface_desc->desc.bInterfaceNumber); + + mdev->conf = kcalloc(num_endpoints, sizeof(*mdev->conf), GFP_KERNEL); + if (!mdev->conf) + goto exit_free; + + mdev->cap = kcalloc(num_endpoints, sizeof(*mdev->cap), GFP_KERNEL); + if (!mdev->cap) + goto exit_free1; + + mdev->iface.channel_vector = mdev->cap; + mdev->iface.priv = NULL; + + mdev->ep_address = + kcalloc(num_endpoints, sizeof(*mdev->ep_address), GFP_KERNEL); + if (!mdev->ep_address) + goto exit_free2; + + mdev->busy_urbs = + kcalloc(num_endpoints, sizeof(*mdev->busy_urbs), GFP_KERNEL); + if (!mdev->busy_urbs) + goto exit_free3; + + tmp_cap = mdev->cap; + for (i = 0; i < num_endpoints; i++) { + ep_desc = &usb_iface_desc->endpoint[i].desc; + mdev->ep_address[i] = ep_desc->bEndpointAddress; + mdev->padding_active[i] = false; + mdev->is_channel_healthy[i] = true; + + snprintf(&mdev->suffix[i][0], MAX_SUFFIX_LEN, "ep%02x", + mdev->ep_address[i]); + + tmp_cap->name_suffix = &mdev->suffix[i][0]; + tmp_cap->buffer_size_packet = MAX_BUF_SIZE; + tmp_cap->buffer_size_streaming = MAX_BUF_SIZE; + tmp_cap->num_buffers_packet = BUF_CHAIN_SIZE; + tmp_cap->num_buffers_streaming = BUF_CHAIN_SIZE; + tmp_cap->data_type = MOST_CH_CONTROL | MOST_CH_ASYNC | + MOST_CH_ISOC | MOST_CH_SYNC; + if (usb_endpoint_dir_in(ep_desc)) + tmp_cap->direction = MOST_CH_RX; + else + tmp_cap->direction = MOST_CH_TX; + tmp_cap++; + init_usb_anchor(&mdev->busy_urbs[i]); + spin_lock_init(&mdev->channel_lock[i]); + } + dev_notice(dev, "claimed gadget: Vendor=%4.4x ProdID=%4.4x Bus=%02x Device=%02x\n", + le16_to_cpu(usb_dev->descriptor.idVendor), + le16_to_cpu(usb_dev->descriptor.idProduct), + usb_dev->bus->busnum, + usb_dev->devnum); + + dev_notice(dev, "device path: /sys/bus/usb/devices/%d-%s:%d.%d\n", + usb_dev->bus->busnum, + usb_dev->devpath, + usb_dev->config->desc.bConfigurationValue, + usb_iface_desc->desc.bInterfaceNumber); + + mdev->parent = most_register_interface(&mdev->iface); + if (IS_ERR(mdev->parent)) { + ret = PTR_ERR(mdev->parent); + goto exit_free4; + } + + mutex_lock(&mdev->io_mutex); + if (le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81118 || + le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81119 || + le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81210) { + /* this increments the reference count of the instance + * object of the core + */ + mdev->dci = create_most_dci_obj(mdev->parent); + if (!mdev->dci) { + mutex_unlock(&mdev->io_mutex); + most_deregister_interface(&mdev->iface); + ret = -ENOMEM; + goto exit_free4; + } + + kobject_uevent(&mdev->dci->kobj, KOBJ_ADD); + mdev->dci->usb_device = mdev->usb_device; + } + mutex_unlock(&mdev->io_mutex); + return 0; + +exit_free4: + kfree(mdev->busy_urbs); +exit_free3: + kfree(mdev->ep_address); +exit_free2: + kfree(mdev->cap); +exit_free1: + kfree(mdev->conf); +exit_free: + kfree(mdev); +exit_ENOMEM: + if (ret == 0 || ret == -ENOMEM) { + ret = -ENOMEM; + dev_err(dev, "out of memory\n"); + } + return ret; +} + +/** + * hdm_disconnect - disconnect function of USB device driver + * @interface: Interface of the attached USB device + * + * This deregisters the interface with the core, removes the kernel timer + * and frees resources. + * + * Context: hub kernel thread + */ +static void hdm_disconnect(struct usb_interface *interface) +{ + struct most_dev *mdev = usb_get_intfdata(interface); + + mutex_lock(&mdev->io_mutex); + usb_set_intfdata(interface, NULL); + mdev->usb_device = NULL; + mutex_unlock(&mdev->io_mutex); + + del_timer_sync(&mdev->link_stat_timer); + cancel_work_sync(&mdev->poll_work_obj); + + destroy_most_dci_obj(mdev->dci); + most_deregister_interface(&mdev->iface); + + kfree(mdev->busy_urbs); + kfree(mdev->cap); + kfree(mdev->conf); + kfree(mdev->ep_address); + kfree(mdev); +} + +static struct usb_driver hdm_usb = { + .name = "hdm_usb", + .id_table = usbid, + .probe = hdm_probe, + .disconnect = hdm_disconnect, +}; + +module_usb_driver(hdm_usb); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Christian Gromm "); +MODULE_DESCRIPTION("HDM_4_USB"); -- cgit From 2c22cdfb4e817a7be48419c3c7b1423ee8f5df38 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:42 +0100 Subject: staging: most: video: rename module This patch renames the folder of the video module. It is needed to clean up the directory layout of the driver. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/Kconfig | 2 +- drivers/staging/most/aim-v4l2/Kconfig | 12 - drivers/staging/most/aim-v4l2/video.c | 610 ---------------------------------- drivers/staging/most/video/Kconfig | 12 + drivers/staging/most/video/Makefile | 4 + drivers/staging/most/video/video.c | 610 ++++++++++++++++++++++++++++++++++ 6 files changed, 627 insertions(+), 623 deletions(-) delete mode 100644 drivers/staging/most/aim-v4l2/Kconfig delete mode 100644 drivers/staging/most/aim-v4l2/video.c create mode 100644 drivers/staging/most/video/Kconfig create mode 100644 drivers/staging/most/video/Makefile create mode 100644 drivers/staging/most/video/video.c (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/Kconfig b/drivers/staging/most/Kconfig index 86d4f12596bb..20047abbe560 100644 --- a/drivers/staging/most/Kconfig +++ b/drivers/staging/most/Kconfig @@ -23,7 +23,7 @@ source "drivers/staging/most/net/Kconfig" source "drivers/staging/most/sound/Kconfig" -source "drivers/staging/most/aim-v4l2/Kconfig" +source "drivers/staging/most/video/Kconfig" source "drivers/staging/most/dim2/Kconfig" diff --git a/drivers/staging/most/aim-v4l2/Kconfig b/drivers/staging/most/aim-v4l2/Kconfig deleted file mode 100644 index d70eaaf0936c..000000000000 --- a/drivers/staging/most/aim-v4l2/Kconfig +++ /dev/null @@ -1,12 +0,0 @@ -# -# MOST V4L2 configuration -# - -config AIM_V4L2 - tristate "V4L2 AIM" - depends on VIDEO_V4L2 - ---help--- - Say Y here if you want to commumicate via Video 4 Linux. - - To compile this driver as a module, choose M here: the - module will be called aim_v4l2. \ No newline at end of file diff --git a/drivers/staging/most/aim-v4l2/video.c b/drivers/staging/most/aim-v4l2/video.c deleted file mode 100644 index 3c813ed7f3ec..000000000000 --- a/drivers/staging/most/aim-v4l2/video.c +++ /dev/null @@ -1,610 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * V4L2 AIM - V4L2 Application Interface Module for MostCore - * - * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "most/core.h" - -#define V4L2_AIM_MAX_INPUT 1 - -static struct most_aim aim_info; - -struct most_video_dev { - struct most_interface *iface; - int ch_idx; - struct list_head list; - bool mute; - - struct list_head pending_mbos; - spinlock_t list_lock; - - struct v4l2_device v4l2_dev; - atomic_t access_ref; - struct video_device *vdev; - unsigned int ctrl_input; - - struct mutex lock; - - wait_queue_head_t wait_data; -}; - -struct aim_fh { - /* must be the first field of this struct! */ - struct v4l2_fh fh; - struct most_video_dev *mdev; - u32 offs; -}; - -static struct list_head video_devices = LIST_HEAD_INIT(video_devices); -static struct spinlock list_lock; - -static inline bool data_ready(struct most_video_dev *mdev) -{ - return !list_empty(&mdev->pending_mbos); -} - -static inline struct mbo *get_top_mbo(struct most_video_dev *mdev) -{ - return list_first_entry(&mdev->pending_mbos, struct mbo, list); -} - -static int aim_vdev_open(struct file *filp) -{ - int ret; - struct video_device *vdev = video_devdata(filp); - struct most_video_dev *mdev = video_drvdata(filp); - struct aim_fh *fh; - - v4l2_info(&mdev->v4l2_dev, "aim_vdev_open()\n"); - - switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: - break; - default: - return -EINVAL; - } - - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (!fh) - return -ENOMEM; - - if (!atomic_inc_and_test(&mdev->access_ref)) { - v4l2_err(&mdev->v4l2_dev, "too many clients\n"); - ret = -EBUSY; - goto err_dec; - } - - fh->mdev = mdev; - v4l2_fh_init(&fh->fh, vdev); - filp->private_data = fh; - - v4l2_fh_add(&fh->fh); - - ret = most_start_channel(mdev->iface, mdev->ch_idx, &aim_info); - if (ret) { - v4l2_err(&mdev->v4l2_dev, "most_start_channel() failed\n"); - goto err_rm; - } - - return 0; - -err_rm: - v4l2_fh_del(&fh->fh); - v4l2_fh_exit(&fh->fh); - -err_dec: - atomic_dec(&mdev->access_ref); - kfree(fh); - return ret; -} - -static int aim_vdev_close(struct file *filp) -{ - struct aim_fh *fh = filp->private_data; - struct most_video_dev *mdev = fh->mdev; - struct mbo *mbo, *tmp; - - v4l2_info(&mdev->v4l2_dev, "aim_vdev_close()\n"); - - /* - * We need to put MBOs back before we call most_stop_channel() - * to deallocate MBOs. - * From the other hand mostcore still calling rx_completion() - * to deliver MBOs until most_stop_channel() is called. - * Use mute to work around this issue. - * This must be implemented in core. - */ - - spin_lock_irq(&mdev->list_lock); - mdev->mute = true; - list_for_each_entry_safe(mbo, tmp, &mdev->pending_mbos, list) { - list_del(&mbo->list); - spin_unlock_irq(&mdev->list_lock); - most_put_mbo(mbo); - spin_lock_irq(&mdev->list_lock); - } - spin_unlock_irq(&mdev->list_lock); - most_stop_channel(mdev->iface, mdev->ch_idx, &aim_info); - mdev->mute = false; - - v4l2_fh_del(&fh->fh); - v4l2_fh_exit(&fh->fh); - - atomic_dec(&mdev->access_ref); - kfree(fh); - return 0; -} - -static ssize_t aim_vdev_read(struct file *filp, char __user *buf, - size_t count, loff_t *pos) -{ - struct aim_fh *fh = filp->private_data; - struct most_video_dev *mdev = fh->mdev; - int ret = 0; - - if (*pos) - return -ESPIPE; - - if (!mdev) - return -ENODEV; - - /* wait for the first buffer */ - if (!(filp->f_flags & O_NONBLOCK)) { - if (wait_event_interruptible(mdev->wait_data, data_ready(mdev))) - return -ERESTARTSYS; - } - - if (!data_ready(mdev)) - return -EAGAIN; - - while (count > 0 && data_ready(mdev)) { - struct mbo *const mbo = get_top_mbo(mdev); - int const rem = mbo->processed_length - fh->offs; - int const cnt = rem < count ? rem : count; - - if (copy_to_user(buf, mbo->virt_address + fh->offs, cnt)) { - v4l2_err(&mdev->v4l2_dev, "read: copy_to_user failed\n"); - if (!ret) - ret = -EFAULT; - return ret; - } - - fh->offs += cnt; - count -= cnt; - buf += cnt; - ret += cnt; - - if (cnt >= rem) { - fh->offs = 0; - spin_lock_irq(&mdev->list_lock); - list_del(&mbo->list); - spin_unlock_irq(&mdev->list_lock); - most_put_mbo(mbo); - } - } - return ret; -} - -static unsigned int aim_vdev_poll(struct file *filp, poll_table *wait) -{ - struct aim_fh *fh = filp->private_data; - struct most_video_dev *mdev = fh->mdev; - unsigned int mask = 0; - - /* only wait if no data is available */ - if (!data_ready(mdev)) - poll_wait(filp, &mdev->wait_data, wait); - if (data_ready(mdev)) - mask |= POLLIN | POLLRDNORM; - - return mask; -} - -static void aim_set_format_struct(struct v4l2_format *f) -{ - f->fmt.pix.width = 8; - f->fmt.pix.height = 8; - f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = 188 * 2; - f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.priv = 0; -} - -static int aim_set_format(struct most_video_dev *mdev, unsigned int cmd, - struct v4l2_format *format) -{ - if (format->fmt.pix.pixelformat != V4L2_PIX_FMT_MPEG) - return -EINVAL; - - if (cmd == VIDIOC_TRY_FMT) - return 0; - - aim_set_format_struct(format); - - return 0; -} - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct aim_fh *fh = priv; - struct most_video_dev *mdev = fh->mdev; - - v4l2_info(&mdev->v4l2_dev, "vidioc_querycap()\n"); - - strlcpy(cap->driver, "v4l2_most_aim", sizeof(cap->driver)); - strlcpy(cap->card, "MOST", sizeof(cap->card)); - snprintf(cap->bus_info, sizeof(cap->bus_info), - "%s", mdev->iface->description); - - cap->capabilities = - V4L2_CAP_READWRITE | - V4L2_CAP_TUNER | - V4L2_CAP_VIDEO_CAPTURE; - return 0; -} - -static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct aim_fh *fh = priv; - struct most_video_dev *mdev = fh->mdev; - - v4l2_info(&mdev->v4l2_dev, "vidioc_enum_fmt_vid_cap() %d\n", f->index); - - if (f->index) - return -EINVAL; - - strcpy(f->description, "MPEG"); - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - f->flags = V4L2_FMT_FLAG_COMPRESSED; - f->pixelformat = V4L2_PIX_FMT_MPEG; - - return 0; -} - -static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct aim_fh *fh = priv; - struct most_video_dev *mdev = fh->mdev; - - v4l2_info(&mdev->v4l2_dev, "vidioc_g_fmt_vid_cap()\n"); - - aim_set_format_struct(f); - return 0; -} - -static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct aim_fh *fh = priv; - struct most_video_dev *mdev = fh->mdev; - - return aim_set_format(mdev, VIDIOC_TRY_FMT, f); -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct aim_fh *fh = priv; - struct most_video_dev *mdev = fh->mdev; - - return aim_set_format(mdev, VIDIOC_S_FMT, f); -} - -static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) -{ - struct aim_fh *fh = priv; - struct most_video_dev *mdev = fh->mdev; - - v4l2_info(&mdev->v4l2_dev, "vidioc_g_std()\n"); - - *norm = V4L2_STD_UNKNOWN; - return 0; -} - -static int vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *input) -{ - struct aim_fh *fh = priv; - struct most_video_dev *mdev = fh->mdev; - - if (input->index >= V4L2_AIM_MAX_INPUT) - return -EINVAL; - - strcpy(input->name, "MOST Video"); - input->type |= V4L2_INPUT_TYPE_CAMERA; - input->audioset = 0; - - input->std = mdev->vdev->tvnorms; - - return 0; -} - -static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) -{ - struct aim_fh *fh = priv; - struct most_video_dev *mdev = fh->mdev; - *i = mdev->ctrl_input; - return 0; -} - -static int vidioc_s_input(struct file *file, void *priv, unsigned int index) -{ - struct aim_fh *fh = priv; - struct most_video_dev *mdev = fh->mdev; - - v4l2_info(&mdev->v4l2_dev, "vidioc_s_input(%d)\n", index); - - if (index >= V4L2_AIM_MAX_INPUT) - return -EINVAL; - mdev->ctrl_input = index; - return 0; -} - -static const struct v4l2_file_operations aim_fops = { - .owner = THIS_MODULE, - .open = aim_vdev_open, - .release = aim_vdev_close, - .read = aim_vdev_read, - .poll = aim_vdev_poll, - .unlocked_ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_g_std = vidioc_g_std, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, -}; - -static const struct video_device aim_videodev_template = { - .fops = &aim_fops, - .release = video_device_release, - .ioctl_ops = &video_ioctl_ops, - .tvnorms = V4L2_STD_UNKNOWN, -}; - -/**************************************************************************/ - -static struct most_video_dev *get_aim_dev( - struct most_interface *iface, int channel_idx) -{ - struct most_video_dev *mdev; - unsigned long flags; - - spin_lock_irqsave(&list_lock, flags); - list_for_each_entry(mdev, &video_devices, list) { - if (mdev->iface == iface && mdev->ch_idx == channel_idx) { - spin_unlock_irqrestore(&list_lock, flags); - return mdev; - } - } - spin_unlock_irqrestore(&list_lock, flags); - return NULL; -} - -static int aim_rx_data(struct mbo *mbo) -{ - unsigned long flags; - struct most_video_dev *mdev = - get_aim_dev(mbo->ifp, mbo->hdm_channel_id); - - if (!mdev) - return -EIO; - - spin_lock_irqsave(&mdev->list_lock, flags); - if (unlikely(mdev->mute)) { - spin_unlock_irqrestore(&mdev->list_lock, flags); - return -EIO; - } - - list_add_tail(&mbo->list, &mdev->pending_mbos); - spin_unlock_irqrestore(&mdev->list_lock, flags); - wake_up_interruptible(&mdev->wait_data); - return 0; -} - -static int aim_register_videodev(struct most_video_dev *mdev) -{ - int ret; - - v4l2_info(&mdev->v4l2_dev, "aim_register_videodev()\n"); - - init_waitqueue_head(&mdev->wait_data); - - /* allocate and fill v4l2 video struct */ - mdev->vdev = video_device_alloc(); - if (!mdev->vdev) - return -ENOMEM; - - /* Fill the video capture device struct */ - *mdev->vdev = aim_videodev_template; - mdev->vdev->v4l2_dev = &mdev->v4l2_dev; - mdev->vdev->lock = &mdev->lock; - snprintf(mdev->vdev->name, sizeof(mdev->vdev->name), "MOST: %s", - mdev->v4l2_dev.name); - - /* Register the v4l2 device */ - video_set_drvdata(mdev->vdev, mdev); - ret = video_register_device(mdev->vdev, VFL_TYPE_GRABBER, -1); - if (ret) { - v4l2_err(&mdev->v4l2_dev, "video_register_device failed (%d)\n", - ret); - video_device_release(mdev->vdev); - } - - return ret; -} - -static void aim_unregister_videodev(struct most_video_dev *mdev) -{ - v4l2_info(&mdev->v4l2_dev, "aim_unregister_videodev()\n"); - - video_unregister_device(mdev->vdev); -} - -static void aim_v4l2_dev_release(struct v4l2_device *v4l2_dev) -{ - struct most_video_dev *mdev = - container_of(v4l2_dev, struct most_video_dev, v4l2_dev); - - v4l2_device_unregister(v4l2_dev); - kfree(mdev); -} - -static int aim_probe_channel(struct most_interface *iface, int channel_idx, - struct most_channel_config *ccfg, - struct kobject *parent, char *name) -{ - int ret; - struct most_video_dev *mdev = get_aim_dev(iface, channel_idx); - - pr_info("aim_probe_channel(%s)\n", name); - - if (mdev) { - pr_err("channel already linked\n"); - return -EEXIST; - } - - if (ccfg->direction != MOST_CH_RX) { - pr_err("wrong direction, expect rx\n"); - return -EINVAL; - } - - if (ccfg->data_type != MOST_CH_SYNC && - ccfg->data_type != MOST_CH_ISOC) { - pr_err("wrong channel type, expect sync or isoc\n"); - return -EINVAL; - } - - mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); - if (!mdev) - return -ENOMEM; - - mutex_init(&mdev->lock); - atomic_set(&mdev->access_ref, -1); - spin_lock_init(&mdev->list_lock); - INIT_LIST_HEAD(&mdev->pending_mbos); - mdev->iface = iface; - mdev->ch_idx = channel_idx; - mdev->v4l2_dev.release = aim_v4l2_dev_release; - - /* Create the v4l2_device */ - strlcpy(mdev->v4l2_dev.name, name, sizeof(mdev->v4l2_dev.name)); - ret = v4l2_device_register(NULL, &mdev->v4l2_dev); - if (ret) { - pr_err("v4l2_device_register() failed\n"); - kfree(mdev); - return ret; - } - - ret = aim_register_videodev(mdev); - if (ret) - goto err_unreg; - - spin_lock_irq(&list_lock); - list_add(&mdev->list, &video_devices); - spin_unlock_irq(&list_lock); - v4l2_info(&mdev->v4l2_dev, "aim_probe_channel() done\n"); - return 0; - -err_unreg: - v4l2_device_disconnect(&mdev->v4l2_dev); - v4l2_device_put(&mdev->v4l2_dev); - return ret; -} - -static int aim_disconnect_channel(struct most_interface *iface, - int channel_idx) -{ - struct most_video_dev *mdev = get_aim_dev(iface, channel_idx); - - if (!mdev) { - pr_err("no such channel is linked\n"); - return -ENOENT; - } - - v4l2_info(&mdev->v4l2_dev, "aim_disconnect_channel()\n"); - - spin_lock_irq(&list_lock); - list_del(&mdev->list); - spin_unlock_irq(&list_lock); - - aim_unregister_videodev(mdev); - v4l2_device_disconnect(&mdev->v4l2_dev); - v4l2_device_put(&mdev->v4l2_dev); - return 0; -} - -static struct most_aim aim_info = { - .name = "v4l", - .probe_channel = aim_probe_channel, - .disconnect_channel = aim_disconnect_channel, - .rx_completion = aim_rx_data, -}; - -static int __init aim_init(void) -{ - spin_lock_init(&list_lock); - return most_register_aim(&aim_info); -} - -static void __exit aim_exit(void) -{ - struct most_video_dev *mdev, *tmp; - - /* - * As the mostcore currently doesn't call disconnect_channel() - * for linked channels while we call most_deregister_aim() - * we simulate this call here. - * This must be fixed in core. - */ - spin_lock_irq(&list_lock); - list_for_each_entry_safe(mdev, tmp, &video_devices, list) { - list_del(&mdev->list); - spin_unlock_irq(&list_lock); - - aim_unregister_videodev(mdev); - v4l2_device_disconnect(&mdev->v4l2_dev); - v4l2_device_put(&mdev->v4l2_dev); - spin_lock_irq(&list_lock); - } - spin_unlock_irq(&list_lock); - - most_deregister_aim(&aim_info); - BUG_ON(!list_empty(&video_devices)); -} - -module_init(aim_init); -module_exit(aim_exit); - -MODULE_DESCRIPTION("V4L2 Application Interface Module for MostCore"); -MODULE_AUTHOR("Andrey Shvetsov "); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/most/video/Kconfig b/drivers/staging/most/video/Kconfig new file mode 100644 index 000000000000..ce6af4f951a6 --- /dev/null +++ b/drivers/staging/most/video/Kconfig @@ -0,0 +1,12 @@ +# +# MOST V4L2 configuration +# + +config MOST_VIDEO + tristate "Video" + depends on VIDEO_V4L2 + ---help--- + Say Y here if you want to commumicate via Video 4 Linux. + + To compile this driver as a module, choose M here: the + module will be called most_video. diff --git a/drivers/staging/most/video/Makefile b/drivers/staging/most/video/Makefile new file mode 100644 index 000000000000..1c8e520e02a2 --- /dev/null +++ b/drivers/staging/most/video/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_MOST_VIDEO) += most_video.o + +most_video-objs := video.o +ccflags-y += -Idrivers/staging/ diff --git a/drivers/staging/most/video/video.c b/drivers/staging/most/video/video.c new file mode 100644 index 000000000000..3c813ed7f3ec --- /dev/null +++ b/drivers/staging/most/video/video.c @@ -0,0 +1,610 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * V4L2 AIM - V4L2 Application Interface Module for MostCore + * + * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "most/core.h" + +#define V4L2_AIM_MAX_INPUT 1 + +static struct most_aim aim_info; + +struct most_video_dev { + struct most_interface *iface; + int ch_idx; + struct list_head list; + bool mute; + + struct list_head pending_mbos; + spinlock_t list_lock; + + struct v4l2_device v4l2_dev; + atomic_t access_ref; + struct video_device *vdev; + unsigned int ctrl_input; + + struct mutex lock; + + wait_queue_head_t wait_data; +}; + +struct aim_fh { + /* must be the first field of this struct! */ + struct v4l2_fh fh; + struct most_video_dev *mdev; + u32 offs; +}; + +static struct list_head video_devices = LIST_HEAD_INIT(video_devices); +static struct spinlock list_lock; + +static inline bool data_ready(struct most_video_dev *mdev) +{ + return !list_empty(&mdev->pending_mbos); +} + +static inline struct mbo *get_top_mbo(struct most_video_dev *mdev) +{ + return list_first_entry(&mdev->pending_mbos, struct mbo, list); +} + +static int aim_vdev_open(struct file *filp) +{ + int ret; + struct video_device *vdev = video_devdata(filp); + struct most_video_dev *mdev = video_drvdata(filp); + struct aim_fh *fh; + + v4l2_info(&mdev->v4l2_dev, "aim_vdev_open()\n"); + + switch (vdev->vfl_type) { + case VFL_TYPE_GRABBER: + break; + default: + return -EINVAL; + } + + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (!fh) + return -ENOMEM; + + if (!atomic_inc_and_test(&mdev->access_ref)) { + v4l2_err(&mdev->v4l2_dev, "too many clients\n"); + ret = -EBUSY; + goto err_dec; + } + + fh->mdev = mdev; + v4l2_fh_init(&fh->fh, vdev); + filp->private_data = fh; + + v4l2_fh_add(&fh->fh); + + ret = most_start_channel(mdev->iface, mdev->ch_idx, &aim_info); + if (ret) { + v4l2_err(&mdev->v4l2_dev, "most_start_channel() failed\n"); + goto err_rm; + } + + return 0; + +err_rm: + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); + +err_dec: + atomic_dec(&mdev->access_ref); + kfree(fh); + return ret; +} + +static int aim_vdev_close(struct file *filp) +{ + struct aim_fh *fh = filp->private_data; + struct most_video_dev *mdev = fh->mdev; + struct mbo *mbo, *tmp; + + v4l2_info(&mdev->v4l2_dev, "aim_vdev_close()\n"); + + /* + * We need to put MBOs back before we call most_stop_channel() + * to deallocate MBOs. + * From the other hand mostcore still calling rx_completion() + * to deliver MBOs until most_stop_channel() is called. + * Use mute to work around this issue. + * This must be implemented in core. + */ + + spin_lock_irq(&mdev->list_lock); + mdev->mute = true; + list_for_each_entry_safe(mbo, tmp, &mdev->pending_mbos, list) { + list_del(&mbo->list); + spin_unlock_irq(&mdev->list_lock); + most_put_mbo(mbo); + spin_lock_irq(&mdev->list_lock); + } + spin_unlock_irq(&mdev->list_lock); + most_stop_channel(mdev->iface, mdev->ch_idx, &aim_info); + mdev->mute = false; + + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); + + atomic_dec(&mdev->access_ref); + kfree(fh); + return 0; +} + +static ssize_t aim_vdev_read(struct file *filp, char __user *buf, + size_t count, loff_t *pos) +{ + struct aim_fh *fh = filp->private_data; + struct most_video_dev *mdev = fh->mdev; + int ret = 0; + + if (*pos) + return -ESPIPE; + + if (!mdev) + return -ENODEV; + + /* wait for the first buffer */ + if (!(filp->f_flags & O_NONBLOCK)) { + if (wait_event_interruptible(mdev->wait_data, data_ready(mdev))) + return -ERESTARTSYS; + } + + if (!data_ready(mdev)) + return -EAGAIN; + + while (count > 0 && data_ready(mdev)) { + struct mbo *const mbo = get_top_mbo(mdev); + int const rem = mbo->processed_length - fh->offs; + int const cnt = rem < count ? rem : count; + + if (copy_to_user(buf, mbo->virt_address + fh->offs, cnt)) { + v4l2_err(&mdev->v4l2_dev, "read: copy_to_user failed\n"); + if (!ret) + ret = -EFAULT; + return ret; + } + + fh->offs += cnt; + count -= cnt; + buf += cnt; + ret += cnt; + + if (cnt >= rem) { + fh->offs = 0; + spin_lock_irq(&mdev->list_lock); + list_del(&mbo->list); + spin_unlock_irq(&mdev->list_lock); + most_put_mbo(mbo); + } + } + return ret; +} + +static unsigned int aim_vdev_poll(struct file *filp, poll_table *wait) +{ + struct aim_fh *fh = filp->private_data; + struct most_video_dev *mdev = fh->mdev; + unsigned int mask = 0; + + /* only wait if no data is available */ + if (!data_ready(mdev)) + poll_wait(filp, &mdev->wait_data, wait); + if (data_ready(mdev)) + mask |= POLLIN | POLLRDNORM; + + return mask; +} + +static void aim_set_format_struct(struct v4l2_format *f) +{ + f->fmt.pix.width = 8; + f->fmt.pix.height = 8; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = 188 * 2; + f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.priv = 0; +} + +static int aim_set_format(struct most_video_dev *mdev, unsigned int cmd, + struct v4l2_format *format) +{ + if (format->fmt.pix.pixelformat != V4L2_PIX_FMT_MPEG) + return -EINVAL; + + if (cmd == VIDIOC_TRY_FMT) + return 0; + + aim_set_format_struct(format); + + return 0; +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct aim_fh *fh = priv; + struct most_video_dev *mdev = fh->mdev; + + v4l2_info(&mdev->v4l2_dev, "vidioc_querycap()\n"); + + strlcpy(cap->driver, "v4l2_most_aim", sizeof(cap->driver)); + strlcpy(cap->card, "MOST", sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "%s", mdev->iface->description); + + cap->capabilities = + V4L2_CAP_READWRITE | + V4L2_CAP_TUNER | + V4L2_CAP_VIDEO_CAPTURE; + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct aim_fh *fh = priv; + struct most_video_dev *mdev = fh->mdev; + + v4l2_info(&mdev->v4l2_dev, "vidioc_enum_fmt_vid_cap() %d\n", f->index); + + if (f->index) + return -EINVAL; + + strcpy(f->description, "MPEG"); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->flags = V4L2_FMT_FLAG_COMPRESSED; + f->pixelformat = V4L2_PIX_FMT_MPEG; + + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct aim_fh *fh = priv; + struct most_video_dev *mdev = fh->mdev; + + v4l2_info(&mdev->v4l2_dev, "vidioc_g_fmt_vid_cap()\n"); + + aim_set_format_struct(f); + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct aim_fh *fh = priv; + struct most_video_dev *mdev = fh->mdev; + + return aim_set_format(mdev, VIDIOC_TRY_FMT, f); +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct aim_fh *fh = priv; + struct most_video_dev *mdev = fh->mdev; + + return aim_set_format(mdev, VIDIOC_S_FMT, f); +} + +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) +{ + struct aim_fh *fh = priv; + struct most_video_dev *mdev = fh->mdev; + + v4l2_info(&mdev->v4l2_dev, "vidioc_g_std()\n"); + + *norm = V4L2_STD_UNKNOWN; + return 0; +} + +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct aim_fh *fh = priv; + struct most_video_dev *mdev = fh->mdev; + + if (input->index >= V4L2_AIM_MAX_INPUT) + return -EINVAL; + + strcpy(input->name, "MOST Video"); + input->type |= V4L2_INPUT_TYPE_CAMERA; + input->audioset = 0; + + input->std = mdev->vdev->tvnorms; + + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct aim_fh *fh = priv; + struct most_video_dev *mdev = fh->mdev; + *i = mdev->ctrl_input; + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int index) +{ + struct aim_fh *fh = priv; + struct most_video_dev *mdev = fh->mdev; + + v4l2_info(&mdev->v4l2_dev, "vidioc_s_input(%d)\n", index); + + if (index >= V4L2_AIM_MAX_INPUT) + return -EINVAL; + mdev->ctrl_input = index; + return 0; +} + +static const struct v4l2_file_operations aim_fops = { + .owner = THIS_MODULE, + .open = aim_vdev_open, + .release = aim_vdev_close, + .read = aim_vdev_read, + .poll = aim_vdev_poll, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_g_std = vidioc_g_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, +}; + +static const struct video_device aim_videodev_template = { + .fops = &aim_fops, + .release = video_device_release, + .ioctl_ops = &video_ioctl_ops, + .tvnorms = V4L2_STD_UNKNOWN, +}; + +/**************************************************************************/ + +static struct most_video_dev *get_aim_dev( + struct most_interface *iface, int channel_idx) +{ + struct most_video_dev *mdev; + unsigned long flags; + + spin_lock_irqsave(&list_lock, flags); + list_for_each_entry(mdev, &video_devices, list) { + if (mdev->iface == iface && mdev->ch_idx == channel_idx) { + spin_unlock_irqrestore(&list_lock, flags); + return mdev; + } + } + spin_unlock_irqrestore(&list_lock, flags); + return NULL; +} + +static int aim_rx_data(struct mbo *mbo) +{ + unsigned long flags; + struct most_video_dev *mdev = + get_aim_dev(mbo->ifp, mbo->hdm_channel_id); + + if (!mdev) + return -EIO; + + spin_lock_irqsave(&mdev->list_lock, flags); + if (unlikely(mdev->mute)) { + spin_unlock_irqrestore(&mdev->list_lock, flags); + return -EIO; + } + + list_add_tail(&mbo->list, &mdev->pending_mbos); + spin_unlock_irqrestore(&mdev->list_lock, flags); + wake_up_interruptible(&mdev->wait_data); + return 0; +} + +static int aim_register_videodev(struct most_video_dev *mdev) +{ + int ret; + + v4l2_info(&mdev->v4l2_dev, "aim_register_videodev()\n"); + + init_waitqueue_head(&mdev->wait_data); + + /* allocate and fill v4l2 video struct */ + mdev->vdev = video_device_alloc(); + if (!mdev->vdev) + return -ENOMEM; + + /* Fill the video capture device struct */ + *mdev->vdev = aim_videodev_template; + mdev->vdev->v4l2_dev = &mdev->v4l2_dev; + mdev->vdev->lock = &mdev->lock; + snprintf(mdev->vdev->name, sizeof(mdev->vdev->name), "MOST: %s", + mdev->v4l2_dev.name); + + /* Register the v4l2 device */ + video_set_drvdata(mdev->vdev, mdev); + ret = video_register_device(mdev->vdev, VFL_TYPE_GRABBER, -1); + if (ret) { + v4l2_err(&mdev->v4l2_dev, "video_register_device failed (%d)\n", + ret); + video_device_release(mdev->vdev); + } + + return ret; +} + +static void aim_unregister_videodev(struct most_video_dev *mdev) +{ + v4l2_info(&mdev->v4l2_dev, "aim_unregister_videodev()\n"); + + video_unregister_device(mdev->vdev); +} + +static void aim_v4l2_dev_release(struct v4l2_device *v4l2_dev) +{ + struct most_video_dev *mdev = + container_of(v4l2_dev, struct most_video_dev, v4l2_dev); + + v4l2_device_unregister(v4l2_dev); + kfree(mdev); +} + +static int aim_probe_channel(struct most_interface *iface, int channel_idx, + struct most_channel_config *ccfg, + struct kobject *parent, char *name) +{ + int ret; + struct most_video_dev *mdev = get_aim_dev(iface, channel_idx); + + pr_info("aim_probe_channel(%s)\n", name); + + if (mdev) { + pr_err("channel already linked\n"); + return -EEXIST; + } + + if (ccfg->direction != MOST_CH_RX) { + pr_err("wrong direction, expect rx\n"); + return -EINVAL; + } + + if (ccfg->data_type != MOST_CH_SYNC && + ccfg->data_type != MOST_CH_ISOC) { + pr_err("wrong channel type, expect sync or isoc\n"); + return -EINVAL; + } + + mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); + if (!mdev) + return -ENOMEM; + + mutex_init(&mdev->lock); + atomic_set(&mdev->access_ref, -1); + spin_lock_init(&mdev->list_lock); + INIT_LIST_HEAD(&mdev->pending_mbos); + mdev->iface = iface; + mdev->ch_idx = channel_idx; + mdev->v4l2_dev.release = aim_v4l2_dev_release; + + /* Create the v4l2_device */ + strlcpy(mdev->v4l2_dev.name, name, sizeof(mdev->v4l2_dev.name)); + ret = v4l2_device_register(NULL, &mdev->v4l2_dev); + if (ret) { + pr_err("v4l2_device_register() failed\n"); + kfree(mdev); + return ret; + } + + ret = aim_register_videodev(mdev); + if (ret) + goto err_unreg; + + spin_lock_irq(&list_lock); + list_add(&mdev->list, &video_devices); + spin_unlock_irq(&list_lock); + v4l2_info(&mdev->v4l2_dev, "aim_probe_channel() done\n"); + return 0; + +err_unreg: + v4l2_device_disconnect(&mdev->v4l2_dev); + v4l2_device_put(&mdev->v4l2_dev); + return ret; +} + +static int aim_disconnect_channel(struct most_interface *iface, + int channel_idx) +{ + struct most_video_dev *mdev = get_aim_dev(iface, channel_idx); + + if (!mdev) { + pr_err("no such channel is linked\n"); + return -ENOENT; + } + + v4l2_info(&mdev->v4l2_dev, "aim_disconnect_channel()\n"); + + spin_lock_irq(&list_lock); + list_del(&mdev->list); + spin_unlock_irq(&list_lock); + + aim_unregister_videodev(mdev); + v4l2_device_disconnect(&mdev->v4l2_dev); + v4l2_device_put(&mdev->v4l2_dev); + return 0; +} + +static struct most_aim aim_info = { + .name = "v4l", + .probe_channel = aim_probe_channel, + .disconnect_channel = aim_disconnect_channel, + .rx_completion = aim_rx_data, +}; + +static int __init aim_init(void) +{ + spin_lock_init(&list_lock); + return most_register_aim(&aim_info); +} + +static void __exit aim_exit(void) +{ + struct most_video_dev *mdev, *tmp; + + /* + * As the mostcore currently doesn't call disconnect_channel() + * for linked channels while we call most_deregister_aim() + * we simulate this call here. + * This must be fixed in core. + */ + spin_lock_irq(&list_lock); + list_for_each_entry_safe(mdev, tmp, &video_devices, list) { + list_del(&mdev->list); + spin_unlock_irq(&list_lock); + + aim_unregister_videodev(mdev); + v4l2_device_disconnect(&mdev->v4l2_dev); + v4l2_device_put(&mdev->v4l2_dev); + spin_lock_irq(&list_lock); + } + spin_unlock_irq(&list_lock); + + most_deregister_aim(&aim_info); + BUG_ON(!list_empty(&video_devices)); +} + +module_init(aim_init); +module_exit(aim_exit); + +MODULE_DESCRIPTION("V4L2 Application Interface Module for MostCore"); +MODULE_AUTHOR("Andrey Shvetsov "); +MODULE_LICENSE("GPL"); -- cgit From 4d5f022f3a664ee5987118b754058ff31df03835 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:43 +0100 Subject: staging: most: remove proprietary kobjects This patch removes the proprietary kobjects used by the driver modules and replaces them with device structs. The patch is needed to have the driver being integrated into the kernel's device model. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/cdev/cdev.c | 6 +- drivers/staging/most/core.c | 803 +++++++++++-------------------------- drivers/staging/most/core.h | 13 +- drivers/staging/most/dim2/dim2.c | 19 +- drivers/staging/most/dim2/sysfs.c | 92 +---- drivers/staging/most/dim2/sysfs.h | 6 +- drivers/staging/most/i2c/i2c.c | 7 +- drivers/staging/most/net/net.c | 3 +- drivers/staging/most/sound/sound.c | 3 +- drivers/staging/most/usb/usb.c | 243 +++-------- drivers/staging/most/video/video.c | 3 +- 11 files changed, 342 insertions(+), 856 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/cdev/cdev.c b/drivers/staging/most/cdev/cdev.c index 3ae9246c0c5c..cd23db574d5f 100644 --- a/drivers/staging/most/cdev/cdev.c +++ b/drivers/staging/most/cdev/cdev.c @@ -412,7 +412,6 @@ static int aim_tx_completion(struct most_interface *iface, int channel_id) * @iface: pointer to interface instance * @channel_id: channel index/ID * @cfg: pointer to actual channel configuration - * @parent: pointer to kobject (needed for sysfs hook-up) * @name: name of the device to be created * * This allocates achannel object and creates the device node in /dev @@ -420,15 +419,14 @@ static int aim_tx_completion(struct most_interface *iface, int channel_id) * Returns 0 on success or error code otherwise. */ static int aim_probe(struct most_interface *iface, int channel_id, - struct most_channel_config *cfg, - struct kobject *parent, char *name) + struct most_channel_config *cfg, char *name) { struct aim_channel *c; unsigned long cl_flags; int retval; int current_minor; - if ((!iface) || (!cfg) || (!parent) || (!name)) { + if ((!iface) || (!cfg) || (!name)) { pr_info("Probing AIM with bad arguments"); return -EINVAL; } diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index 8fe3f2ecd4e4..d4456abf18ed 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -27,7 +27,7 @@ #define STRING_SIZE 80 static struct class *most_class; -static struct device *core_dev; +static struct device core_dev; static struct ida mdev_id; static int dummy_num_buffers; @@ -38,7 +38,7 @@ struct most_c_aim_obj { }; struct most_c_obj { - struct kobject kobj; + struct device dev; struct completion cleanup; atomic_t mbo_ref; atomic_t mbo_nq_level; @@ -63,14 +63,13 @@ struct most_c_obj { wait_queue_head_t hdm_fifo_wq; }; -#define to_c_obj(d) container_of(d, struct most_c_obj, kobj) +#define to_c_obj(d) container_of(d, struct most_c_obj, dev) struct most_inst_obj { int dev_id; struct most_interface *iface; struct list_head channel_list; struct most_c_obj *channel[MAX_CHANNELS]; - struct kobject kobj; struct list_head list; }; @@ -85,8 +84,6 @@ static const struct { { MOST_CH_ISOC, "isoc_avp\n"}, }; -#define to_inst_obj(d) container_of(d, struct most_inst_obj, kobj) - /** * list_pop_mbo - retrieves the first MBO of the list and removes it * @ptr: the list head to grab the MBO from. @@ -102,68 +99,6 @@ static const struct { * ___C H A N N E L___ */ -/** - * struct most_c_attr - to access the attributes of a channel object - * @attr: attributes of a channel - * @show: pointer to the show function - * @store: pointer to the store function - */ -struct most_c_attr { - struct attribute attr; - ssize_t (*show)(struct most_c_obj *d, - struct most_c_attr *attr, - char *buf); - ssize_t (*store)(struct most_c_obj *d, - struct most_c_attr *attr, - const char *buf, - size_t count); -}; - -#define to_channel_attr(a) container_of(a, struct most_c_attr, attr) - -/** - * channel_attr_show - show function of channel object - * @kobj: pointer to its kobject - * @attr: pointer to its attributes - * @buf: buffer - */ -static ssize_t channel_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - struct most_c_attr *channel_attr = to_channel_attr(attr); - struct most_c_obj *c_obj = to_c_obj(kobj); - - if (!channel_attr->show) - return -EIO; - - return channel_attr->show(c_obj, channel_attr, buf); -} - -/** - * channel_attr_store - store function of channel object - * @kobj: pointer to its kobject - * @attr: pointer to its attributes - * @buf: buffer - * @len: length of buffer - */ -static ssize_t channel_attr_store(struct kobject *kobj, - struct attribute *attr, - const char *buf, - size_t len) -{ - struct most_c_attr *channel_attr = to_channel_attr(attr); - struct most_c_obj *c_obj = to_c_obj(kobj); - - if (!channel_attr->store) - return -EIO; - return channel_attr->store(c_obj, channel_attr, buf, len); -} - -static const struct sysfs_ops most_channel_sysfs_ops = { - .show = channel_attr_show, - .store = channel_attr_store, -}; - /** * most_free_mbo_coherent - free an MBO and its coherent buffer * @mbo: buffer to be released @@ -235,21 +170,11 @@ static int flush_trash_fifo(struct most_c_obj *c) return 0; } -/** - * most_channel_release - release function of channel object - * @kobj: pointer to channel's kobject - */ -static void most_channel_release(struct kobject *kobj) -{ - struct most_c_obj *c = to_c_obj(kobj); - - kfree(c); -} - -static ssize_t available_directions_show(struct most_c_obj *c, - struct most_c_attr *attr, +static ssize_t available_directions_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct most_c_obj *c = to_c_obj(dev); unsigned int i = c->channel_id; strcpy(buf, ""); @@ -261,10 +186,11 @@ static ssize_t available_directions_show(struct most_c_obj *c, return strlen(buf); } -static ssize_t available_datatypes_show(struct most_c_obj *c, - struct most_c_attr *attr, +static ssize_t available_datatypes_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct most_c_obj *c = to_c_obj(dev); unsigned int i = c->channel_id; strcpy(buf, ""); @@ -280,65 +206,75 @@ static ssize_t available_datatypes_show(struct most_c_obj *c, return strlen(buf); } -static ssize_t number_of_packet_buffers_show(struct most_c_obj *c, - struct most_c_attr *attr, +static ssize_t number_of_packet_buffers_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct most_c_obj *c = to_c_obj(dev); unsigned int i = c->channel_id; return snprintf(buf, PAGE_SIZE, "%d\n", c->iface->channel_vector[i].num_buffers_packet); } -static ssize_t number_of_stream_buffers_show(struct most_c_obj *c, - struct most_c_attr *attr, +static ssize_t number_of_stream_buffers_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct most_c_obj *c = to_c_obj(dev); unsigned int i = c->channel_id; return snprintf(buf, PAGE_SIZE, "%d\n", c->iface->channel_vector[i].num_buffers_streaming); } -static ssize_t size_of_packet_buffer_show(struct most_c_obj *c, - struct most_c_attr *attr, +static ssize_t size_of_packet_buffer_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct most_c_obj *c = to_c_obj(dev); unsigned int i = c->channel_id; return snprintf(buf, PAGE_SIZE, "%d\n", c->iface->channel_vector[i].buffer_size_packet); } -static ssize_t size_of_stream_buffer_show(struct most_c_obj *c, - struct most_c_attr *attr, +static ssize_t size_of_stream_buffer_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct most_c_obj *c = to_c_obj(dev); unsigned int i = c->channel_id; return snprintf(buf, PAGE_SIZE, "%d\n", c->iface->channel_vector[i].buffer_size_streaming); } -static ssize_t channel_starving_show(struct most_c_obj *c, - struct most_c_attr *attr, +static ssize_t channel_starving_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct most_c_obj *c = to_c_obj(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", c->is_starving); } -static ssize_t set_number_of_buffers_show(struct most_c_obj *c, - struct most_c_attr *attr, +static ssize_t set_number_of_buffers_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct most_c_obj *c = to_c_obj(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.num_buffers); } -static ssize_t set_number_of_buffers_store(struct most_c_obj *c, - struct most_c_attr *attr, +static ssize_t set_number_of_buffers_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { + struct most_c_obj *c = to_c_obj(dev); + int ret = kstrtou16(buf, 0, &c->cfg.num_buffers); if (ret) @@ -346,18 +282,21 @@ static ssize_t set_number_of_buffers_store(struct most_c_obj *c, return count; } -static ssize_t set_buffer_size_show(struct most_c_obj *c, - struct most_c_attr *attr, +static ssize_t set_buffer_size_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct most_c_obj *c = to_c_obj(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.buffer_size); } -static ssize_t set_buffer_size_store(struct most_c_obj *c, - struct most_c_attr *attr, +static ssize_t set_buffer_size_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { + struct most_c_obj *c = to_c_obj(dev); int ret = kstrtou16(buf, 0, &c->cfg.buffer_size); if (ret) @@ -365,10 +304,12 @@ static ssize_t set_buffer_size_store(struct most_c_obj *c, return count; } -static ssize_t set_direction_show(struct most_c_obj *c, - struct most_c_attr *attr, +static ssize_t set_direction_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct most_c_obj *c = to_c_obj(dev); + if (c->cfg.direction & MOST_CH_TX) return snprintf(buf, PAGE_SIZE, "tx\n"); else if (c->cfg.direction & MOST_CH_RX) @@ -376,11 +317,13 @@ static ssize_t set_direction_show(struct most_c_obj *c, return snprintf(buf, PAGE_SIZE, "unconfigured\n"); } -static ssize_t set_direction_store(struct most_c_obj *c, - struct most_c_attr *attr, +static ssize_t set_direction_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { + struct most_c_obj *c = to_c_obj(dev); + if (!strcmp(buf, "dir_rx\n")) { c->cfg.direction = MOST_CH_RX; } else if (!strcmp(buf, "rx\n")) { @@ -396,11 +339,12 @@ static ssize_t set_direction_store(struct most_c_obj *c, return count; } -static ssize_t set_datatype_show(struct most_c_obj *c, - struct most_c_attr *attr, +static ssize_t set_datatype_show(struct device *dev, + struct device_attribute *attr, char *buf) { int i; + struct most_c_obj *c = to_c_obj(dev); for (i = 0; i < ARRAY_SIZE(ch_data_type); i++) { if (c->cfg.data_type & ch_data_type[i].most_ch_data_type) @@ -409,12 +353,13 @@ static ssize_t set_datatype_show(struct most_c_obj *c, return snprintf(buf, PAGE_SIZE, "unconfigured\n"); } -static ssize_t set_datatype_store(struct most_c_obj *c, - struct most_c_attr *attr, +static ssize_t set_datatype_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { int i; + struct most_c_obj *c = to_c_obj(dev); for (i = 0; i < ARRAY_SIZE(ch_data_type); i++) { if (!strcmp(buf, ch_data_type[i].name)) { @@ -430,18 +375,21 @@ static ssize_t set_datatype_store(struct most_c_obj *c, return count; } -static ssize_t set_subbuffer_size_show(struct most_c_obj *c, - struct most_c_attr *attr, +static ssize_t set_subbuffer_size_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct most_c_obj *c = to_c_obj(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.subbuffer_size); } -static ssize_t set_subbuffer_size_store(struct most_c_obj *c, - struct most_c_attr *attr, +static ssize_t set_subbuffer_size_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { + struct most_c_obj *c = to_c_obj(dev); int ret = kstrtou16(buf, 0, &c->cfg.subbuffer_size); if (ret) @@ -449,18 +397,21 @@ static ssize_t set_subbuffer_size_store(struct most_c_obj *c, return count; } -static ssize_t set_packets_per_xact_show(struct most_c_obj *c, - struct most_c_attr *attr, +static ssize_t set_packets_per_xact_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct most_c_obj *c = to_c_obj(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.packets_per_xact); } -static ssize_t set_packets_per_xact_store(struct most_c_obj *c, - struct most_c_attr *attr, +static ssize_t set_packets_per_xact_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { + struct most_c_obj *c = to_c_obj(dev); int ret = kstrtou16(buf, 0, &c->cfg.packets_per_xact); if (ret) @@ -468,77 +419,47 @@ static ssize_t set_packets_per_xact_store(struct most_c_obj *c, return count; } -static struct most_c_attr most_c_attrs[] = { - __ATTR_RO(available_directions), - __ATTR_RO(available_datatypes), - __ATTR_RO(number_of_packet_buffers), - __ATTR_RO(number_of_stream_buffers), - __ATTR_RO(size_of_stream_buffer), - __ATTR_RO(size_of_packet_buffer), - __ATTR_RO(channel_starving), - __ATTR_RW(set_buffer_size), - __ATTR_RW(set_number_of_buffers), - __ATTR_RW(set_direction), - __ATTR_RW(set_datatype), - __ATTR_RW(set_subbuffer_size), - __ATTR_RW(set_packets_per_xact), -}; - -/** - * most_channel_def_attrs - array of default attributes of channel object - */ -static struct attribute *most_channel_def_attrs[] = { - &most_c_attrs[0].attr, - &most_c_attrs[1].attr, - &most_c_attrs[2].attr, - &most_c_attrs[3].attr, - &most_c_attrs[4].attr, - &most_c_attrs[5].attr, - &most_c_attrs[6].attr, - &most_c_attrs[7].attr, - &most_c_attrs[8].attr, - &most_c_attrs[9].attr, - &most_c_attrs[10].attr, - &most_c_attrs[11].attr, - &most_c_attrs[12].attr, +#define DEV_ATTR(_name) (&dev_attr_##_name.attr) + +static DEVICE_ATTR_RO(available_directions); +static DEVICE_ATTR_RO(available_datatypes); +static DEVICE_ATTR_RO(number_of_packet_buffers); +static DEVICE_ATTR_RO(number_of_stream_buffers); +static DEVICE_ATTR_RO(size_of_stream_buffer); +static DEVICE_ATTR_RO(size_of_packet_buffer); +static DEVICE_ATTR_RO(channel_starving); +static DEVICE_ATTR_RW(set_buffer_size); +static DEVICE_ATTR_RW(set_number_of_buffers); +static DEVICE_ATTR_RW(set_direction); +static DEVICE_ATTR_RW(set_datatype); +static DEVICE_ATTR_RW(set_subbuffer_size); +static DEVICE_ATTR_RW(set_packets_per_xact); + +static struct attribute *channel_attrs[] = { + DEV_ATTR(available_directions), + DEV_ATTR(available_datatypes), + DEV_ATTR(number_of_packet_buffers), + DEV_ATTR(number_of_stream_buffers), + DEV_ATTR(size_of_stream_buffer), + DEV_ATTR(size_of_packet_buffer), + DEV_ATTR(channel_starving), + DEV_ATTR(set_buffer_size), + DEV_ATTR(set_number_of_buffers), + DEV_ATTR(set_direction), + DEV_ATTR(set_datatype), + DEV_ATTR(set_subbuffer_size), + DEV_ATTR(set_packets_per_xact), NULL, }; -static struct kobj_type most_channel_ktype = { - .sysfs_ops = &most_channel_sysfs_ops, - .release = most_channel_release, - .default_attrs = most_channel_def_attrs, +static struct attribute_group channel_attr_group = { + .attrs = channel_attrs, }; -static struct kset *most_channel_kset; - -/** - * create_most_c_obj - allocates a channel object - * @name: name of the channel object - * @parent: parent kobject - * - * This create a channel object and registers it with sysfs. - * Returns a pointer to the object or NULL when something went wrong. - */ -static struct most_c_obj * -create_most_c_obj(const char *name, struct kobject *parent) -{ - struct most_c_obj *c; - int retval; - - c = kzalloc(sizeof(*c), GFP_KERNEL); - if (!c) - return NULL; - c->kobj.kset = most_channel_kset; - retval = kobject_init_and_add(&c->kobj, &most_channel_ktype, parent, - "%s", name); - if (retval) { - kobject_put(&c->kobj); - return NULL; - } - kobject_uevent(&c->kobj, KOBJ_ADD); - return c; -} +static const struct attribute_group *channel_attr_groups[] = { + &channel_attr_group, + NULL, +}; /* ___ ___ * ___I N S T A N C E___ @@ -546,103 +467,22 @@ create_most_c_obj(const char *name, struct kobject *parent) static struct list_head instance_list; -/** - * struct most_inst_attribute - to access the attributes of instance object - * @attr: attributes of an instance - * @show: pointer to the show function - * @store: pointer to the store function - */ -struct most_inst_attribute { - struct attribute attr; - ssize_t (*show)(struct most_inst_obj *d, - struct most_inst_attribute *attr, - char *buf); - ssize_t (*store)(struct most_inst_obj *d, - struct most_inst_attribute *attr, - const char *buf, - size_t count); -}; - -#define to_instance_attr(a) \ - container_of(a, struct most_inst_attribute, attr) - -/** - * instance_attr_show - show function for an instance object - * @kobj: pointer to kobject - * @attr: pointer to attribute struct - * @buf: buffer - */ -static ssize_t instance_attr_show(struct kobject *kobj, - struct attribute *attr, - char *buf) -{ - struct most_inst_attribute *instance_attr; - struct most_inst_obj *instance_obj; - - instance_attr = to_instance_attr(attr); - instance_obj = to_inst_obj(kobj); - - if (!instance_attr->show) - return -EIO; - - return instance_attr->show(instance_obj, instance_attr, buf); -} - -/** - * instance_attr_store - store function for an instance object - * @kobj: pointer to kobject - * @attr: pointer to attribute struct - * @buf: buffer - * @len: length of buffer - */ -static ssize_t instance_attr_store(struct kobject *kobj, - struct attribute *attr, - const char *buf, - size_t len) -{ - struct most_inst_attribute *instance_attr; - struct most_inst_obj *instance_obj; - - instance_attr = to_instance_attr(attr); - instance_obj = to_inst_obj(kobj); - - if (!instance_attr->store) - return -EIO; - - return instance_attr->store(instance_obj, instance_attr, buf, len); -} - -static const struct sysfs_ops most_inst_sysfs_ops = { - .show = instance_attr_show, - .store = instance_attr_store, -}; - -/** - * most_inst_release - release function for instance object - * @kobj: pointer to instance's kobject - * - * This frees the allocated memory for the instance object - */ -static void most_inst_release(struct kobject *kobj) -{ - struct most_inst_obj *inst = to_inst_obj(kobj); - - kfree(inst); -} - -static ssize_t description_show(struct most_inst_obj *instance_obj, - struct most_inst_attribute *attr, +static ssize_t description_show(struct device *dev, + struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "%s\n", - instance_obj->iface->description); + struct most_interface *iface = to_most_interface(dev); + + return snprintf(buf, PAGE_SIZE, "%s\n", iface->description); } -static ssize_t interface_show(struct most_inst_obj *instance_obj, - struct most_inst_attribute *attr, +static ssize_t interface_show(struct device *dev, + struct device_attribute *attr, char *buf) { - switch (instance_obj->iface->interface) { + struct most_interface *iface = to_most_interface(dev); + + switch (iface->interface) { case ITYPE_LOOPBACK: return snprintf(buf, PAGE_SIZE, "loopback\n"); case ITYPE_I2C: @@ -665,182 +505,42 @@ static ssize_t interface_show(struct most_inst_obj *instance_obj, return snprintf(buf, PAGE_SIZE, "unknown\n"); } -static struct most_inst_attribute most_inst_attr_description = - __ATTR_RO(description); - -static struct most_inst_attribute most_inst_attr_interface = - __ATTR_RO(interface); +static DEVICE_ATTR_RO(description); +static DEVICE_ATTR_RO(interface); -static struct attribute *most_inst_def_attrs[] = { - &most_inst_attr_description.attr, - &most_inst_attr_interface.attr, +static struct attribute *interface_attrs[] = { + DEV_ATTR(description), + DEV_ATTR(interface), NULL, }; -static struct kobj_type most_inst_ktype = { - .sysfs_ops = &most_inst_sysfs_ops, - .release = most_inst_release, - .default_attrs = most_inst_def_attrs, +static struct attribute_group interface_attr_group = { + .attrs = interface_attrs, }; -static struct kset *most_inst_kset; - -/** - * create_most_inst_obj - creates an instance object - * @name: name of the object to be created - * - * This allocates memory for an instance structure, assigns the proper kset - * and registers it with sysfs. - * - * Returns a pointer to the instance object or NULL when something went wrong. - */ -static struct most_inst_obj *create_most_inst_obj(const char *name) -{ - struct most_inst_obj *inst; - int retval; - - inst = kzalloc(sizeof(*inst), GFP_KERNEL); - if (!inst) - return NULL; - inst->kobj.kset = most_inst_kset; - retval = kobject_init_and_add(&inst->kobj, &most_inst_ktype, NULL, - "%s", name); - if (retval) { - kobject_put(&inst->kobj); - return NULL; - } - kobject_uevent(&inst->kobj, KOBJ_ADD); - return inst; -} - -/** - * destroy_most_inst_obj - MOST instance release function - * @inst: pointer to the instance object - * - * This decrements the reference counter of the instance object. - * If the reference count turns zero, its release function is called - */ -static void destroy_most_inst_obj(struct most_inst_obj *inst) -{ - struct most_c_obj *c, *tmp; - - list_for_each_entry_safe(c, tmp, &inst->channel_list, list) { - flush_trash_fifo(c); - flush_channel_fifos(c); - kobject_put(&c->kobj); - } - kobject_put(&inst->kobj); -} +static const struct attribute_group *interface_attr_groups[] = { + &interface_attr_group, + NULL, +}; /* ___ ___ * ___A I M___ */ -struct most_aim_obj { - struct kobject kobj; - struct list_head list; - struct most_aim *driver; -}; - -#define to_aim_obj(d) container_of(d, struct most_aim_obj, kobj) - -static struct list_head aim_list; - -/** - * struct most_aim_attribute - to access the attributes of AIM object - * @attr: attributes of an AIM - * @show: pointer to the show function - * @store: pointer to the store function - */ -struct most_aim_attribute { - struct attribute attr; - ssize_t (*show)(struct most_aim_obj *d, - struct most_aim_attribute *attr, - char *buf); - ssize_t (*store)(struct most_aim_obj *d, - struct most_aim_attribute *attr, - const char *buf, - size_t count); -}; - -#define to_aim_attr(a) container_of(a, struct most_aim_attribute, attr) - -/** - * aim_attr_show - show function of an AIM object - * @kobj: pointer to kobject - * @attr: pointer to attribute struct - * @buf: buffer - */ -static ssize_t aim_attr_show(struct kobject *kobj, - struct attribute *attr, - char *buf) -{ - struct most_aim_attribute *aim_attr; - struct most_aim_obj *aim_obj; - - aim_attr = to_aim_attr(attr); - aim_obj = to_aim_obj(kobj); - - if (!aim_attr->show) - return -EIO; - - return aim_attr->show(aim_obj, aim_attr, buf); -} - -/** - * aim_attr_store - store function of an AIM object - * @kobj: pointer to kobject - * @attr: pointer to attribute struct - * @buf: buffer - * @len: length of buffer - */ -static ssize_t aim_attr_store(struct kobject *kobj, - struct attribute *attr, - const char *buf, - size_t len) -{ - struct most_aim_attribute *aim_attr; - struct most_aim_obj *aim_obj; - - aim_attr = to_aim_attr(attr); - aim_obj = to_aim_obj(kobj); - - if (!aim_attr->store) - return -EIO; - return aim_attr->store(aim_obj, aim_attr, buf, len); -} - -static const struct sysfs_ops most_aim_sysfs_ops = { - .show = aim_attr_show, - .store = aim_attr_store, -}; - -/** - * most_aim_release - AIM release function - * @kobj: pointer to AIM's kobject - */ -static void most_aim_release(struct kobject *kobj) -{ - struct most_aim_obj *aim_obj = to_aim_obj(kobj); - - kfree(aim_obj); -} - -static ssize_t links_show(struct most_aim_obj *aim_obj, - struct most_aim_attribute *attr, +static ssize_t links_show(struct device *dev, struct device_attribute *attr, char *buf) { struct most_c_obj *c; struct most_inst_obj *i; + struct most_aim *aim = to_most_aim(dev); int offs = 0; list_for_each_entry(i, &instance_list, list) { list_for_each_entry(c, &i->channel_list, list) { - if (c->aim0.ptr == aim_obj->driver || - c->aim1.ptr == aim_obj->driver) { + if (c->aim0.ptr == aim || c->aim1.ptr == aim) { offs += snprintf(buf + offs, PAGE_SIZE - offs, "%s:%s\n", - kobject_name(&i->kobj), - kobject_name(&c->kobj)); + dev_name(&i->iface->dev), + dev_name(&c->dev)); } } } @@ -901,7 +601,7 @@ most_c_obj *get_channel_by_name(char *mdev, char *mdev_ch) int found = 0; list_for_each_entry_safe(i, i_tmp, &instance_list, list) { - if (!strcmp(kobject_name(&i->kobj), mdev)) { + if (!strcmp(dev_name(&i->iface->dev), mdev)) { found++; break; } @@ -910,7 +610,7 @@ most_c_obj *get_channel_by_name(char *mdev, char *mdev_ch) return ERR_PTR(-EIO); list_for_each_entry_safe(c, tmp, &i->channel_list, list) { - if (!strcmp(kobject_name(&c->kobj), mdev_ch)) { + if (!strcmp(dev_name(&c->dev), mdev_ch)) { found++; break; } @@ -942,13 +642,14 @@ most_c_obj *get_channel_by_name(char *mdev, char *mdev_ch) * (1) would create the device node /dev/my_rxchannel * (2) would create the device node /dev/mdev1-ep81 */ -static ssize_t add_link_store(struct most_aim_obj *aim_obj, - struct most_aim_attribute *attr, +static ssize_t add_link_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) { struct most_c_obj *c; struct most_aim **aim_ptr; + struct most_aim *aim = to_most_aim(dev); char buffer[STRING_SIZE]; char *mdev; char *mdev_ch; @@ -980,9 +681,8 @@ static ssize_t add_link_store(struct most_aim_obj *aim_obj, else return -ENOSPC; - *aim_ptr = aim_obj->driver; - ret = aim_obj->driver->probe_channel(c->iface, c->channel_id, - &c->cfg, &c->kobj, mdev_devnod); + *aim_ptr = aim; + ret = aim->probe_channel(c->iface, c->channel_id, &c->cfg, mdev_devnod); if (ret) { *aim_ptr = NULL; return ret; @@ -1001,12 +701,13 @@ static ssize_t add_link_store(struct most_aim_obj *aim_obj, * Example: * echo "mdev0:ep81" >remove_link */ -static ssize_t remove_link_store(struct most_aim_obj *aim_obj, - struct most_aim_attribute *attr, +static ssize_t remove_link_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) { struct most_c_obj *c; + struct most_aim *aim = to_most_aim(dev); char buffer[STRING_SIZE]; char *mdev; char *mdev_ch; @@ -1022,74 +723,34 @@ static ssize_t remove_link_store(struct most_aim_obj *aim_obj, if (IS_ERR(c)) return -ENODEV; - if (aim_obj->driver->disconnect_channel(c->iface, c->channel_id)) + if (aim->disconnect_channel(c->iface, c->channel_id)) return -EIO; - if (c->aim0.ptr == aim_obj->driver) + if (c->aim0.ptr == aim) c->aim0.ptr = NULL; - if (c->aim1.ptr == aim_obj->driver) + if (c->aim1.ptr == aim) c->aim1.ptr = NULL; return len; } -static struct most_aim_attribute most_aim_attrs[] = { - __ATTR_RO(links), - __ATTR_WO(add_link), - __ATTR_WO(remove_link), -}; +static DEVICE_ATTR_RO(links); +static DEVICE_ATTR_WO(add_link); +static DEVICE_ATTR_WO(remove_link); -static struct attribute *most_aim_def_attrs[] = { - &most_aim_attrs[0].attr, - &most_aim_attrs[1].attr, - &most_aim_attrs[2].attr, +static struct attribute *aim_attrs[] = { + DEV_ATTR(links), + DEV_ATTR(add_link), + DEV_ATTR(remove_link), NULL, }; -static struct kobj_type most_aim_ktype = { - .sysfs_ops = &most_aim_sysfs_ops, - .release = most_aim_release, - .default_attrs = most_aim_def_attrs, +static struct attribute_group aim_attr_group = { + .attrs = aim_attrs, }; -static struct kset *most_aim_kset; - -/** - * create_most_aim_obj - creates an AIM object - * @name: name of the AIM - * - * This creates an AIM object assigns the proper kset and registers - * it with sysfs. - * Returns a pointer to the object or NULL if something went wrong. - */ -static struct most_aim_obj *create_most_aim_obj(const char *name) -{ - struct most_aim_obj *most_aim; - int retval; - - most_aim = kzalloc(sizeof(*most_aim), GFP_KERNEL); - if (!most_aim) - return NULL; - most_aim->kobj.kset = most_aim_kset; - retval = kobject_init_and_add(&most_aim->kobj, &most_aim_ktype, - NULL, "%s", name); - if (retval) { - kobject_put(&most_aim->kobj); - return NULL; - } - kobject_uevent(&most_aim->kobj, KOBJ_ADD); - return most_aim; -} - -/** - * destroy_most_aim_obj - AIM release function - * @p: pointer to AIM object - * - * This decrements the reference counter of the AIM object. If the - * reference count turns zero, its release function will be called. - */ -static void destroy_most_aim_obj(struct most_aim_obj *p) -{ - kobject_put(&p->kobj); -} +static const struct attribute_group *aim_attr_groups[] = { + &aim_attr_group, + NULL, +}; /* ___ ___ * ___C O R E___ @@ -1615,28 +1276,35 @@ out: } EXPORT_SYMBOL_GPL(most_stop_channel); +void release_aim(struct device *dev) +{ + pr_info("releasing aim %s\n", dev_name(dev)); +} + /** * most_register_aim - registers an AIM (driver) with the core * @aim: instance of AIM to be registered */ int most_register_aim(struct most_aim *aim) { - struct most_aim_obj *aim_obj; + int ret; if (!aim) { pr_err("Bad driver\n"); return -EINVAL; } - aim_obj = create_most_aim_obj(aim->name); - if (!aim_obj) { - pr_info("failed to alloc driver object\n"); - return -ENOMEM; + aim->dev.init_name = aim->name; + aim->dev.bus = &most_bus; + aim->dev.parent = &core_dev; + aim->dev.groups = aim_attr_groups; + aim->dev.release = release_aim; + ret = device_register(&aim->dev); + if (ret) { + pr_err("registering device %s failed\n", aim->name); + return ret; } - aim_obj->driver = aim; - aim->context = aim_obj; pr_info("registered new application interfacing module %s\n", aim->name); - list_add_tail(&aim_obj->list, &aim_list); return 0; } EXPORT_SYMBOL_GPL(most_register_aim); @@ -1647,7 +1315,6 @@ EXPORT_SYMBOL_GPL(most_register_aim); */ int most_deregister_aim(struct most_aim *aim) { - struct most_aim_obj *aim_obj; struct most_c_obj *c, *tmp; struct most_inst_obj *i, *i_tmp; @@ -1656,11 +1323,6 @@ int most_deregister_aim(struct most_aim *aim) return -EINVAL; } - aim_obj = aim->context; - if (!aim_obj) { - pr_info("driver not registered.\n"); - return -EINVAL; - } list_for_each_entry_safe(i, i_tmp, &instance_list, list) { list_for_each_entry_safe(c, tmp, &i->channel_list, list) { if (c->aim0.ptr == aim || c->aim1.ptr == aim) @@ -1672,13 +1334,22 @@ int most_deregister_aim(struct most_aim *aim) c->aim1.ptr = NULL; } } - list_del(&aim_obj->list); - destroy_most_aim_obj(aim_obj); + device_unregister(&aim->dev); pr_info("deregistering application interfacing module %s\n", aim->name); return 0; } EXPORT_SYMBOL_GPL(most_deregister_aim); +static void release_interface(struct device *dev) +{ + pr_info("releasing interface dev %s...\n", dev_name(dev)); +} + +static void release_channel(struct device *dev) +{ + pr_info("releasing channel dev %s...\n", dev_name(dev)); +} + /** * most_register_interface - registers an interface with core * @iface: pointer to the instance of the interface description. @@ -1686,7 +1357,7 @@ EXPORT_SYMBOL_GPL(most_deregister_aim); * Allocates and initializes a new interface instance and all of its channels. * Returns a pointer to kobject or an error pointer. */ -struct kobject *most_register_interface(struct most_interface *iface) +int most_register_interface(struct most_interface *iface) { unsigned int i; int id; @@ -1698,21 +1369,20 @@ struct kobject *most_register_interface(struct most_interface *iface) if (!iface || !iface->enqueue || !iface->configure || !iface->poison_channel || (iface->num_channels > MAX_CHANNELS)) { pr_err("Bad interface or channel overflow\n"); - return ERR_PTR(-EINVAL); + return -EINVAL; } id = ida_simple_get(&mdev_id, 0, 0, GFP_KERNEL); if (id < 0) { pr_info("Failed to alloc mdev ID\n"); - return ERR_PTR(id); + return id; } - snprintf(name, STRING_SIZE, "mdev%d", id); - inst = create_most_inst_obj(name); + inst = kzalloc(sizeof(*inst), GFP_KERNEL); if (!inst) { pr_info("Failed to allocate interface instance\n"); ida_simple_remove(&mdev_id, id); - return ERR_PTR(-ENOMEM); + return -ENOMEM; } iface->priv = inst; @@ -1720,6 +1390,18 @@ struct kobject *most_register_interface(struct most_interface *iface) inst->iface = iface; inst->dev_id = id; list_add_tail(&inst->list, &instance_list); + snprintf(name, STRING_SIZE, "mdev%d", id); + iface->dev.init_name = name; + iface->dev.bus = &most_bus; + iface->dev.parent = &core_dev; + iface->dev.groups = interface_attr_groups; + iface->dev.release = release_interface; + if (device_register(&iface->dev)) { + pr_err("registering iface->dev failed\n"); + kfree(inst); + ida_simple_remove(&mdev_id, id); + return -ENOMEM; + } for (i = 0; i < iface->num_channels; i++) { const char *name_suffix = iface->channel_vector[i].name_suffix; @@ -1730,9 +1412,17 @@ struct kobject *most_register_interface(struct most_interface *iface) snprintf(channel_name, STRING_SIZE, "%s", name_suffix); /* this increments the reference count of this instance */ - c = create_most_c_obj(channel_name, &inst->kobj); + c = kzalloc(sizeof(*c), GFP_KERNEL); if (!c) goto free_instance; + c->dev.init_name = channel_name; + c->dev.parent = &iface->dev; + c->dev.groups = channel_attr_groups; + c->dev.release = release_channel; + if (device_register(&c->dev)) { + pr_err("registering c->dev failed\n"); + goto free_instance; + } inst->channel[i] = c; c->is_starving = 0; c->iface = iface; @@ -1758,15 +1448,14 @@ struct kobject *most_register_interface(struct most_interface *iface) list_add_tail(&c->list, &inst->channel_list); } pr_info("registered new MOST device mdev%d (%s)\n", - inst->dev_id, iface->description); - return &inst->kobj; + id, iface->description); + return 0; free_instance: pr_info("Failed allocate channel(s)\n"); - list_del(&inst->list); + device_unregister(&iface->dev); ida_simple_remove(&mdev_id, id); - destroy_most_inst_obj(inst); - return ERR_PTR(-ENOMEM); + return -ENOMEM; } EXPORT_SYMBOL_GPL(most_register_interface); @@ -1779,17 +1468,14 @@ EXPORT_SYMBOL_GPL(most_register_interface); */ void most_deregister_interface(struct most_interface *iface) { - struct most_inst_obj *i = iface->priv; + int i; struct most_c_obj *c; + struct most_inst_obj *inst; - if (unlikely(!i)) { - pr_info("Bad Interface\n"); - return; - } - pr_info("deregistering MOST device %s (%s)\n", i->kobj.name, - iface->description); - - list_for_each_entry(c, &i->channel_list, list) { + pr_info("deregistering MOST device %s (%s)\n", dev_name(&iface->dev), iface->description); + inst = iface->priv; + for (i = 0; i < iface->num_channels; i++) { + c = inst->channel[i]; if (c->aim0.ptr) c->aim0.ptr->disconnect_channel(c->iface, c->channel_id); @@ -1798,11 +1484,14 @@ void most_deregister_interface(struct most_interface *iface) c->channel_id); c->aim0.ptr = NULL; c->aim1.ptr = NULL; + list_del(&c->list); + device_unregister(&c->dev); + kfree(c); } - ida_simple_remove(&mdev_id, i->dev_id); - list_del(&i->list); - destroy_most_inst_obj(i); + ida_simple_remove(&mdev_id, inst->dev_id); + kfree(inst); + device_unregister(&iface->dev); } EXPORT_SYMBOL_GPL(most_deregister_interface); @@ -1852,13 +1541,17 @@ void most_resume_enqueue(struct most_interface *iface, int id) } EXPORT_SYMBOL_GPL(most_resume_enqueue); +static void release_most_sub(struct device *dev) +{ + pr_info("releasing most_subsystem\n"); +} + static int __init most_init(void) { int err; pr_info("init()\n"); INIT_LIST_HEAD(&instance_list); - INIT_LIST_HEAD(&aim_list); ida_init(&mdev_id); err = bus_register(&most_bus); @@ -1880,30 +1573,15 @@ static int __init most_init(void) goto exit_class; } - core_dev = device_create(most_class, NULL, 0, NULL, "mostcore"); - if (IS_ERR(core_dev)) { - err = PTR_ERR(core_dev); - goto exit_driver; - } - - most_aim_kset = kset_create_and_add("aims", NULL, &core_dev->kobj); - if (!most_aim_kset) { - err = -ENOMEM; - goto exit_class_container; - } - - most_inst_kset = kset_create_and_add("devices", NULL, &core_dev->kobj); - if (!most_inst_kset) { + core_dev.init_name = "most_bus"; + core_dev.release = release_most_sub; + if (device_register(&core_dev)) { err = -ENOMEM; - goto exit_driver_kset; + goto exit_driver; } return 0; -exit_driver_kset: - kset_unregister(most_aim_kset); -exit_class_container: - device_destroy(most_class, 0); exit_driver: driver_unregister(&mostcore); exit_class: @@ -1915,21 +1593,8 @@ exit_bus: static void __exit most_exit(void) { - struct most_inst_obj *i, *i_tmp; - struct most_aim_obj *d, *d_tmp; - pr_info("exit core module\n"); - list_for_each_entry_safe(d, d_tmp, &aim_list, list) { - destroy_most_aim_obj(d); - } - - list_for_each_entry_safe(i, i_tmp, &instance_list, list) { - list_del(&i->list); - destroy_most_inst_obj(i); - } - kset_unregister(most_inst_kset); - kset_unregister(most_aim_kset); - device_destroy(most_class, 0); + device_unregister(&core_dev); driver_unregister(&mostcore); class_destroy(most_class); bus_unregister(&most_bus); diff --git a/drivers/staging/most/core.h b/drivers/staging/most/core.h index 5577e8735196..29595d9ef7ed 100644 --- a/drivers/staging/most/core.h +++ b/drivers/staging/most/core.h @@ -17,8 +17,8 @@ #define __MOST_CORE_H__ #include +#include -struct kobject; struct module; /** @@ -232,6 +232,7 @@ struct mbo { * @priv Private field used by mostcore to store context information. */ struct most_interface { + struct device dev; struct module *mod; enum most_interface_type interface; const char *description; @@ -249,6 +250,8 @@ struct most_interface { void *priv; }; +#define to_most_interface(d) container_of(d, struct most_interface, dev) + /** * struct most_aim - identifies MOST device driver to mostcore * @name: Driver name @@ -259,10 +262,10 @@ struct most_interface { * @context: context pointer to be used by mostcore */ struct most_aim { + struct device dev; const char *name; int (*probe_channel)(struct most_interface *iface, int channel_idx, - struct most_channel_config *cfg, - struct kobject *parent, char *name); + struct most_channel_config *cfg, char *name); int (*disconnect_channel)(struct most_interface *iface, int channel_idx); int (*rx_completion)(struct mbo *mbo); @@ -270,6 +273,8 @@ struct most_aim { void *context; }; +#define to_most_aim(d) container_of(d, struct most_aim, dev) + /** * most_register_interface - Registers instance of the interface. * @iface: Pointer to the interface instance description. @@ -279,7 +284,7 @@ struct most_aim { * Note: HDM has to ensure that any reference held on the kobj is * released before deregistering the interface. */ -struct kobject *most_register_interface(struct most_interface *iface); +int most_register_interface(struct most_interface *iface); /** * Deregisters instance of the interface. diff --git a/drivers/staging/most/dim2/dim2.c b/drivers/staging/most/dim2/dim2.c index 921db9880d80..2bd40abbc8c6 100644 --- a/drivers/staging/most/dim2/dim2.c +++ b/drivers/staging/most/dim2/dim2.c @@ -87,6 +87,7 @@ struct hdm_channel { * @atx_idx: index of async tx channel */ struct dim2_hdm { + struct device dev; struct hdm_channel hch[DMA_CHANNELS]; struct most_channel_capability capabilities[DMA_CHANNELS]; struct most_interface most_iface; @@ -738,7 +739,6 @@ static int dim2_probe(struct platform_device *pdev) struct dim2_hdm *dev; struct resource *res; int ret, i; - struct kobject *kobj; int irq; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); @@ -826,17 +826,20 @@ static int dim2_probe(struct platform_device *pdev) dev->most_iface.enqueue = enqueue; dev->most_iface.poison_channel = poison_channel; dev->most_iface.request_netinfo = request_netinfo; + dev->dev.init_name = "dim2_state"; + dev->dev.parent = &dev->most_iface.dev; - kobj = most_register_interface(&dev->most_iface); - if (IS_ERR(kobj)) { - ret = PTR_ERR(kobj); + ret = most_register_interface(&dev->most_iface); + if (ret) { dev_err(&pdev->dev, "failed to register MOST interface\n"); goto err_stop_thread; } - ret = dim2_sysfs_probe(&dev->bus, kobj); - if (ret) + ret = dim2_sysfs_probe(&dev->dev); + if (ret) { + dev_err(&pdev->dev, "failed to create sysfs attribute\n"); goto err_unreg_iface; + } ret = startup_dim(pdev); if (ret) { @@ -847,7 +850,7 @@ static int dim2_probe(struct platform_device *pdev) return 0; err_destroy_bus: - dim2_sysfs_destroy(&dev->bus); + dim2_sysfs_destroy(&dev->dev); err_unreg_iface: most_deregister_interface(&dev->most_iface); err_stop_thread: @@ -875,7 +878,7 @@ static int dim2_remove(struct platform_device *pdev) if (pdata && pdata->destroy) pdata->destroy(pdata); - dim2_sysfs_destroy(&dev->bus); + dim2_sysfs_destroy(&dev->dev); most_deregister_interface(&dev->most_iface); kthread_stop(dev->netinfo_task); diff --git a/drivers/staging/most/dim2/sysfs.c b/drivers/staging/most/dim2/sysfs.c index ec1f4cecf9e7..7ead7030c6b8 100644 --- a/drivers/staging/most/dim2/sysfs.c +++ b/drivers/staging/most/dim2/sysfs.c @@ -11,99 +11,39 @@ #include #include "sysfs.h" +#include -struct bus_attr { - struct attribute attr; - ssize_t (*show)(struct medialb_bus *bus, char *buf); - ssize_t (*store)(struct medialb_bus *bus, const char *buf, - size_t count); -}; - -static ssize_t state_show(struct medialb_bus *bus, char *buf) +static ssize_t state_show(struct device *dev, struct device_attribute *attr, + char *buf) { bool state = dim2_sysfs_get_state_cb(); return sprintf(buf, "%s\n", state ? "locked" : ""); } -static struct bus_attr state_attr = __ATTR_RO(state); +DEVICE_ATTR_RO(state); -static struct attribute *bus_default_attrs[] = { - &state_attr.attr, +static struct attribute *dev_attrs[] = { + &dev_attr_state.attr, NULL, }; -static const struct attribute_group bus_attr_group = { - .attrs = bus_default_attrs, +static struct attribute_group dev_attr_group = { + .attrs = dev_attrs, }; -static void bus_kobj_release(struct kobject *kobj) -{ -} - -static ssize_t bus_kobj_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - struct medialb_bus *bus = - container_of(kobj, struct medialb_bus, kobj_group); - struct bus_attr *xattr = container_of(attr, struct bus_attr, attr); - - if (!xattr->show) - return -EIO; - - return xattr->show(bus, buf); -} - -static ssize_t bus_kobj_attr_store(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t count) -{ - struct medialb_bus *bus = - container_of(kobj, struct medialb_bus, kobj_group); - struct bus_attr *xattr = container_of(attr, struct bus_attr, attr); - - if (!xattr->store) - return -EIO; - - return xattr->store(bus, buf, count); -} - -static struct sysfs_ops const bus_kobj_sysfs_ops = { - .show = bus_kobj_attr_show, - .store = bus_kobj_attr_store, -}; - -static struct kobj_type bus_ktype = { - .release = bus_kobj_release, - .sysfs_ops = &bus_kobj_sysfs_ops, +static const struct attribute_group *dev_attr_groups[] = { + &dev_attr_group, + NULL, }; -int dim2_sysfs_probe(struct medialb_bus *bus, struct kobject *parent_kobj) +int dim2_sysfs_probe(struct device *dev) { - int err; - - kobject_init(&bus->kobj_group, &bus_ktype); - err = kobject_add(&bus->kobj_group, parent_kobj, "bus"); - if (err) { - pr_err("kobject_add() failed: %d\n", err); - goto err_kobject_add; - } - - err = sysfs_create_group(&bus->kobj_group, &bus_attr_group); - if (err) { - pr_err("sysfs_create_group() failed: %d\n", err); - goto err_create_group; - } - - return 0; - -err_create_group: - kobject_put(&bus->kobj_group); - -err_kobject_add: - return err; + dev->groups = dev_attr_groups; + return device_register(dev); } -void dim2_sysfs_destroy(struct medialb_bus *bus) +void dim2_sysfs_destroy(struct device *dev) { - kobject_put(&bus->kobj_group); + device_unregister(dev); } diff --git a/drivers/staging/most/dim2/sysfs.h b/drivers/staging/most/dim2/sysfs.h index a33ebd8b45f5..33756a3bffe2 100644 --- a/drivers/staging/most/dim2/sysfs.h +++ b/drivers/staging/most/dim2/sysfs.h @@ -16,10 +16,10 @@ struct medialb_bus { struct kobject kobj_group; }; -struct dim2_hdm; +struct device; -int dim2_sysfs_probe(struct medialb_bus *bus, struct kobject *parent_kobj); -void dim2_sysfs_destroy(struct medialb_bus *bus); +int dim2_sysfs_probe(struct device *dev); +void dim2_sysfs_destroy(struct device *dev); /* * callback, diff --git a/drivers/staging/most/i2c/i2c.c b/drivers/staging/most/i2c/i2c.c index 156b2b1bdba9..141239fc9f51 100644 --- a/drivers/staging/most/i2c/i2c.c +++ b/drivers/staging/most/i2c/i2c.c @@ -303,7 +303,6 @@ static int i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct hdm_i2c *dev; int ret, i; - struct kobject *kobj; dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) @@ -341,11 +340,11 @@ static int i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) dev->client = client; i2c_set_clientdata(client, dev); - kobj = most_register_interface(&dev->most_iface); - if (IS_ERR(kobj)) { + ret = most_register_interface(&dev->most_iface); + if (ret) { pr_err("Failed to register i2c as a MOST interface\n"); kfree(dev); - return PTR_ERR(kobj); + return ret; } dev->polling_mode = polling_req || client->irq <= 0; diff --git a/drivers/staging/most/net/net.c b/drivers/staging/most/net/net.c index 3cbd403aa516..a9323e4afad7 100644 --- a/drivers/staging/most/net/net.c +++ b/drivers/staging/most/net/net.c @@ -291,8 +291,7 @@ static struct net_dev_context *get_net_dev_hold(struct most_interface *iface) } static int aim_probe_channel(struct most_interface *iface, int channel_idx, - struct most_channel_config *ccfg, - struct kobject *parent, char *name) + struct most_channel_config *ccfg, char *name) { struct net_dev_context *nd; struct net_dev_channel *ch; diff --git a/drivers/staging/most/sound/sound.c b/drivers/staging/most/sound/sound.c index 5826f710c925..5504f93aca39 100644 --- a/drivers/staging/most/sound/sound.c +++ b/drivers/staging/most/sound/sound.c @@ -539,7 +539,6 @@ error: * @iface: pointer to interface instance * @channel_id: channel index/ID * @cfg: pointer to actual channel configuration - * @parent: pointer to kobject (needed for sysfs hook-up) * @arg_list: string that provides the name of the device to be created in /dev * plus the desired audio resolution * @@ -549,7 +548,7 @@ error: */ static int audio_probe_channel(struct most_interface *iface, int channel_id, struct most_channel_config *cfg, - struct kobject *parent, char *arg_list) + char *arg_list) { struct channel *channel; struct snd_card *card; diff --git a/drivers/staging/most/usb/usb.c b/drivers/staging/most/usb/usb.c index b2d2abe15efe..7da7dd5e718d 100644 --- a/drivers/staging/most/usb/usb.c +++ b/drivers/staging/most/usb/usb.c @@ -64,12 +64,12 @@ * @reg_addr: register address for arbitrary DCI access */ struct most_dci_obj { - struct kobject kobj; + struct device dev; struct usb_device *usb_device; u16 reg_addr; }; -#define to_dci_obj(p) container_of(p, struct most_dci_obj, kobj) +#define to_dci_obj(p) container_of(p, struct most_dci_obj, dev) struct most_dev; @@ -84,7 +84,6 @@ struct clear_hold_work { /** * struct most_dev - holds all usb interface specific stuff - * @parent: parent object in sysfs * @usb_device: pointer to usb device * @iface: hardware interface * @cap: channel capabilities @@ -102,7 +101,6 @@ struct clear_hold_work { * @poll_work_obj: work for polling link status */ struct most_dev { - struct kobject *parent; struct usb_device *usb_device; struct most_interface iface; struct most_channel_capability *cap; @@ -834,94 +832,6 @@ static const struct usb_device_id usbid[] = { { } /* Terminating entry */ }; -#define MOST_DCI_RO_ATTR(_name) \ - struct most_dci_attribute most_dci_attr_##_name = \ - __ATTR(_name, 0444, show_value, NULL) - -#define MOST_DCI_ATTR(_name) \ - struct most_dci_attribute most_dci_attr_##_name = \ - __ATTR(_name, 0644, show_value, store_value) - -#define MOST_DCI_WO_ATTR(_name) \ - struct most_dci_attribute most_dci_attr_##_name = \ - __ATTR(_name, 0200, NULL, store_value) - -/** - * struct most_dci_attribute - to access the attributes of a dci object - * @attr: attributes of a dci object - * @show: pointer to the show function - * @store: pointer to the store function - */ -struct most_dci_attribute { - struct attribute attr; - ssize_t (*show)(struct most_dci_obj *d, - struct most_dci_attribute *attr, - char *buf); - ssize_t (*store)(struct most_dci_obj *d, - struct most_dci_attribute *attr, - const char *buf, - size_t count); -}; - -#define to_dci_attr(a) container_of(a, struct most_dci_attribute, attr) - -/** - * dci_attr_show - show function for dci object - * @kobj: pointer to kobject - * @attr: pointer to attribute struct - * @buf: buffer - */ -static ssize_t dci_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - struct most_dci_attribute *dci_attr = to_dci_attr(attr); - struct most_dci_obj *dci_obj = to_dci_obj(kobj); - - if (!dci_attr->show) - return -EIO; - - return dci_attr->show(dci_obj, dci_attr, buf); -} - -/** - * dci_attr_store - store function for dci object - * @kobj: pointer to kobject - * @attr: pointer to attribute struct - * @buf: buffer - * @len: length of buffer - */ -static ssize_t dci_attr_store(struct kobject *kobj, - struct attribute *attr, - const char *buf, - size_t len) -{ - struct most_dci_attribute *dci_attr = to_dci_attr(attr); - struct most_dci_obj *dci_obj = to_dci_obj(kobj); - - if (!dci_attr->store) - return -EIO; - - return dci_attr->store(dci_obj, dci_attr, buf, len); -} - -static const struct sysfs_ops most_dci_sysfs_ops = { - .show = dci_attr_show, - .store = dci_attr_store, -}; - -/** - * most_dci_release - release function for dci object - * @kobj: pointer to kobject - * - * This frees the memory allocated for the dci object - */ -static void most_dci_release(struct kobject *kobj) -{ - struct most_dci_obj *dci_obj = to_dci_obj(kobj); - - kfree(dci_obj); -} - struct regs { const char *name; u16 reg; @@ -962,10 +872,11 @@ static int get_stat_reg_addr(const struct regs *regs, int size, #define get_static_reg_addr(regs, name, reg_addr) \ get_stat_reg_addr(regs, ARRAY_SIZE(regs), name, reg_addr) -static ssize_t show_value(struct most_dci_obj *dci_obj, - struct most_dci_attribute *attr, char *buf) +static ssize_t show_value(struct device *dev, struct device_attribute *attr, + char *buf) { const char *name = attr->attr.name; + struct most_dci_obj *dci_obj = to_dci_obj(dev); u16 val; u16 reg_addr; int err; @@ -986,13 +897,13 @@ static ssize_t show_value(struct most_dci_obj *dci_obj, return snprintf(buf, PAGE_SIZE, "%04x\n", val); } -static ssize_t store_value(struct most_dci_obj *dci_obj, - struct most_dci_attribute *attr, +static ssize_t store_value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { u16 val; u16 reg_addr; const char *name = attr->attr.name; + struct most_dci_obj *dci_obj = to_dci_obj(dev); struct usb_device *usb_dev = dci_obj->usb_device; int err = kstrtou16(buf, 16, &val); @@ -1019,86 +930,49 @@ static ssize_t store_value(struct most_dci_obj *dci_obj, return count; } -static MOST_DCI_RO_ATTR(ni_state); -static MOST_DCI_RO_ATTR(packet_bandwidth); -static MOST_DCI_RO_ATTR(node_address); -static MOST_DCI_RO_ATTR(node_position); -static MOST_DCI_WO_ATTR(sync_ep); -static MOST_DCI_ATTR(mep_filter); -static MOST_DCI_ATTR(mep_hash0); -static MOST_DCI_ATTR(mep_hash1); -static MOST_DCI_ATTR(mep_hash2); -static MOST_DCI_ATTR(mep_hash3); -static MOST_DCI_ATTR(mep_eui48_hi); -static MOST_DCI_ATTR(mep_eui48_mi); -static MOST_DCI_ATTR(mep_eui48_lo); -static MOST_DCI_ATTR(arb_address); -static MOST_DCI_ATTR(arb_value); - -/** - * most_dci_def_attrs - array of default attribute files of the dci object - */ -static struct attribute *most_dci_def_attrs[] = { - &most_dci_attr_ni_state.attr, - &most_dci_attr_packet_bandwidth.attr, - &most_dci_attr_node_address.attr, - &most_dci_attr_node_position.attr, - &most_dci_attr_sync_ep.attr, - &most_dci_attr_mep_filter.attr, - &most_dci_attr_mep_hash0.attr, - &most_dci_attr_mep_hash1.attr, - &most_dci_attr_mep_hash2.attr, - &most_dci_attr_mep_hash3.attr, - &most_dci_attr_mep_eui48_hi.attr, - &most_dci_attr_mep_eui48_mi.attr, - &most_dci_attr_mep_eui48_lo.attr, - &most_dci_attr_arb_address.attr, - &most_dci_attr_arb_value.attr, +DEVICE_ATTR(ni_state, 0444, show_value, NULL); +DEVICE_ATTR(packet_bandwidth, 0444, show_value, NULL); +DEVICE_ATTR(node_address, 0444, show_value, NULL); +DEVICE_ATTR(node_position, 0444, show_value, NULL); +DEVICE_ATTR(sync_ep, 0200, NULL, store_value); +DEVICE_ATTR(mep_filter, 0644, show_value, store_value); +DEVICE_ATTR(mep_hash0, 0644, show_value, store_value); +DEVICE_ATTR(mep_hash1, 0644, show_value, store_value); +DEVICE_ATTR(mep_hash2, 0644, show_value, store_value); +DEVICE_ATTR(mep_hash3, 0644, show_value, store_value); +DEVICE_ATTR(mep_eui48_hi, 0644, show_value, store_value); +DEVICE_ATTR(mep_eui48_mi, 0644, show_value, store_value); +DEVICE_ATTR(mep_eui48_lo, 0644, show_value, store_value); +DEVICE_ATTR(arb_address, 0644, show_value, store_value); +DEVICE_ATTR(arb_value, 0644, show_value, store_value); + +static struct attribute *dci_attrs[] = { + &dev_attr_ni_state.attr, + &dev_attr_packet_bandwidth.attr, + &dev_attr_node_address.attr, + &dev_attr_node_position.attr, + &dev_attr_sync_ep.attr, + &dev_attr_mep_filter.attr, + &dev_attr_mep_hash0.attr, + &dev_attr_mep_hash1.attr, + &dev_attr_mep_hash2.attr, + &dev_attr_mep_hash3.attr, + &dev_attr_mep_eui48_hi.attr, + &dev_attr_mep_eui48_mi.attr, + &dev_attr_mep_eui48_lo.attr, + &dev_attr_arb_address.attr, + &dev_attr_arb_value.attr, NULL, }; -/** - * DCI ktype - */ -static struct kobj_type most_dci_ktype = { - .sysfs_ops = &most_dci_sysfs_ops, - .release = most_dci_release, - .default_attrs = most_dci_def_attrs, +static struct attribute_group dci_attr_group = { + .attrs = dci_attrs, }; -/** - * create_most_dci_obj - allocates a dci object - * @parent: parent kobject - * - * This creates a dci object and registers it with sysfs. - * Returns a pointer to the object or NULL when something went wrong. - */ -static struct -most_dci_obj *create_most_dci_obj(struct kobject *parent) -{ - struct most_dci_obj *most_dci = kzalloc(sizeof(*most_dci), GFP_KERNEL); - int retval; - - if (!most_dci) - return NULL; - - retval = kobject_init_and_add(&most_dci->kobj, &most_dci_ktype, parent, - "dci"); - if (retval) { - kobject_put(&most_dci->kobj); - return NULL; - } - return most_dci; -} - -/** - * destroy_most_dci_obj - DCI object release function - * @p: pointer to dci object - */ -static void destroy_most_dci_obj(struct most_dci_obj *p) -{ - kobject_put(&p->kobj); -} +static const struct attribute_group *dci_attr_groups[] = { + &dci_attr_group, + NULL, +}; /** * hdm_probe - probe function of USB device driver @@ -1211,20 +1085,15 @@ hdm_probe(struct usb_interface *interface, const struct usb_device_id *id) usb_dev->config->desc.bConfigurationValue, usb_iface_desc->desc.bInterfaceNumber); - mdev->parent = most_register_interface(&mdev->iface); - if (IS_ERR(mdev->parent)) { - ret = PTR_ERR(mdev->parent); + ret = most_register_interface(&mdev->iface); + if (ret) goto exit_free4; - } mutex_lock(&mdev->io_mutex); if (le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81118 || le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81119 || le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81210) { - /* this increments the reference count of the instance - * object of the core - */ - mdev->dci = create_most_dci_obj(mdev->parent); + mdev->dci = kzalloc(sizeof(*mdev->dci), GFP_KERNEL); if (!mdev->dci) { mutex_unlock(&mdev->io_mutex); most_deregister_interface(&mdev->iface); @@ -1232,12 +1101,21 @@ hdm_probe(struct usb_interface *interface, const struct usb_device_id *id) goto exit_free4; } - kobject_uevent(&mdev->dci->kobj, KOBJ_ADD); + mdev->dci->dev.init_name = "dci"; + mdev->dci->dev.parent = &mdev->iface.dev; + mdev->dci->dev.groups = dci_attr_groups; + if (device_register(&mdev->dci->dev)) { + mutex_unlock(&mdev->io_mutex); + most_deregister_interface(&mdev->iface); + ret = -ENOMEM; + goto exit_free5; + } mdev->dci->usb_device = mdev->usb_device; } mutex_unlock(&mdev->io_mutex); return 0; - +exit_free5: + kfree(mdev->dci); exit_free4: kfree(mdev->busy_urbs); exit_free3: @@ -1277,7 +1155,8 @@ static void hdm_disconnect(struct usb_interface *interface) del_timer_sync(&mdev->link_stat_timer); cancel_work_sync(&mdev->poll_work_obj); - destroy_most_dci_obj(mdev->dci); + device_unregister(&mdev->dci->dev); + kfree(mdev->dci); most_deregister_interface(&mdev->iface); kfree(mdev->busy_urbs); diff --git a/drivers/staging/most/video/video.c b/drivers/staging/most/video/video.c index 3c813ed7f3ec..25ae4887b04d 100644 --- a/drivers/staging/most/video/video.c +++ b/drivers/staging/most/video/video.c @@ -480,8 +480,7 @@ static void aim_v4l2_dev_release(struct v4l2_device *v4l2_dev) } static int aim_probe_channel(struct most_interface *iface, int channel_idx, - struct most_channel_config *ccfg, - struct kobject *parent, char *name) + struct most_channel_config *ccfg, char *name) { int ret; struct most_video_dev *mdev = get_aim_dev(iface, channel_idx); -- cgit From 14c018a93e8e74310e2e9f9a9e6cb39e7337c44b Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:44 +0100 Subject: staging: most: core: remove function get_channel_by_iface This patch removes the function get_channel_by_iface that walks a list of all registered interfaces and returns a pointer to a channel when matched. Instead the private field of the interface structure is used to directly access the channel via the id. The patch is needed to remove unnecessary list traversing. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.c | 47 +++++++++++++-------------------------------- 1 file changed, 13 insertions(+), 34 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index d4456abf18ed..7f7d6b65d76f 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -989,37 +989,10 @@ static void most_write_completion(struct mbo *mbo) arm_mbo(mbo); } -/** - * get_channel_by_iface - get pointer to channel object - * @iface: pointer to interface instance - * @id: channel ID - * - * This retrieves a pointer to a channel of the given interface and channel ID. - */ -static struct -most_c_obj *get_channel_by_iface(struct most_interface *iface, int id) -{ - struct most_inst_obj *i; - - if (unlikely(!iface)) { - pr_err("Bad interface\n"); - return NULL; - } - if (unlikely((id < 0) || (id >= iface->num_channels))) { - pr_err("Channel index (%d) out of range\n", id); - return NULL; - } - i = iface->priv; - if (unlikely(!i)) { - pr_err("interface is not registered\n"); - return NULL; - } - return i->channel[id]; -} - int channel_has_mbo(struct most_interface *iface, int id, struct most_aim *aim) { - struct most_c_obj *c = get_channel_by_iface(iface, id); + struct most_inst_obj *inst = iface->priv; + struct most_c_obj *c = inst->channel[id]; unsigned long flags; int empty; @@ -1051,10 +1024,11 @@ struct mbo *most_get_mbo(struct most_interface *iface, int id, { struct mbo *mbo; struct most_c_obj *c; + struct most_inst_obj *inst = iface->priv; unsigned long flags; int *num_buffers_ptr; - c = get_channel_by_iface(iface, id); + c = inst->channel[id]; if (unlikely(!c)) return NULL; @@ -1156,7 +1130,8 @@ int most_start_channel(struct most_interface *iface, int id, { int num_buffer; int ret; - struct most_c_obj *c = get_channel_by_iface(iface, id); + struct most_inst_obj *inst = iface->priv; + struct most_c_obj *c = inst->channel[id]; if (unlikely(!c)) return -EINVAL; @@ -1224,13 +1199,15 @@ EXPORT_SYMBOL_GPL(most_start_channel); int most_stop_channel(struct most_interface *iface, int id, struct most_aim *aim) { + struct most_inst_obj *inst; struct most_c_obj *c; if (unlikely((!iface) || (id >= iface->num_channels) || (id < 0))) { pr_err("Bad interface or index out of range\n"); return -EINVAL; } - c = get_channel_by_iface(iface, id); + inst = iface->priv; + c = inst->channel[id]; if (unlikely(!c)) return -EINVAL; @@ -1507,7 +1484,8 @@ EXPORT_SYMBOL_GPL(most_deregister_interface); */ void most_stop_enqueue(struct most_interface *iface, int id) { - struct most_c_obj *c = get_channel_by_iface(iface, id); + struct most_inst_obj *inst = iface->priv; + struct most_c_obj *c = inst->channel[id]; if (!c) return; @@ -1528,7 +1506,8 @@ EXPORT_SYMBOL_GPL(most_stop_enqueue); */ void most_resume_enqueue(struct most_interface *iface, int id) { - struct most_c_obj *c = get_channel_by_iface(iface, id); + struct most_inst_obj *inst = iface->priv; + struct most_c_obj *c = inst->channel[id]; if (!c) return; -- cgit From 921c80c52b7440a0f5c5c542d40a3295d525276b Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:45 +0100 Subject: staging: most: core: add a match function for the bus This patch adds the function most_match. It is needed to accociate registered devices and drivers with the bus. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index 7f7d6b65d76f..ce063a1597c3 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -756,11 +756,20 @@ static const struct attribute_group *aim_attr_groups[] = { * ___C O R E___ */ +int most_match(struct device *dev, struct device_driver *drv) +{ + if (!strcmp(dev_name(dev), "most")) + return 0; + else + return 1; +} + /** * Instantiation of the MOST bus */ static struct bus_type most_bus = { .name = "most", + .match = most_match, }; /** -- cgit From e6e79b449ed99925c59eff66ba836f2532fec911 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:46 +0100 Subject: staging: most: core: encapsulate code in function This patch adds the function link_channel_to_aim to increase readability of the function add_link_store. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.c | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index ce063a1597c3..0ef90851f891 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -620,6 +620,30 @@ most_c_obj *get_channel_by_name(char *mdev, char *mdev_ch) return c; } +static inline int link_channel_to_aim(struct most_c_obj *c, + struct most_aim *aim, char *aim_param) +{ + int ret; + struct most_aim **aim_ptr; + + if (!c->aim0.ptr) + aim_ptr = &c->aim0.ptr; + else if (!c->aim1.ptr) + aim_ptr = &c->aim1.ptr; + else + return -ENOSPC; + + *aim_ptr = aim; + ret = aim->probe_channel(c->iface, c->channel_id, + &c->cfg, aim_param); + if (ret) { + *aim_ptr = NULL; + return ret; + } + + return 0; +} + /** * add_link_store - store() function for add_link attribute * @aim_obj: pointer to AIM object @@ -648,7 +672,6 @@ static ssize_t add_link_store(struct device *dev, size_t len) { struct most_c_obj *c; - struct most_aim **aim_ptr; struct most_aim *aim = to_most_aim(dev); char buffer[STRING_SIZE]; char *mdev; @@ -674,19 +697,9 @@ static ssize_t add_link_store(struct device *dev, if (IS_ERR(c)) return -ENODEV; - if (!c->aim0.ptr) - aim_ptr = &c->aim0.ptr; - else if (!c->aim1.ptr) - aim_ptr = &c->aim1.ptr; - else - return -ENOSPC; - - *aim_ptr = aim; - ret = aim->probe_channel(c->iface, c->channel_id, &c->cfg, mdev_devnod); - if (ret) { - *aim_ptr = NULL; + ret = link_channel_to_aim(c, aim, mdev_devnod); + if (ret) return ret; - } return len; } -- cgit From fcb7fad82e23f62e2ffa2b732a1cf3c2d10b923e Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:47 +0100 Subject: staging: most: core: rename structure This patch renames the structure most_c_obj to most_channel. This is needed to enhance readability. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.c | 104 ++++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 52 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index 0ef90851f891..587e42f65a04 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -37,7 +37,7 @@ struct most_c_aim_obj { int num_buffers; }; -struct most_c_obj { +struct most_channel { struct device dev; struct completion cleanup; atomic_t mbo_ref; @@ -63,13 +63,13 @@ struct most_c_obj { wait_queue_head_t hdm_fifo_wq; }; -#define to_c_obj(d) container_of(d, struct most_c_obj, dev) +#define to_channel(d) container_of(d, struct most_channel, dev) struct most_inst_obj { int dev_id; struct most_interface *iface; struct list_head channel_list; - struct most_c_obj *channel[MAX_CHANNELS]; + struct most_channel *channel[MAX_CHANNELS]; struct list_head list; }; @@ -106,7 +106,7 @@ static const struct { */ static void most_free_mbo_coherent(struct mbo *mbo) { - struct most_c_obj *c = mbo->context; + struct most_channel *c = mbo->context; u16 const coherent_buf_size = c->cfg.buffer_size + c->cfg.extra_len; dma_free_coherent(NULL, coherent_buf_size, mbo->virt_address, @@ -120,7 +120,7 @@ static void most_free_mbo_coherent(struct mbo *mbo) * flush_channel_fifos - clear the channel fifos * @c: pointer to channel object */ -static void flush_channel_fifos(struct most_c_obj *c) +static void flush_channel_fifos(struct most_channel *c) { unsigned long flags, hf_flags; struct mbo *mbo, *tmp; @@ -154,7 +154,7 @@ static void flush_channel_fifos(struct most_c_obj *c) * flush_trash_fifo - clear the trash fifo * @c: pointer to channel object */ -static int flush_trash_fifo(struct most_c_obj *c) +static int flush_trash_fifo(struct most_channel *c) { struct mbo *mbo, *tmp; unsigned long flags; @@ -174,7 +174,7 @@ static ssize_t available_directions_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct most_c_obj *c = to_c_obj(dev); + struct most_channel *c = to_channel(dev); unsigned int i = c->channel_id; strcpy(buf, ""); @@ -190,7 +190,7 @@ static ssize_t available_datatypes_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct most_c_obj *c = to_c_obj(dev); + struct most_channel *c = to_channel(dev); unsigned int i = c->channel_id; strcpy(buf, ""); @@ -210,7 +210,7 @@ static ssize_t number_of_packet_buffers_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct most_c_obj *c = to_c_obj(dev); + struct most_channel *c = to_channel(dev); unsigned int i = c->channel_id; return snprintf(buf, PAGE_SIZE, "%d\n", @@ -221,7 +221,7 @@ static ssize_t number_of_stream_buffers_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct most_c_obj *c = to_c_obj(dev); + struct most_channel *c = to_channel(dev); unsigned int i = c->channel_id; return snprintf(buf, PAGE_SIZE, "%d\n", @@ -232,7 +232,7 @@ static ssize_t size_of_packet_buffer_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct most_c_obj *c = to_c_obj(dev); + struct most_channel *c = to_channel(dev); unsigned int i = c->channel_id; return snprintf(buf, PAGE_SIZE, "%d\n", @@ -243,7 +243,7 @@ static ssize_t size_of_stream_buffer_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct most_c_obj *c = to_c_obj(dev); + struct most_channel *c = to_channel(dev); unsigned int i = c->channel_id; return snprintf(buf, PAGE_SIZE, "%d\n", @@ -254,7 +254,7 @@ static ssize_t channel_starving_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct most_c_obj *c = to_c_obj(dev); + struct most_channel *c = to_channel(dev); return snprintf(buf, PAGE_SIZE, "%d\n", c->is_starving); } @@ -263,7 +263,7 @@ static ssize_t set_number_of_buffers_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct most_c_obj *c = to_c_obj(dev); + struct most_channel *c = to_channel(dev); return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.num_buffers); } @@ -273,7 +273,7 @@ static ssize_t set_number_of_buffers_store(struct device *dev, const char *buf, size_t count) { - struct most_c_obj *c = to_c_obj(dev); + struct most_channel *c = to_channel(dev); int ret = kstrtou16(buf, 0, &c->cfg.num_buffers); @@ -286,7 +286,7 @@ static ssize_t set_buffer_size_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct most_c_obj *c = to_c_obj(dev); + struct most_channel *c = to_channel(dev); return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.buffer_size); } @@ -296,7 +296,7 @@ static ssize_t set_buffer_size_store(struct device *dev, const char *buf, size_t count) { - struct most_c_obj *c = to_c_obj(dev); + struct most_channel *c = to_channel(dev); int ret = kstrtou16(buf, 0, &c->cfg.buffer_size); if (ret) @@ -308,7 +308,7 @@ static ssize_t set_direction_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct most_c_obj *c = to_c_obj(dev); + struct most_channel *c = to_channel(dev); if (c->cfg.direction & MOST_CH_TX) return snprintf(buf, PAGE_SIZE, "tx\n"); @@ -322,7 +322,7 @@ static ssize_t set_direction_store(struct device *dev, const char *buf, size_t count) { - struct most_c_obj *c = to_c_obj(dev); + struct most_channel *c = to_channel(dev); if (!strcmp(buf, "dir_rx\n")) { c->cfg.direction = MOST_CH_RX; @@ -344,7 +344,7 @@ static ssize_t set_datatype_show(struct device *dev, char *buf) { int i; - struct most_c_obj *c = to_c_obj(dev); + struct most_channel *c = to_channel(dev); for (i = 0; i < ARRAY_SIZE(ch_data_type); i++) { if (c->cfg.data_type & ch_data_type[i].most_ch_data_type) @@ -359,7 +359,7 @@ static ssize_t set_datatype_store(struct device *dev, size_t count) { int i; - struct most_c_obj *c = to_c_obj(dev); + struct most_channel *c = to_channel(dev); for (i = 0; i < ARRAY_SIZE(ch_data_type); i++) { if (!strcmp(buf, ch_data_type[i].name)) { @@ -379,7 +379,7 @@ static ssize_t set_subbuffer_size_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct most_c_obj *c = to_c_obj(dev); + struct most_channel *c = to_channel(dev); return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.subbuffer_size); } @@ -389,7 +389,7 @@ static ssize_t set_subbuffer_size_store(struct device *dev, const char *buf, size_t count) { - struct most_c_obj *c = to_c_obj(dev); + struct most_channel *c = to_channel(dev); int ret = kstrtou16(buf, 0, &c->cfg.subbuffer_size); if (ret) @@ -401,7 +401,7 @@ static ssize_t set_packets_per_xact_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct most_c_obj *c = to_c_obj(dev); + struct most_channel *c = to_channel(dev); return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.packets_per_xact); } @@ -411,7 +411,7 @@ static ssize_t set_packets_per_xact_store(struct device *dev, const char *buf, size_t count) { - struct most_c_obj *c = to_c_obj(dev); + struct most_channel *c = to_channel(dev); int ret = kstrtou16(buf, 0, &c->cfg.packets_per_xact); if (ret) @@ -529,7 +529,7 @@ static const struct attribute_group *interface_attr_groups[] = { static ssize_t links_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct most_c_obj *c; + struct most_channel *c; struct most_inst_obj *i; struct most_aim *aim = to_most_aim(dev); int offs = 0; @@ -593,10 +593,9 @@ static int split_string(char *buf, char **a, char **b, char **c) * * This retrieves the pointer to a channel object. */ -static struct -most_c_obj *get_channel_by_name(char *mdev, char *mdev_ch) +static struct most_channel *get_channel_by_name(char *mdev, char *mdev_ch) { - struct most_c_obj *c, *tmp; + struct most_channel *c, *tmp; struct most_inst_obj *i, *i_tmp; int found = 0; @@ -620,8 +619,9 @@ most_c_obj *get_channel_by_name(char *mdev, char *mdev_ch) return c; } -static inline int link_channel_to_aim(struct most_c_obj *c, - struct most_aim *aim, char *aim_param) +static +inline int link_channel_to_aim(struct most_channel *c, struct most_aim *aim, + char *aim_param) { int ret; struct most_aim **aim_ptr; @@ -671,7 +671,7 @@ static ssize_t add_link_store(struct device *dev, const char *buf, size_t len) { - struct most_c_obj *c; + struct most_channel *c; struct most_aim *aim = to_most_aim(dev); char buffer[STRING_SIZE]; char *mdev; @@ -719,7 +719,7 @@ static ssize_t remove_link_store(struct device *dev, const char *buf, size_t len) { - struct most_c_obj *c; + struct most_channel *c; struct most_aim *aim = to_most_aim(dev); char buffer[STRING_SIZE]; char *mdev; @@ -796,14 +796,14 @@ static struct device_driver mostcore = { static inline void trash_mbo(struct mbo *mbo) { unsigned long flags; - struct most_c_obj *c = mbo->context; + struct most_channel *c = mbo->context; spin_lock_irqsave(&c->fifo_lock, flags); list_add(&mbo->list, &c->trash_fifo); spin_unlock_irqrestore(&c->fifo_lock, flags); } -static bool hdm_mbo_ready(struct most_c_obj *c) +static bool hdm_mbo_ready(struct most_channel *c) { bool empty; @@ -820,7 +820,7 @@ static bool hdm_mbo_ready(struct most_c_obj *c) static void nq_hdm_mbo(struct mbo *mbo) { unsigned long flags; - struct most_c_obj *c = mbo->context; + struct most_channel *c = mbo->context; spin_lock_irqsave(&c->fifo_lock, flags); list_add_tail(&mbo->list, &c->halt_fifo); @@ -830,7 +830,7 @@ static void nq_hdm_mbo(struct mbo *mbo) static int hdm_enqueue_thread(void *data) { - struct most_c_obj *c = data; + struct most_channel *c = data; struct mbo *mbo; int ret; typeof(c->iface->enqueue) enqueue = c->iface->enqueue; @@ -868,7 +868,7 @@ static int hdm_enqueue_thread(void *data) return 0; } -static int run_enqueue_thread(struct most_c_obj *c, int channel_id) +static int run_enqueue_thread(struct most_channel *c, int channel_id) { struct task_struct *task = kthread_run(hdm_enqueue_thread, c, "hdm_fifo_%d", @@ -895,7 +895,7 @@ static int run_enqueue_thread(struct most_c_obj *c, int channel_id) static void arm_mbo(struct mbo *mbo) { unsigned long flags; - struct most_c_obj *c; + struct most_channel *c; BUG_ON((!mbo) || (!mbo->context)); c = mbo->context; @@ -930,7 +930,7 @@ static void arm_mbo(struct mbo *mbo) * * Returns the number of allocated and enqueued MBOs. */ -static int arm_mbo_chain(struct most_c_obj *c, int dir, +static int arm_mbo_chain(struct most_channel *c, int dir, void (*compl)(struct mbo *)) { unsigned int i; @@ -998,7 +998,7 @@ EXPORT_SYMBOL_GPL(most_submit_mbo); */ static void most_write_completion(struct mbo *mbo) { - struct most_c_obj *c; + struct most_channel *c; BUG_ON((!mbo) || (!mbo->context)); @@ -1014,7 +1014,7 @@ static void most_write_completion(struct mbo *mbo) int channel_has_mbo(struct most_interface *iface, int id, struct most_aim *aim) { struct most_inst_obj *inst = iface->priv; - struct most_c_obj *c = inst->channel[id]; + struct most_channel *c = inst->channel[id]; unsigned long flags; int empty; @@ -1045,7 +1045,7 @@ struct mbo *most_get_mbo(struct most_interface *iface, int id, struct most_aim *aim) { struct mbo *mbo; - struct most_c_obj *c; + struct most_channel *c; struct most_inst_obj *inst = iface->priv; unsigned long flags; int *num_buffers_ptr; @@ -1087,7 +1087,7 @@ EXPORT_SYMBOL_GPL(most_get_mbo); */ void most_put_mbo(struct mbo *mbo) { - struct most_c_obj *c = mbo->context; + struct most_channel *c = mbo->context; if (c->cfg.direction == MOST_CH_TX) { arm_mbo(mbo); @@ -1110,7 +1110,7 @@ EXPORT_SYMBOL_GPL(most_put_mbo); */ static void most_read_completion(struct mbo *mbo) { - struct most_c_obj *c = mbo->context; + struct most_channel *c = mbo->context; if (unlikely(c->is_poisoned || (mbo->status == MBO_E_CLOSE))) { trash_mbo(mbo); @@ -1153,7 +1153,7 @@ int most_start_channel(struct most_interface *iface, int id, int num_buffer; int ret; struct most_inst_obj *inst = iface->priv; - struct most_c_obj *c = inst->channel[id]; + struct most_channel *c = inst->channel[id]; if (unlikely(!c)) return -EINVAL; @@ -1222,7 +1222,7 @@ int most_stop_channel(struct most_interface *iface, int id, struct most_aim *aim) { struct most_inst_obj *inst; - struct most_c_obj *c; + struct most_channel *c; if (unlikely((!iface) || (id >= iface->num_channels) || (id < 0))) { pr_err("Bad interface or index out of range\n"); @@ -1314,7 +1314,7 @@ EXPORT_SYMBOL_GPL(most_register_aim); */ int most_deregister_aim(struct most_aim *aim) { - struct most_c_obj *c, *tmp; + struct most_channel *c, *tmp; struct most_inst_obj *i, *i_tmp; if (!aim) { @@ -1362,7 +1362,7 @@ int most_register_interface(struct most_interface *iface) int id; char name[STRING_SIZE]; char channel_name[STRING_SIZE]; - struct most_c_obj *c; + struct most_channel *c; struct most_inst_obj *inst; if (!iface || !iface->enqueue || !iface->configure || @@ -1468,7 +1468,7 @@ EXPORT_SYMBOL_GPL(most_register_interface); void most_deregister_interface(struct most_interface *iface) { int i; - struct most_c_obj *c; + struct most_channel *c; struct most_inst_obj *inst; pr_info("deregistering MOST device %s (%s)\n", dev_name(&iface->dev), iface->description); @@ -1507,7 +1507,7 @@ EXPORT_SYMBOL_GPL(most_deregister_interface); void most_stop_enqueue(struct most_interface *iface, int id) { struct most_inst_obj *inst = iface->priv; - struct most_c_obj *c = inst->channel[id]; + struct most_channel *c = inst->channel[id]; if (!c) return; @@ -1529,7 +1529,7 @@ EXPORT_SYMBOL_GPL(most_stop_enqueue); void most_resume_enqueue(struct most_interface *iface, int id) { struct most_inst_obj *inst = iface->priv; - struct most_c_obj *c = inst->channel[id]; + struct most_channel *c = inst->channel[id]; if (!c) return; -- cgit From 7faeffec7f4f1a28db47dda9614010b3d7dcc48a Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:48 +0100 Subject: staging: most: core: rename struct most_c_aim_obj to pipe This patch replaces the confusing name of struct "most_c_aim_obj" with "pipe" to better express the function that is behind the structure. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index 587e42f65a04..d6aee31f891f 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -31,7 +31,7 @@ static struct device core_dev; static struct ida mdev_id; static int dummy_num_buffers; -struct most_c_aim_obj { +struct pipe { struct most_aim *ptr; int refs; int num_buffers; @@ -56,8 +56,8 @@ struct most_channel { spinlock_t fifo_lock; struct list_head halt_fifo; struct list_head list; - struct most_c_aim_obj aim0; - struct most_c_aim_obj aim1; + struct pipe aim0; + struct pipe aim1; struct list_head trash_fifo; struct task_struct *hdm_enqueue_task; wait_queue_head_t hdm_fifo_wq; -- cgit From ab868bad3c3cd649c78540d21852378ba0b957ba Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:49 +0100 Subject: staging: most: core: rename struct memeber This patch renames the member "ptr" of struct pipe to "aim". This is needed to increase the readability of the code. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.c | 78 ++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 39 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index d6aee31f891f..404595a638b2 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -32,7 +32,7 @@ static struct ida mdev_id; static int dummy_num_buffers; struct pipe { - struct most_aim *ptr; + struct most_aim *aim; int refs; int num_buffers; }; @@ -536,7 +536,7 @@ static ssize_t links_show(struct device *dev, struct device_attribute *attr, list_for_each_entry(i, &instance_list, list) { list_for_each_entry(c, &i->channel_list, list) { - if (c->aim0.ptr == aim || c->aim1.ptr == aim) { + if (c->aim0.aim == aim || c->aim1.aim == aim) { offs += snprintf(buf + offs, PAGE_SIZE - offs, "%s:%s\n", dev_name(&i->iface->dev), @@ -626,10 +626,10 @@ inline int link_channel_to_aim(struct most_channel *c, struct most_aim *aim, int ret; struct most_aim **aim_ptr; - if (!c->aim0.ptr) - aim_ptr = &c->aim0.ptr; - else if (!c->aim1.ptr) - aim_ptr = &c->aim1.ptr; + if (!c->aim0.aim) + aim_ptr = &c->aim0.aim; + else if (!c->aim1.aim) + aim_ptr = &c->aim1.aim; else return -ENOSPC; @@ -738,10 +738,10 @@ static ssize_t remove_link_store(struct device *dev, if (aim->disconnect_channel(c->iface, c->channel_id)) return -EIO; - if (c->aim0.ptr == aim) - c->aim0.ptr = NULL; - if (c->aim1.ptr == aim) - c->aim1.ptr = NULL; + if (c->aim0.aim == aim) + c->aim0.aim = NULL; + if (c->aim1.aim == aim) + c->aim1.aim = NULL; return len; } @@ -910,11 +910,11 @@ static void arm_mbo(struct mbo *mbo) list_add_tail(&mbo->list, &c->fifo); spin_unlock_irqrestore(&c->fifo_lock, flags); - if (c->aim0.refs && c->aim0.ptr->tx_completion) - c->aim0.ptr->tx_completion(c->iface, c->channel_id); + if (c->aim0.refs && c->aim0.aim->tx_completion) + c->aim0.aim->tx_completion(c->iface, c->channel_id); - if (c->aim1.refs && c->aim1.ptr->tx_completion) - c->aim1.ptr->tx_completion(c->iface, c->channel_id); + if (c->aim1.refs && c->aim1.aim->tx_completion) + c->aim1.aim->tx_completion(c->iface, c->channel_id); } /** @@ -1022,8 +1022,8 @@ int channel_has_mbo(struct most_interface *iface, int id, struct most_aim *aim) return -EINVAL; if (c->aim0.refs && c->aim1.refs && - ((aim == c->aim0.ptr && c->aim0.num_buffers <= 0) || - (aim == c->aim1.ptr && c->aim1.num_buffers <= 0))) + ((aim == c->aim0.aim && c->aim0.num_buffers <= 0) || + (aim == c->aim1.aim && c->aim1.num_buffers <= 0))) return 0; spin_lock_irqsave(&c->fifo_lock, flags); @@ -1055,13 +1055,13 @@ struct mbo *most_get_mbo(struct most_interface *iface, int id, return NULL; if (c->aim0.refs && c->aim1.refs && - ((aim == c->aim0.ptr && c->aim0.num_buffers <= 0) || - (aim == c->aim1.ptr && c->aim1.num_buffers <= 0))) + ((aim == c->aim0.aim && c->aim0.num_buffers <= 0) || + (aim == c->aim1.aim && c->aim1.num_buffers <= 0))) return NULL; - if (aim == c->aim0.ptr) + if (aim == c->aim0.aim) num_buffers_ptr = &c->aim0.num_buffers; - else if (aim == c->aim1.ptr) + else if (aim == c->aim1.aim) num_buffers_ptr = &c->aim1.num_buffers; else num_buffers_ptr = &dummy_num_buffers; @@ -1126,12 +1126,12 @@ static void most_read_completion(struct mbo *mbo) if (atomic_sub_and_test(1, &c->mbo_nq_level)) c->is_starving = 1; - if (c->aim0.refs && c->aim0.ptr->rx_completion && - c->aim0.ptr->rx_completion(mbo) == 0) + if (c->aim0.refs && c->aim0.aim->rx_completion && + c->aim0.aim->rx_completion(mbo) == 0) return; - if (c->aim1.refs && c->aim1.ptr->rx_completion && - c->aim1.ptr->rx_completion(mbo) == 0) + if (c->aim1.refs && c->aim1.aim->rx_completion && + c->aim1.aim->rx_completion(mbo) == 0) return; most_put_mbo(mbo); @@ -1199,9 +1199,9 @@ int most_start_channel(struct most_interface *iface, int id, atomic_set(&c->mbo_ref, num_buffer); out: - if (aim == c->aim0.ptr) + if (aim == c->aim0.aim) c->aim0.refs++; - if (aim == c->aim1.ptr) + if (aim == c->aim1.aim) c->aim1.refs++; mutex_unlock(&c->start_mutex); return 0; @@ -1266,9 +1266,9 @@ int most_stop_channel(struct most_interface *iface, int id, c->is_poisoned = false; out: - if (aim == c->aim0.ptr) + if (aim == c->aim0.aim) c->aim0.refs--; - if (aim == c->aim1.ptr) + if (aim == c->aim1.aim) c->aim1.refs--; mutex_unlock(&c->start_mutex); return 0; @@ -1324,13 +1324,13 @@ int most_deregister_aim(struct most_aim *aim) list_for_each_entry_safe(i, i_tmp, &instance_list, list) { list_for_each_entry_safe(c, tmp, &i->channel_list, list) { - if (c->aim0.ptr == aim || c->aim1.ptr == aim) + if (c->aim0.aim == aim || c->aim1.aim == aim) aim->disconnect_channel( c->iface, c->channel_id); - if (c->aim0.ptr == aim) - c->aim0.ptr = NULL; - if (c->aim1.ptr == aim) - c->aim1.ptr = NULL; + if (c->aim0.aim == aim) + c->aim0.aim = NULL; + if (c->aim1.aim == aim) + c->aim1.aim = NULL; } } device_unregister(&aim->dev); @@ -1475,14 +1475,14 @@ void most_deregister_interface(struct most_interface *iface) inst = iface->priv; for (i = 0; i < iface->num_channels; i++) { c = inst->channel[i]; - if (c->aim0.ptr) - c->aim0.ptr->disconnect_channel(c->iface, + if (c->aim0.aim) + c->aim0.aim->disconnect_channel(c->iface, c->channel_id); - if (c->aim1.ptr) - c->aim1.ptr->disconnect_channel(c->iface, + if (c->aim1.aim) + c->aim1.aim->disconnect_channel(c->iface, c->channel_id); - c->aim0.ptr = NULL; - c->aim1.ptr = NULL; + c->aim0.aim = NULL; + c->aim1.aim = NULL; list_del(&c->list); device_unregister(&c->dev); kfree(c); -- cgit From f898f989550817b917a9ba812a79fdbac00cb177 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:50 +0100 Subject: staging: most: core: rename members aim* of struct most_channel This patch renames the struct members "aim0" and "aim0" to "pipe0" and "pipe1". It is needed to have a conclusive nomenclature of the struct and its instances. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.c | 104 ++++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 52 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index 404595a638b2..d7a28035f4a2 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -56,8 +56,8 @@ struct most_channel { spinlock_t fifo_lock; struct list_head halt_fifo; struct list_head list; - struct pipe aim0; - struct pipe aim1; + struct pipe pipe0; + struct pipe pipe1; struct list_head trash_fifo; struct task_struct *hdm_enqueue_task; wait_queue_head_t hdm_fifo_wq; @@ -536,7 +536,7 @@ static ssize_t links_show(struct device *dev, struct device_attribute *attr, list_for_each_entry(i, &instance_list, list) { list_for_each_entry(c, &i->channel_list, list) { - if (c->aim0.aim == aim || c->aim1.aim == aim) { + if (c->pipe0.aim == aim || c->pipe1.aim == aim) { offs += snprintf(buf + offs, PAGE_SIZE - offs, "%s:%s\n", dev_name(&i->iface->dev), @@ -626,10 +626,10 @@ inline int link_channel_to_aim(struct most_channel *c, struct most_aim *aim, int ret; struct most_aim **aim_ptr; - if (!c->aim0.aim) - aim_ptr = &c->aim0.aim; - else if (!c->aim1.aim) - aim_ptr = &c->aim1.aim; + if (!c->pipe0.aim) + aim_ptr = &c->pipe0.aim; + else if (!c->pipe1.aim) + aim_ptr = &c->pipe1.aim; else return -ENOSPC; @@ -738,10 +738,10 @@ static ssize_t remove_link_store(struct device *dev, if (aim->disconnect_channel(c->iface, c->channel_id)) return -EIO; - if (c->aim0.aim == aim) - c->aim0.aim = NULL; - if (c->aim1.aim == aim) - c->aim1.aim = NULL; + if (c->pipe0.aim == aim) + c->pipe0.aim = NULL; + if (c->pipe1.aim == aim) + c->pipe1.aim = NULL; return len; } @@ -910,11 +910,11 @@ static void arm_mbo(struct mbo *mbo) list_add_tail(&mbo->list, &c->fifo); spin_unlock_irqrestore(&c->fifo_lock, flags); - if (c->aim0.refs && c->aim0.aim->tx_completion) - c->aim0.aim->tx_completion(c->iface, c->channel_id); + if (c->pipe0.refs && c->pipe0.aim->tx_completion) + c->pipe0.aim->tx_completion(c->iface, c->channel_id); - if (c->aim1.refs && c->aim1.aim->tx_completion) - c->aim1.aim->tx_completion(c->iface, c->channel_id); + if (c->pipe1.refs && c->pipe1.aim->tx_completion) + c->pipe1.aim->tx_completion(c->iface, c->channel_id); } /** @@ -1021,9 +1021,9 @@ int channel_has_mbo(struct most_interface *iface, int id, struct most_aim *aim) if (unlikely(!c)) return -EINVAL; - if (c->aim0.refs && c->aim1.refs && - ((aim == c->aim0.aim && c->aim0.num_buffers <= 0) || - (aim == c->aim1.aim && c->aim1.num_buffers <= 0))) + if (c->pipe0.refs && c->pipe1.refs && + ((aim == c->pipe0.aim && c->pipe0.num_buffers <= 0) || + (aim == c->pipe1.aim && c->pipe1.num_buffers <= 0))) return 0; spin_lock_irqsave(&c->fifo_lock, flags); @@ -1054,15 +1054,15 @@ struct mbo *most_get_mbo(struct most_interface *iface, int id, if (unlikely(!c)) return NULL; - if (c->aim0.refs && c->aim1.refs && - ((aim == c->aim0.aim && c->aim0.num_buffers <= 0) || - (aim == c->aim1.aim && c->aim1.num_buffers <= 0))) + if (c->pipe0.refs && c->pipe1.refs && + ((aim == c->pipe0.aim && c->pipe0.num_buffers <= 0) || + (aim == c->pipe1.aim && c->pipe1.num_buffers <= 0))) return NULL; - if (aim == c->aim0.aim) - num_buffers_ptr = &c->aim0.num_buffers; - else if (aim == c->aim1.aim) - num_buffers_ptr = &c->aim1.num_buffers; + if (aim == c->pipe0.aim) + num_buffers_ptr = &c->pipe0.num_buffers; + else if (aim == c->pipe1.aim) + num_buffers_ptr = &c->pipe1.num_buffers; else num_buffers_ptr = &dummy_num_buffers; @@ -1126,12 +1126,12 @@ static void most_read_completion(struct mbo *mbo) if (atomic_sub_and_test(1, &c->mbo_nq_level)) c->is_starving = 1; - if (c->aim0.refs && c->aim0.aim->rx_completion && - c->aim0.aim->rx_completion(mbo) == 0) + if (c->pipe0.refs && c->pipe0.aim->rx_completion && + c->pipe0.aim->rx_completion(mbo) == 0) return; - if (c->aim1.refs && c->aim1.aim->rx_completion && - c->aim1.aim->rx_completion(mbo) == 0) + if (c->pipe1.refs && c->pipe1.aim->rx_completion && + c->pipe1.aim->rx_completion(mbo) == 0) return; most_put_mbo(mbo); @@ -1159,7 +1159,7 @@ int most_start_channel(struct most_interface *iface, int id, return -EINVAL; mutex_lock(&c->start_mutex); - if (c->aim0.refs + c->aim1.refs > 0) + if (c->pipe0.refs + c->pipe1.refs > 0) goto out; /* already started by other aim */ if (!try_module_get(iface->mod)) { @@ -1194,15 +1194,15 @@ int most_start_channel(struct most_interface *iface, int id, goto error; c->is_starving = 0; - c->aim0.num_buffers = c->cfg.num_buffers / 2; - c->aim1.num_buffers = c->cfg.num_buffers - c->aim0.num_buffers; + c->pipe0.num_buffers = c->cfg.num_buffers / 2; + c->pipe1.num_buffers = c->cfg.num_buffers - c->pipe0.num_buffers; atomic_set(&c->mbo_ref, num_buffer); out: - if (aim == c->aim0.aim) - c->aim0.refs++; - if (aim == c->aim1.aim) - c->aim1.refs++; + if (aim == c->pipe0.aim) + c->pipe0.refs++; + if (aim == c->pipe1.aim) + c->pipe1.refs++; mutex_unlock(&c->start_mutex); return 0; @@ -1234,7 +1234,7 @@ int most_stop_channel(struct most_interface *iface, int id, return -EINVAL; mutex_lock(&c->start_mutex); - if (c->aim0.refs + c->aim1.refs >= 2) + if (c->pipe0.refs + c->pipe1.refs >= 2) goto out; if (c->hdm_enqueue_task) @@ -1266,10 +1266,10 @@ int most_stop_channel(struct most_interface *iface, int id, c->is_poisoned = false; out: - if (aim == c->aim0.aim) - c->aim0.refs--; - if (aim == c->aim1.aim) - c->aim1.refs--; + if (aim == c->pipe0.aim) + c->pipe0.refs--; + if (aim == c->pipe1.aim) + c->pipe1.refs--; mutex_unlock(&c->start_mutex); return 0; } @@ -1324,13 +1324,13 @@ int most_deregister_aim(struct most_aim *aim) list_for_each_entry_safe(i, i_tmp, &instance_list, list) { list_for_each_entry_safe(c, tmp, &i->channel_list, list) { - if (c->aim0.aim == aim || c->aim1.aim == aim) + if (c->pipe0.aim == aim || c->pipe1.aim == aim) aim->disconnect_channel( c->iface, c->channel_id); - if (c->aim0.aim == aim) - c->aim0.aim = NULL; - if (c->aim1.aim == aim) - c->aim1.aim = NULL; + if (c->pipe0.aim == aim) + c->pipe0.aim = NULL; + if (c->pipe1.aim == aim) + c->pipe1.aim = NULL; } } device_unregister(&aim->dev); @@ -1475,14 +1475,14 @@ void most_deregister_interface(struct most_interface *iface) inst = iface->priv; for (i = 0; i < iface->num_channels; i++) { c = inst->channel[i]; - if (c->aim0.aim) - c->aim0.aim->disconnect_channel(c->iface, + if (c->pipe0.aim) + c->pipe0.aim->disconnect_channel(c->iface, c->channel_id); - if (c->aim1.aim) - c->aim1.aim->disconnect_channel(c->iface, + if (c->pipe1.aim) + c->pipe1.aim->disconnect_channel(c->iface, c->channel_id); - c->aim0.aim = NULL; - c->aim1.aim = NULL; + c->pipe0.aim = NULL; + c->pipe1.aim = NULL; list_del(&c->list); device_unregister(&c->dev); kfree(c); -- cgit From 14ae5f0383924e502139a2a6e8cbfadc985d3218 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:51 +0100 Subject: staging: most: core: use structure to pack driver specific data This patch introduces the structure "mostcore" to bundle core specific data structures. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.c | 73 +++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 39 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index d7a28035f4a2..779464a58674 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -26,11 +26,19 @@ #define MAX_CHANNELS 64 #define STRING_SIZE 80 -static struct class *most_class; -static struct device core_dev; static struct ida mdev_id; static int dummy_num_buffers; +static struct mostcore { + struct device dev; + struct device_driver drv; + struct bus_type bus; + struct class *class; + struct list_head mod_list; +} mc; + +#define to_driver(d) container_of(d, struct mostcore, drv); + struct pipe { struct most_aim *aim; int refs; @@ -777,22 +785,6 @@ int most_match(struct device *dev, struct device_driver *drv) return 1; } -/** - * Instantiation of the MOST bus - */ -static struct bus_type most_bus = { - .name = "most", - .match = most_match, -}; - -/** - * Instantiation of the core driver - */ -static struct device_driver mostcore = { - .name = "mostcore", - .bus = &most_bus, -}; - static inline void trash_mbo(struct mbo *mbo) { unsigned long flags; @@ -1293,8 +1285,8 @@ int most_register_aim(struct most_aim *aim) return -EINVAL; } aim->dev.init_name = aim->name; - aim->dev.bus = &most_bus; - aim->dev.parent = &core_dev; + aim->dev.bus = &mc.bus; + aim->dev.parent = &mc.dev; aim->dev.groups = aim_attr_groups; aim->dev.release = release_aim; ret = device_register(&aim->dev); @@ -1391,8 +1383,8 @@ int most_register_interface(struct most_interface *iface) list_add_tail(&inst->list, &instance_list); snprintf(name, STRING_SIZE, "mdev%d", id); iface->dev.init_name = name; - iface->dev.bus = &most_bus; - iface->dev.parent = &core_dev; + iface->dev.bus = &mc.bus; + iface->dev.parent = &mc.dev; iface->dev.groups = interface_attr_groups; iface->dev.release = release_interface; if (device_register(&iface->dev)) { @@ -1555,28 +1547,31 @@ static int __init most_init(void) INIT_LIST_HEAD(&instance_list); ida_init(&mdev_id); - err = bus_register(&most_bus); + mc.bus.name = "most", + mc.bus.match = most_match, + mc.drv.name = "most_core", + mc.drv.bus = &mc.bus, + + err = bus_register(&mc.bus); if (err) { pr_info("Cannot register most bus\n"); return err; } - - most_class = class_create(THIS_MODULE, "most"); - if (IS_ERR(most_class)) { + mc.class = class_create(THIS_MODULE, "most"); + if (IS_ERR(mc.class)) { pr_info("No udev support.\n"); - err = PTR_ERR(most_class); + err = PTR_ERR(mc.class); goto exit_bus; } - err = driver_register(&mostcore); + err = driver_register(&mc.drv); if (err) { pr_info("Cannot register core driver\n"); goto exit_class; } - - core_dev.init_name = "most_bus"; - core_dev.release = release_most_sub; - if (device_register(&core_dev)) { + mc.dev.init_name = "most_bus"; + mc.dev.release = release_most_sub; + if (device_register(&mc.dev)) { err = -ENOMEM; goto exit_driver; } @@ -1584,21 +1579,21 @@ static int __init most_init(void) return 0; exit_driver: - driver_unregister(&mostcore); + driver_unregister(&mc.drv); exit_class: - class_destroy(most_class); + class_destroy(mc.class); exit_bus: - bus_unregister(&most_bus); + bus_unregister(&mc.bus); return err; } static void __exit most_exit(void) { pr_info("exit core module\n"); - device_unregister(&core_dev); - driver_unregister(&mostcore); - class_destroy(most_class); - bus_unregister(&most_bus); + device_unregister(&mc.dev); + driver_unregister(&mc.drv); + class_destroy(mc.class); + bus_unregister(&mc.bus); ida_destroy(&mdev_id); } -- cgit From a6404e6e5a0e2933e10be3e42311ee6b65b821fc Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:52 +0100 Subject: staging: most: core: track aim modules with linked list The core needs to know what modules are registered. This patch makes the core keep track of the registered modules. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.c | 6 ++++-- drivers/staging/most/core.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index 779464a58674..41a35382a9e3 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -1294,8 +1294,8 @@ int most_register_aim(struct most_aim *aim) pr_err("registering device %s failed\n", aim->name); return ret; } - pr_info("registered new application interfacing module %s\n", - aim->name); + list_add_tail(&aim->list, &mc.mod_list); + pr_info("registered new application interfacing module %s\n", aim->name); return 0; } EXPORT_SYMBOL_GPL(most_register_aim); @@ -1326,6 +1326,7 @@ int most_deregister_aim(struct most_aim *aim) } } device_unregister(&aim->dev); + list_del(&aim->list); pr_info("deregistering application interfacing module %s\n", aim->name); return 0; } @@ -1545,6 +1546,7 @@ static int __init most_init(void) pr_info("init()\n"); INIT_LIST_HEAD(&instance_list); + INIT_LIST_HEAD(&mc.mod_list); ida_init(&mdev_id); mc.bus.name = "most", diff --git a/drivers/staging/most/core.h b/drivers/staging/most/core.h index 29595d9ef7ed..845e1d63d8fc 100644 --- a/drivers/staging/most/core.h +++ b/drivers/staging/most/core.h @@ -263,6 +263,7 @@ struct most_interface { */ struct most_aim { struct device dev; + struct list_head list; const char *name; int (*probe_channel)(struct most_interface *iface, int channel_idx, struct most_channel_config *cfg, char *name); -- cgit From bdafb7e83b0229379b11bfdd36ccfb3adf447666 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:53 +0100 Subject: staging: most: core: fix sysfs attribute management This patch creates a new attribute group to manage the attributes of a registered aim module in sysfs and changes the show and store functions accordingly. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.c | 109 +++++++++++++++++++++++++++++--------------- 1 file changed, 73 insertions(+), 36 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index 41a35382a9e3..37b9703504cf 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -534,28 +534,57 @@ static const struct attribute_group *interface_attr_groups[] = { /* ___ ___ * ___A I M___ */ -static ssize_t links_show(struct device *dev, struct device_attribute *attr, - char *buf) +static struct most_aim *match_module(char *name) +{ + struct most_aim *aim; + + list_for_each_entry(aim, &mc.mod_list, list) { + if (!strcmp(aim->name, name)) + return aim; + } + return NULL; +} + +static ssize_t links_show(struct device_driver *drv, char *buf) { struct most_channel *c; struct most_inst_obj *i; - struct most_aim *aim = to_most_aim(dev); int offs = 0; list_for_each_entry(i, &instance_list, list) { list_for_each_entry(c, &i->channel_list, list) { - if (c->pipe0.aim == aim || c->pipe1.aim == aim) { - offs += snprintf(buf + offs, PAGE_SIZE - offs, - "%s:%s\n", + if (c->pipe0.aim) { + offs += snprintf(buf + offs, + PAGE_SIZE - offs, + "%s:%s:%s\n", + c->pipe0.aim->name, + dev_name(&i->iface->dev), + dev_name(&c->dev)); + } + if (c->pipe1.aim) { + offs += snprintf(buf + offs, + PAGE_SIZE - offs, + "%s:%s:%s\n", + c->pipe1.aim->name, dev_name(&i->iface->dev), dev_name(&c->dev)); } } } - return offs; } +static ssize_t modules_show(struct device_driver *drv, char *buf) +{ + struct most_aim *aim; + int offs = 0; + + list_for_each_entry(aim, &mc.mod_list, list) { + offs += snprintf(buf + offs, PAGE_SIZE - offs, "%s\n", + aim->name); + } + return offs; +} /** * split_string - parses and changes string in the buffer buf and * splits it into two mandatory and one optional substrings. @@ -578,7 +607,7 @@ static ssize_t links_show(struct device *dev, struct device_attribute *attr, * Input: "mdev1:ep81" * Output: *a -> "mdev1", *b -> "ep81", *c == NULL */ -static int split_string(char *buf, char **a, char **b, char **c) +static int split_string(char *buf, char **a, char **b, char **c, char **d) { *a = strsep(&buf, ":"); if (!*a) @@ -588,8 +617,12 @@ static int split_string(char *buf, char **a, char **b, char **c) if (!*b) return -EIO; - if (c) - *c = strsep(&buf, ":\n"); + *c = strsep(&buf, ":\n"); + if (!*c) + return -EIO; + + if (d) + *d = strsep(&buf, ":\n"); return 0; } @@ -674,38 +707,38 @@ inline int link_channel_to_aim(struct most_channel *c, struct most_aim *aim, * (1) would create the device node /dev/my_rxchannel * (2) would create the device node /dev/mdev1-ep81 */ -static ssize_t add_link_store(struct device *dev, - struct device_attribute *attr, +static ssize_t add_link_store(struct device_driver *drv, const char *buf, size_t len) { struct most_channel *c; - struct most_aim *aim = to_most_aim(dev); + struct most_aim *aim; char buffer[STRING_SIZE]; char *mdev; char *mdev_ch; - char *mdev_devnod; + char *aim_name; + char *aim_param; char devnod_buf[STRING_SIZE]; int ret; size_t max_len = min_t(size_t, len + 1, STRING_SIZE); strlcpy(buffer, buf, max_len); - ret = split_string(buffer, &mdev, &mdev_ch, &mdev_devnod); + ret = split_string(buffer, &mdev, &mdev_ch, &aim_name, &aim_param); if (ret) return ret; - - if (!mdev_devnod || *mdev_devnod == 0) { + aim = match_module(aim_name); + if (!aim_param || *aim_param == 0) { snprintf(devnod_buf, sizeof(devnod_buf), "%s-%s", mdev, mdev_ch); - mdev_devnod = devnod_buf; + aim_param = devnod_buf; } c = get_channel_by_name(mdev, mdev_ch); if (IS_ERR(c)) return -ENODEV; - ret = link_channel_to_aim(c, aim, mdev_devnod); + ret = link_channel_to_aim(c, aim, aim_param); if (ret) return ret; @@ -722,24 +755,24 @@ static ssize_t add_link_store(struct device *dev, * Example: * echo "mdev0:ep81" >remove_link */ -static ssize_t remove_link_store(struct device *dev, - struct device_attribute *attr, +static ssize_t remove_link_store(struct device_driver *drv, const char *buf, size_t len) { struct most_channel *c; - struct most_aim *aim = to_most_aim(dev); + struct most_aim *aim; char buffer[STRING_SIZE]; char *mdev; char *mdev_ch; + char *aim_name; int ret; size_t max_len = min_t(size_t, len + 1, STRING_SIZE); strlcpy(buffer, buf, max_len); - ret = split_string(buffer, &mdev, &mdev_ch, NULL); + ret = split_string(buffer, &mdev, &mdev_ch, &aim_name, NULL); if (ret) return ret; - + aim = match_module(aim_name); c = get_channel_by_name(mdev, mdev_ch); if (IS_ERR(c)) return -ENODEV; @@ -753,23 +786,27 @@ static ssize_t remove_link_store(struct device *dev, return len; } -static DEVICE_ATTR_RO(links); -static DEVICE_ATTR_WO(add_link); -static DEVICE_ATTR_WO(remove_link); +#define DRV_ATTR(_name) (&driver_attr_##_name.attr) + +static DRIVER_ATTR_RO(links); +static DRIVER_ATTR_RO(modules); +static DRIVER_ATTR_WO(add_link); +static DRIVER_ATTR_WO(remove_link); -static struct attribute *aim_attrs[] = { - DEV_ATTR(links), - DEV_ATTR(add_link), - DEV_ATTR(remove_link), +static struct attribute *module_attrs[] = { + DRV_ATTR(links), + DRV_ATTR(modules), + DRV_ATTR(add_link), + DRV_ATTR(remove_link), NULL, }; -static struct attribute_group aim_attr_group = { - .attrs = aim_attrs, +static struct attribute_group module_attr_group = { + .attrs = module_attrs, }; -static const struct attribute_group *aim_attr_groups[] = { - &aim_attr_group, +static const struct attribute_group *module_attr_groups[] = { + &module_attr_group, NULL, }; @@ -1287,7 +1324,6 @@ int most_register_aim(struct most_aim *aim) aim->dev.init_name = aim->name; aim->dev.bus = &mc.bus; aim->dev.parent = &mc.dev; - aim->dev.groups = aim_attr_groups; aim->dev.release = release_aim; ret = device_register(&aim->dev); if (ret) { @@ -1553,6 +1589,7 @@ static int __init most_init(void) mc.bus.match = most_match, mc.drv.name = "most_core", mc.drv.bus = &mc.bus, + mc.drv.groups = module_attr_groups; err = bus_register(&mc.bus); if (err) { -- cgit From 3d6eb1d238ee981d2881c8478ed5bd3e948c1eb0 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:54 +0100 Subject: staging: most: core: remove struct device This patch takes out the struct device of struct most_aim, because it is not needed. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.c | 17 ----------------- drivers/staging/most/core.h | 1 - 2 files changed, 18 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index 37b9703504cf..076d4f2756ff 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -1304,32 +1304,16 @@ out: } EXPORT_SYMBOL_GPL(most_stop_channel); -void release_aim(struct device *dev) -{ - pr_info("releasing aim %s\n", dev_name(dev)); -} - /** * most_register_aim - registers an AIM (driver) with the core * @aim: instance of AIM to be registered */ int most_register_aim(struct most_aim *aim) { - int ret; - if (!aim) { pr_err("Bad driver\n"); return -EINVAL; } - aim->dev.init_name = aim->name; - aim->dev.bus = &mc.bus; - aim->dev.parent = &mc.dev; - aim->dev.release = release_aim; - ret = device_register(&aim->dev); - if (ret) { - pr_err("registering device %s failed\n", aim->name); - return ret; - } list_add_tail(&aim->list, &mc.mod_list); pr_info("registered new application interfacing module %s\n", aim->name); return 0; @@ -1361,7 +1345,6 @@ int most_deregister_aim(struct most_aim *aim) c->pipe1.aim = NULL; } } - device_unregister(&aim->dev); list_del(&aim->list); pr_info("deregistering application interfacing module %s\n", aim->name); return 0; diff --git a/drivers/staging/most/core.h b/drivers/staging/most/core.h index 845e1d63d8fc..2f61a69afb77 100644 --- a/drivers/staging/most/core.h +++ b/drivers/staging/most/core.h @@ -262,7 +262,6 @@ struct most_interface { * @context: context pointer to be used by mostcore */ struct most_aim { - struct device dev; struct list_head list; const char *name; int (*probe_channel)(struct most_interface *iface, int channel_idx, -- cgit From ec0c2f62ab8cebde7dbe8a3a4780dee280f1a98c Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:55 +0100 Subject: staging: most: core: rename function The core module used to have two functions to find a certain channel. One by name and one by interface. Since no channel is searched by its interface name anymore the by_name suffix is rendered redundant. This patch renames the function accordingly. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index 076d4f2756ff..9ab480914f95 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -628,13 +628,13 @@ static int split_string(char *buf, char **a, char **b, char **c, char **d) } /** - * get_channel_by_name - get pointer to channel object + * get_channel - get pointer to channel object * @mdev: name of the device instance * @mdev_ch: name of the respective channel * * This retrieves the pointer to a channel object. */ -static struct most_channel *get_channel_by_name(char *mdev, char *mdev_ch) +static struct most_channel *get_channel(char *mdev, char *mdev_ch) { struct most_channel *c, *tmp; struct most_inst_obj *i, *i_tmp; @@ -734,7 +734,7 @@ static ssize_t add_link_store(struct device_driver *drv, aim_param = devnod_buf; } - c = get_channel_by_name(mdev, mdev_ch); + c = get_channel(mdev, mdev_ch); if (IS_ERR(c)) return -ENODEV; @@ -773,7 +773,7 @@ static ssize_t remove_link_store(struct device_driver *drv, if (ret) return ret; aim = match_module(aim_name); - c = get_channel_by_name(mdev, mdev_ch); + c = get_channel(mdev, mdev_ch); if (IS_ERR(c)) return -ENODEV; -- cgit From 9136fccf38a7aaf173c42365664c2fce1bcc67a1 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:56 +0100 Subject: staging: most: core: replace struct most_inst_obj This patch introduces struct interface_private as a replacement for the struct most_inst_obj. This structure holds private data that is only needed by the core module and will be accessed by a pointer from within the most_interface structure. As a result of this replacement the bus helper functions can be used to search for devices. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.c | 200 +++++++++++++++++++++----------------------- drivers/staging/most/core.h | 2 + 2 files changed, 99 insertions(+), 103 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index 9ab480914f95..02772ce207f8 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -56,7 +56,6 @@ struct most_channel { struct mutex nq_mutex; /* nq thread synchronization */ int is_starving; struct most_interface *iface; - struct most_inst_obj *inst; struct most_channel_config cfg; bool keep_mbo; bool enqueue_halt; @@ -73,12 +72,11 @@ struct most_channel { #define to_channel(d) container_of(d, struct most_channel, dev) -struct most_inst_obj { +struct interface_private { int dev_id; - struct most_interface *iface; - struct list_head channel_list; + char name[STRING_SIZE]; struct most_channel *channel[MAX_CHANNELS]; - struct list_head list; + struct list_head channel_list; }; static const struct { @@ -472,9 +470,6 @@ static const struct attribute_group *channel_attr_groups[] = { /* ___ ___ * ___I N S T A N C E___ */ - -static struct list_head instance_list; - static ssize_t description_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -545,33 +540,38 @@ static struct most_aim *match_module(char *name) return NULL; } -static ssize_t links_show(struct device_driver *drv, char *buf) +int print_links(struct device *dev, void *data) { - struct most_channel *c; - struct most_inst_obj *i; int offs = 0; + char *buf = data; + struct most_channel *c; + struct most_interface *iface = to_most_interface(dev); - list_for_each_entry(i, &instance_list, list) { - list_for_each_entry(c, &i->channel_list, list) { - if (c->pipe0.aim) { - offs += snprintf(buf + offs, - PAGE_SIZE - offs, - "%s:%s:%s\n", - c->pipe0.aim->name, - dev_name(&i->iface->dev), - dev_name(&c->dev)); - } - if (c->pipe1.aim) { - offs += snprintf(buf + offs, - PAGE_SIZE - offs, - "%s:%s:%s\n", - c->pipe1.aim->name, - dev_name(&i->iface->dev), - dev_name(&c->dev)); - } + list_for_each_entry(c, &iface->p->channel_list, list) { + if (c->pipe0.aim) { + offs += snprintf(buf + offs, + PAGE_SIZE - offs, + "%s:%s:%s\n", + c->pipe0.aim->name, + dev_name(&iface->dev), + dev_name(&c->dev)); + } + if (c->pipe1.aim) { + offs += snprintf(buf + offs, + PAGE_SIZE - offs, + "%s:%s:%s\n", + c->pipe1.aim->name, + dev_name(&iface->dev), + dev_name(&c->dev)); } } - return offs; + return 0; +} + +static ssize_t links_show(struct device_driver *drv, char *buf) +{ + bus_for_each_dev(&mc.bus, NULL, buf, print_links); + return strlen(buf); } static ssize_t modules_show(struct device_driver *drv, char *buf) @@ -627,6 +627,13 @@ static int split_string(char *buf, char **a, char **b, char **c, char **d) return 0; } +static int match_bus_dev(struct device *dev, void *data) +{ + char *mdev_name = data; + + return !strcmp(dev_name(dev), mdev_name); +} + /** * get_channel - get pointer to channel object * @mdev: name of the device instance @@ -636,28 +643,19 @@ static int split_string(char *buf, char **a, char **b, char **c, char **d) */ static struct most_channel *get_channel(char *mdev, char *mdev_ch) { + struct device *dev = NULL; + struct most_interface *iface; struct most_channel *c, *tmp; - struct most_inst_obj *i, *i_tmp; - int found = 0; - list_for_each_entry_safe(i, i_tmp, &instance_list, list) { - if (!strcmp(dev_name(&i->iface->dev), mdev)) { - found++; - break; - } - } - if (unlikely(!found)) - return ERR_PTR(-EIO); - - list_for_each_entry_safe(c, tmp, &i->channel_list, list) { - if (!strcmp(dev_name(&c->dev), mdev_ch)) { - found++; - break; - } + dev = bus_find_device(&mc.bus, NULL, mdev, match_bus_dev); + if (!dev) + return NULL; + iface = to_most_interface(dev); + list_for_each_entry_safe(c, tmp, &iface->p->channel_list, list) { + if (!strcmp(dev_name(&c->dev), mdev_ch)) + return c; } - if (unlikely(found < 2)) - return ERR_PTR(-EIO); - return c; + return NULL; } static @@ -735,7 +733,7 @@ static ssize_t add_link_store(struct device_driver *drv, } c = get_channel(mdev, mdev_ch); - if (IS_ERR(c)) + if (!c) return -ENODEV; ret = link_channel_to_aim(c, aim, aim_param); @@ -774,7 +772,7 @@ static ssize_t remove_link_store(struct device_driver *drv, return ret; aim = match_module(aim_name); c = get_channel(mdev, mdev_ch); - if (IS_ERR(c)) + if (!c) return -ENODEV; if (aim->disconnect_channel(c->iface, c->channel_id)) @@ -1042,8 +1040,7 @@ static void most_write_completion(struct mbo *mbo) int channel_has_mbo(struct most_interface *iface, int id, struct most_aim *aim) { - struct most_inst_obj *inst = iface->priv; - struct most_channel *c = inst->channel[id]; + struct most_channel *c = iface->p->channel[id]; unsigned long flags; int empty; @@ -1075,11 +1072,10 @@ struct mbo *most_get_mbo(struct most_interface *iface, int id, { struct mbo *mbo; struct most_channel *c; - struct most_inst_obj *inst = iface->priv; unsigned long flags; int *num_buffers_ptr; - c = inst->channel[id]; + c = iface->p->channel[id]; if (unlikely(!c)) return NULL; @@ -1181,8 +1177,7 @@ int most_start_channel(struct most_interface *iface, int id, { int num_buffer; int ret; - struct most_inst_obj *inst = iface->priv; - struct most_channel *c = inst->channel[id]; + struct most_channel *c = iface->p->channel[id]; if (unlikely(!c)) return -EINVAL; @@ -1250,15 +1245,13 @@ EXPORT_SYMBOL_GPL(most_start_channel); int most_stop_channel(struct most_interface *iface, int id, struct most_aim *aim) { - struct most_inst_obj *inst; struct most_channel *c; if (unlikely((!iface) || (id >= iface->num_channels) || (id < 0))) { pr_err("Bad interface or index out of range\n"); return -EINVAL; } - inst = iface->priv; - c = inst->channel[id]; + c = iface->p->channel[id]; if (unlikely(!c)) return -EINVAL; @@ -1320,33 +1313,38 @@ int most_register_aim(struct most_aim *aim) } EXPORT_SYMBOL_GPL(most_register_aim); +static int disconnect_channels(struct device *dev, void *data) +{ + struct most_interface *iface; + struct most_channel *c, *tmp; + struct most_aim *aim = data; + + iface = to_most_interface(dev); + list_for_each_entry_safe(c, tmp, &iface->p->channel_list, list) { + if (c->pipe0.aim == aim || c->pipe1.aim == aim) + aim->disconnect_channel(c->iface, c->channel_id); + if (c->pipe0.aim == aim) + c->pipe0.aim = NULL; + if (c->pipe1.aim == aim) + c->pipe1.aim = NULL; + } + return 0; +} + /** * most_deregister_aim - deregisters an AIM (driver) with the core * @aim: AIM to be removed */ int most_deregister_aim(struct most_aim *aim) { - struct most_channel *c, *tmp; - struct most_inst_obj *i, *i_tmp; - if (!aim) { pr_err("Bad driver\n"); return -EINVAL; } - list_for_each_entry_safe(i, i_tmp, &instance_list, list) { - list_for_each_entry_safe(c, tmp, &i->channel_list, list) { - if (c->pipe0.aim == aim || c->pipe1.aim == aim) - aim->disconnect_channel( - c->iface, c->channel_id); - if (c->pipe0.aim == aim) - c->pipe0.aim = NULL; - if (c->pipe1.aim == aim) - c->pipe1.aim = NULL; - } - } + bus_for_each_dev(&mc.bus, NULL, aim, disconnect_channels); list_del(&aim->list); - pr_info("deregistering application interfacing module %s\n", aim->name); + pr_info("deregistering module %s\n", aim->name); return 0; } EXPORT_SYMBOL_GPL(most_deregister_aim); @@ -1372,10 +1370,8 @@ int most_register_interface(struct most_interface *iface) { unsigned int i; int id; - char name[STRING_SIZE]; char channel_name[STRING_SIZE]; struct most_channel *c; - struct most_inst_obj *inst; if (!iface || !iface->enqueue || !iface->configure || !iface->poison_channel || (iface->num_channels > MAX_CHANNELS)) { @@ -1389,27 +1385,24 @@ int most_register_interface(struct most_interface *iface) return id; } - inst = kzalloc(sizeof(*inst), GFP_KERNEL); - if (!inst) { + iface->p = kzalloc(sizeof(*iface->p), GFP_KERNEL); + if (!iface->p) { pr_info("Failed to allocate interface instance\n"); ida_simple_remove(&mdev_id, id); return -ENOMEM; } - iface->priv = inst; - INIT_LIST_HEAD(&inst->channel_list); - inst->iface = iface; - inst->dev_id = id; - list_add_tail(&inst->list, &instance_list); - snprintf(name, STRING_SIZE, "mdev%d", id); - iface->dev.init_name = name; + INIT_LIST_HEAD(&iface->p->channel_list); + iface->p->dev_id = id; + snprintf(iface->p->name, STRING_SIZE, "mdev%d", id); + iface->dev.init_name = iface->p->name; iface->dev.bus = &mc.bus; iface->dev.parent = &mc.dev; iface->dev.groups = interface_attr_groups; iface->dev.release = release_interface; if (device_register(&iface->dev)) { pr_err("registering iface->dev failed\n"); - kfree(inst); + kfree(iface->p); ida_simple_remove(&mdev_id, id); return -ENOMEM; } @@ -1422,7 +1415,6 @@ int most_register_interface(struct most_interface *iface) else snprintf(channel_name, STRING_SIZE, "%s", name_suffix); - /* this increments the reference count of this instance */ c = kzalloc(sizeof(*c), GFP_KERNEL); if (!c) goto free_instance; @@ -1432,12 +1424,11 @@ int most_register_interface(struct most_interface *iface) c->dev.release = release_channel; if (device_register(&c->dev)) { pr_err("registering c->dev failed\n"); - goto free_instance; + goto free_instance_nodev; } - inst->channel[i] = c; + iface->p->channel[i] = c; c->is_starving = 0; c->iface = iface; - c->inst = inst; c->channel_id = i; c->keep_mbo = false; c->enqueue_halt = false; @@ -1456,14 +1447,22 @@ int most_register_interface(struct most_interface *iface) atomic_set(&c->mbo_ref, 0); mutex_init(&c->start_mutex); mutex_init(&c->nq_mutex); - list_add_tail(&c->list, &inst->channel_list); + list_add_tail(&c->list, &iface->p->channel_list); } pr_info("registered new MOST device mdev%d (%s)\n", id, iface->description); return 0; +free_instance_nodev: + kfree(c); + free_instance: - pr_info("Failed allocate channel(s)\n"); + while (i > 0) { + c = iface->p->channel[--i]; + device_unregister(&c->dev); + kfree(c); + } + kfree(iface->p); device_unregister(&iface->dev); ida_simple_remove(&mdev_id, id); return -ENOMEM; @@ -1481,12 +1480,10 @@ void most_deregister_interface(struct most_interface *iface) { int i; struct most_channel *c; - struct most_inst_obj *inst; pr_info("deregistering MOST device %s (%s)\n", dev_name(&iface->dev), iface->description); - inst = iface->priv; for (i = 0; i < iface->num_channels; i++) { - c = inst->channel[i]; + c = iface->p->channel[i]; if (c->pipe0.aim) c->pipe0.aim->disconnect_channel(c->iface, c->channel_id); @@ -1500,8 +1497,8 @@ void most_deregister_interface(struct most_interface *iface) kfree(c); } - ida_simple_remove(&mdev_id, inst->dev_id); - kfree(inst); + ida_simple_remove(&mdev_id, iface->p->dev_id); + kfree(iface->p); device_unregister(&iface->dev); } EXPORT_SYMBOL_GPL(most_deregister_interface); @@ -1518,8 +1515,7 @@ EXPORT_SYMBOL_GPL(most_deregister_interface); */ void most_stop_enqueue(struct most_interface *iface, int id) { - struct most_inst_obj *inst = iface->priv; - struct most_channel *c = inst->channel[id]; + struct most_channel *c = iface->p->channel[id]; if (!c) return; @@ -1540,8 +1536,7 @@ EXPORT_SYMBOL_GPL(most_stop_enqueue); */ void most_resume_enqueue(struct most_interface *iface, int id) { - struct most_inst_obj *inst = iface->priv; - struct most_channel *c = inst->channel[id]; + struct most_channel *c = iface->p->channel[id]; if (!c) return; @@ -1564,7 +1559,6 @@ static int __init most_init(void) int err; pr_info("init()\n"); - INIT_LIST_HEAD(&instance_list); INIT_LIST_HEAD(&mc.mod_list); ida_init(&mdev_id); diff --git a/drivers/staging/most/core.h b/drivers/staging/most/core.h index 2f61a69afb77..764f013d0db4 100644 --- a/drivers/staging/most/core.h +++ b/drivers/staging/most/core.h @@ -20,6 +20,7 @@ #include struct module; +struct interface_private; /** * Interface type @@ -248,6 +249,7 @@ struct most_interface { unsigned char link_stat, unsigned char *mac_addr)); void *priv; + struct interface_private *p; }; #define to_most_interface(d) container_of(d, struct most_interface, dev) -- cgit From 845101bed867868f3c2e475341b9cd7cb1a86ab3 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:57 +0100 Subject: staging: most: core: put channel name in struct most_channel This patch stores a channel's name inside the most_channel structure. It is needed to have the channel attributes tied together. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index 02772ce207f8..2b0c85c60c20 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -51,6 +51,7 @@ struct most_channel { atomic_t mbo_ref; atomic_t mbo_nq_level; u16 channel_id; + char name[STRING_SIZE]; bool is_poisoned; struct mutex start_mutex; struct mutex nq_mutex; /* nq thread synchronization */ @@ -1370,7 +1371,6 @@ int most_register_interface(struct most_interface *iface) { unsigned int i; int id; - char channel_name[STRING_SIZE]; struct most_channel *c; if (!iface || !iface->enqueue || !iface->configure || @@ -1410,15 +1410,14 @@ int most_register_interface(struct most_interface *iface) for (i = 0; i < iface->num_channels; i++) { const char *name_suffix = iface->channel_vector[i].name_suffix; - if (!name_suffix) - snprintf(channel_name, STRING_SIZE, "ch%d", i); - else - snprintf(channel_name, STRING_SIZE, "%s", name_suffix); - c = kzalloc(sizeof(*c), GFP_KERNEL); if (!c) goto free_instance; - c->dev.init_name = channel_name; + if (!name_suffix) + snprintf(c->name, STRING_SIZE, "ch%d", i); + else + snprintf(c->name, STRING_SIZE, "%s", name_suffix); + c->dev.init_name = c->name; c->dev.parent = &iface->dev; c->dev.groups = channel_attr_groups; c->dev.release = release_channel; -- cgit From 9abfc8bb795b8d3c1323288f978c3718de843d64 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:58 +0100 Subject: staging: most: core: remove context pointer This patch removes the unused context pointer that was meant to provide the opportunity to store context information. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.h | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.h b/drivers/staging/most/core.h index 764f013d0db4..718dab8b4b18 100644 --- a/drivers/staging/most/core.h +++ b/drivers/staging/most/core.h @@ -272,7 +272,6 @@ struct most_aim { int channel_idx); int (*rx_completion)(struct mbo *mbo); int (*tx_completion)(struct most_interface *iface, int channel_idx); - void *context; }; #define to_most_aim(d) container_of(d, struct most_aim, dev) -- cgit From 66b468865ad83dc1bc48c6a74c84ddc218046ef4 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:04:59 +0100 Subject: staging: most: usb: remove pointer initialization This patch removes the initialization of the priv pointer of the most_interface structure. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/usb/usb.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/usb/usb.c b/drivers/staging/most/usb/usb.c index 7da7dd5e718d..87e7fec2af8a 100644 --- a/drivers/staging/most/usb/usb.c +++ b/drivers/staging/most/usb/usb.c @@ -1036,8 +1036,6 @@ hdm_probe(struct usb_interface *interface, const struct usb_device_id *id) goto exit_free1; mdev->iface.channel_vector = mdev->cap; - mdev->iface.priv = NULL; - mdev->ep_address = kcalloc(num_endpoints, sizeof(*mdev->ep_address), GFP_KERNEL); if (!mdev->ep_address) -- cgit From a12844410c4350305b9ae1c1e0c5d6bd87297e6b Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:05:00 +0100 Subject: staging: most: rename struct most_aim The designator of a module that proivdes means to interface userspace is called an AIM. Since this name seems to be unappropiate, this kind of moduels are going to be referred to as componetns. This is done because such modules function as components to enhance the core with new features. This patch renames the struct most_aim to core_component. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/cdev/cdev.c | 4 ++-- drivers/staging/most/core.c | 30 +++++++++++++++--------------- drivers/staging/most/core.h | 16 +++++++--------- drivers/staging/most/net/net.c | 4 ++-- drivers/staging/most/sound/sound.c | 6 +++--- drivers/staging/most/video/video.c | 4 ++-- 6 files changed, 31 insertions(+), 33 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/cdev/cdev.c b/drivers/staging/most/cdev/cdev.c index cd23db574d5f..a45a4dcbeb9b 100644 --- a/drivers/staging/most/cdev/cdev.c +++ b/drivers/staging/most/cdev/cdev.c @@ -22,7 +22,7 @@ static dev_t aim_devno; static struct class *aim_class; static struct ida minor_id; static unsigned int major; -static struct most_aim cdev_aim; +static struct core_component cdev_aim; struct aim_channel { wait_queue_head_t wq; @@ -489,7 +489,7 @@ error_alloc_channel: return retval; } -static struct most_aim cdev_aim = { +static struct core_component cdev_aim = { .name = "cdev", .probe_channel = aim_probe, .disconnect_channel = aim_disconnect_channel, diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index 2b0c85c60c20..520b851c5bf8 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -40,7 +40,7 @@ static struct mostcore { #define to_driver(d) container_of(d, struct mostcore, drv); struct pipe { - struct most_aim *aim; + struct core_component *aim; int refs; int num_buffers; }; @@ -530,9 +530,9 @@ static const struct attribute_group *interface_attr_groups[] = { /* ___ ___ * ___A I M___ */ -static struct most_aim *match_module(char *name) +static struct core_component *match_module(char *name) { - struct most_aim *aim; + struct core_component *aim; list_for_each_entry(aim, &mc.mod_list, list) { if (!strcmp(aim->name, name)) @@ -577,7 +577,7 @@ static ssize_t links_show(struct device_driver *drv, char *buf) static ssize_t modules_show(struct device_driver *drv, char *buf) { - struct most_aim *aim; + struct core_component *aim; int offs = 0; list_for_each_entry(aim, &mc.mod_list, list) { @@ -660,11 +660,11 @@ static struct most_channel *get_channel(char *mdev, char *mdev_ch) } static -inline int link_channel_to_aim(struct most_channel *c, struct most_aim *aim, +inline int link_channel_to_aim(struct most_channel *c, struct core_component *aim, char *aim_param) { int ret; - struct most_aim **aim_ptr; + struct core_component **aim_ptr; if (!c->pipe0.aim) aim_ptr = &c->pipe0.aim; @@ -711,7 +711,7 @@ static ssize_t add_link_store(struct device_driver *drv, size_t len) { struct most_channel *c; - struct most_aim *aim; + struct core_component *aim; char buffer[STRING_SIZE]; char *mdev; char *mdev_ch; @@ -759,7 +759,7 @@ static ssize_t remove_link_store(struct device_driver *drv, size_t len) { struct most_channel *c; - struct most_aim *aim; + struct core_component *aim; char buffer[STRING_SIZE]; char *mdev; char *mdev_ch; @@ -1039,7 +1039,7 @@ static void most_write_completion(struct mbo *mbo) arm_mbo(mbo); } -int channel_has_mbo(struct most_interface *iface, int id, struct most_aim *aim) +int channel_has_mbo(struct most_interface *iface, int id, struct core_component *aim) { struct most_channel *c = iface->p->channel[id]; unsigned long flags; @@ -1069,7 +1069,7 @@ EXPORT_SYMBOL_GPL(channel_has_mbo); * Returns a pointer to MBO on success or NULL otherwise. */ struct mbo *most_get_mbo(struct most_interface *iface, int id, - struct most_aim *aim) + struct core_component *aim) { struct mbo *mbo; struct most_channel *c; @@ -1174,7 +1174,7 @@ static void most_read_completion(struct mbo *mbo) * Returns 0 on success or error code otherwise. */ int most_start_channel(struct most_interface *iface, int id, - struct most_aim *aim) + struct core_component *aim) { int num_buffer; int ret; @@ -1244,7 +1244,7 @@ EXPORT_SYMBOL_GPL(most_start_channel); * @id: channel ID */ int most_stop_channel(struct most_interface *iface, int id, - struct most_aim *aim) + struct core_component *aim) { struct most_channel *c; @@ -1302,7 +1302,7 @@ EXPORT_SYMBOL_GPL(most_stop_channel); * most_register_aim - registers an AIM (driver) with the core * @aim: instance of AIM to be registered */ -int most_register_aim(struct most_aim *aim) +int most_register_aim(struct core_component *aim) { if (!aim) { pr_err("Bad driver\n"); @@ -1318,7 +1318,7 @@ static int disconnect_channels(struct device *dev, void *data) { struct most_interface *iface; struct most_channel *c, *tmp; - struct most_aim *aim = data; + struct core_component *aim = data; iface = to_most_interface(dev); list_for_each_entry_safe(c, tmp, &iface->p->channel_list, list) { @@ -1336,7 +1336,7 @@ static int disconnect_channels(struct device *dev, void *data) * most_deregister_aim - deregisters an AIM (driver) with the core * @aim: AIM to be removed */ -int most_deregister_aim(struct most_aim *aim) +int most_deregister_aim(struct core_component *aim) { if (!aim) { pr_err("Bad driver\n"); diff --git a/drivers/staging/most/core.h b/drivers/staging/most/core.h index 718dab8b4b18..4b6e47e78ccf 100644 --- a/drivers/staging/most/core.h +++ b/drivers/staging/most/core.h @@ -263,7 +263,7 @@ struct most_interface { * @tx_completion: completion handler for transmitted packets * @context: context pointer to be used by mostcore */ -struct most_aim { +struct core_component { struct list_head list; const char *name; int (*probe_channel)(struct most_interface *iface, int channel_idx, @@ -274,8 +274,6 @@ struct most_aim { int (*tx_completion)(struct most_interface *iface, int channel_idx); }; -#define to_most_aim(d) container_of(d, struct most_aim, dev) - /** * most_register_interface - Registers instance of the interface. * @iface: Pointer to the interface instance description. @@ -310,16 +308,16 @@ void most_stop_enqueue(struct most_interface *iface, int channel_idx); * in wait fifo. */ void most_resume_enqueue(struct most_interface *iface, int channel_idx); -int most_register_aim(struct most_aim *aim); -int most_deregister_aim(struct most_aim *aim); +int most_register_aim(struct core_component *comp); +int most_deregister_aim(struct core_component *comp); struct mbo *most_get_mbo(struct most_interface *iface, int channel_idx, - struct most_aim *); + struct core_component *comp); void most_put_mbo(struct mbo *mbo); int channel_has_mbo(struct most_interface *iface, int channel_idx, - struct most_aim *aim); + struct core_component *comp); int most_start_channel(struct most_interface *iface, int channel_idx, - struct most_aim *); + struct core_component *comp); int most_stop_channel(struct most_interface *iface, int channel_idx, - struct most_aim *); + struct core_component *comp); #endif /* MOST_CORE_H_ */ diff --git a/drivers/staging/most/net/net.c b/drivers/staging/most/net/net.c index a9323e4afad7..edc12ad4de61 100644 --- a/drivers/staging/most/net/net.c +++ b/drivers/staging/most/net/net.c @@ -68,7 +68,7 @@ struct net_dev_context { static struct list_head net_devices = LIST_HEAD_INIT(net_devices); static struct mutex probe_disc_mt; /* ch->linked = true, most_nd_open */ static struct spinlock list_lock; /* list_head, ch->linked = false, dev_hold */ -static struct most_aim aim; +static struct core_component aim; static int skb_to_mamac(const struct sk_buff *skb, struct mbo *mbo) { @@ -494,7 +494,7 @@ put_nd: return ret; } -static struct most_aim aim = { +static struct core_component aim = { .name = "net", .probe_channel = aim_probe_channel, .disconnect_channel = aim_disconnect_channel, diff --git a/drivers/staging/most/sound/sound.c b/drivers/staging/most/sound/sound.c index 5504f93aca39..e605cbe57511 100644 --- a/drivers/staging/most/sound/sound.c +++ b/drivers/staging/most/sound/sound.c @@ -21,7 +21,7 @@ #define DRIVER_NAME "sound" static struct list_head dev_list; -static struct most_aim audio_aim; +static struct core_component audio_aim; /** * struct channel - private structure to keep channel specific data @@ -717,9 +717,9 @@ static int audio_tx_completion(struct most_interface *iface, int channel_id) } /** - * Initialization of the struct most_aim + * Initialization of the struct core_component */ -static struct most_aim audio_aim = { +static struct core_component audio_aim = { .name = DRIVER_NAME, .probe_channel = audio_probe_channel, .disconnect_channel = audio_disconnect_channel, diff --git a/drivers/staging/most/video/video.c b/drivers/staging/most/video/video.c index 25ae4887b04d..2b8b3aed1ec2 100644 --- a/drivers/staging/most/video/video.c +++ b/drivers/staging/most/video/video.c @@ -25,7 +25,7 @@ #define V4L2_AIM_MAX_INPUT 1 -static struct most_aim aim_info; +static struct core_component aim_info; struct most_video_dev { struct most_interface *iface; @@ -562,7 +562,7 @@ static int aim_disconnect_channel(struct most_interface *iface, return 0; } -static struct most_aim aim_info = { +static struct core_component aim_info = { .name = "v4l", .probe_channel = aim_probe_channel, .disconnect_channel = aim_disconnect_channel, -- cgit From ed021a0f8e5b1ac2966a997e908c6a7824da6baa Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:05:01 +0100 Subject: staging: most: rename functions to register a driver with most_core This patch renames the functions to register and deregister a component module with the core. It is needed because the modules that interface the userspace are referred to as components. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/cdev/cdev.c | 4 ++-- drivers/staging/most/core.c | 13 +++++++------ drivers/staging/most/core.h | 4 ++-- drivers/staging/most/net/net.c | 4 ++-- drivers/staging/most/sound/sound.c | 4 ++-- drivers/staging/most/video/video.c | 6 +++--- 6 files changed, 18 insertions(+), 17 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/cdev/cdev.c b/drivers/staging/most/cdev/cdev.c index a45a4dcbeb9b..2447fbfd201e 100644 --- a/drivers/staging/most/cdev/cdev.c +++ b/drivers/staging/most/cdev/cdev.c @@ -518,7 +518,7 @@ static int __init mod_init(void) err = PTR_ERR(aim_class); goto free_cdev; } - err = most_register_aim(&cdev_aim); + err = most_register_component(&cdev_aim); if (err) goto dest_class; return 0; @@ -538,7 +538,7 @@ static void __exit mod_exit(void) pr_info("exit module\n"); - most_deregister_aim(&cdev_aim); + most_deregister_component(&cdev_aim); list_for_each_entry_safe(c, tmp, &channel_list, list) { destroy_cdev(c); diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index 520b851c5bf8..dd6cebf88e59 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -69,6 +69,7 @@ struct most_channel { struct list_head trash_fifo; struct task_struct *hdm_enqueue_task; wait_queue_head_t hdm_fifo_wq; + }; #define to_channel(d) container_of(d, struct most_channel, dev) @@ -1299,10 +1300,10 @@ out: EXPORT_SYMBOL_GPL(most_stop_channel); /** - * most_register_aim - registers an AIM (driver) with the core + * most_register_component - registers an AIM (driver) with the core * @aim: instance of AIM to be registered */ -int most_register_aim(struct core_component *aim) +int most_register_component(struct core_component *aim) { if (!aim) { pr_err("Bad driver\n"); @@ -1312,7 +1313,7 @@ int most_register_aim(struct core_component *aim) pr_info("registered new application interfacing module %s\n", aim->name); return 0; } -EXPORT_SYMBOL_GPL(most_register_aim); +EXPORT_SYMBOL_GPL(most_register_component); static int disconnect_channels(struct device *dev, void *data) { @@ -1333,10 +1334,10 @@ static int disconnect_channels(struct device *dev, void *data) } /** - * most_deregister_aim - deregisters an AIM (driver) with the core + * most_deregister_component - deregisters an AIM (driver) with the core * @aim: AIM to be removed */ -int most_deregister_aim(struct core_component *aim) +int most_deregister_component(struct core_component *aim) { if (!aim) { pr_err("Bad driver\n"); @@ -1348,7 +1349,7 @@ int most_deregister_aim(struct core_component *aim) pr_info("deregistering module %s\n", aim->name); return 0; } -EXPORT_SYMBOL_GPL(most_deregister_aim); +EXPORT_SYMBOL_GPL(most_deregister_component); static void release_interface(struct device *dev) { diff --git a/drivers/staging/most/core.h b/drivers/staging/most/core.h index 4b6e47e78ccf..5a3a5c5c3347 100644 --- a/drivers/staging/most/core.h +++ b/drivers/staging/most/core.h @@ -308,8 +308,8 @@ void most_stop_enqueue(struct most_interface *iface, int channel_idx); * in wait fifo. */ void most_resume_enqueue(struct most_interface *iface, int channel_idx); -int most_register_aim(struct core_component *comp); -int most_deregister_aim(struct core_component *comp); +int most_register_component(struct core_component *comp); +int most_deregister_component(struct core_component *comp); struct mbo *most_get_mbo(struct most_interface *iface, int channel_idx, struct core_component *comp); void most_put_mbo(struct mbo *mbo); diff --git a/drivers/staging/most/net/net.c b/drivers/staging/most/net/net.c index edc12ad4de61..cbe0dda011b4 100644 --- a/drivers/staging/most/net/net.c +++ b/drivers/staging/most/net/net.c @@ -506,12 +506,12 @@ static int __init most_net_init(void) { spin_lock_init(&list_lock); mutex_init(&probe_disc_mt); - return most_register_aim(&aim); + return most_register_component(&aim); } static void __exit most_net_exit(void) { - most_deregister_aim(&aim); + most_deregister_component(&aim); } /** diff --git a/drivers/staging/most/sound/sound.c b/drivers/staging/most/sound/sound.c index e605cbe57511..a3a46a269f43 100644 --- a/drivers/staging/most/sound/sound.c +++ b/drivers/staging/most/sound/sound.c @@ -733,7 +733,7 @@ static int __init audio_init(void) INIT_LIST_HEAD(&dev_list); - return most_register_aim(&audio_aim); + return most_register_component(&audio_aim); } static void __exit audio_exit(void) @@ -747,7 +747,7 @@ static void __exit audio_exit(void) snd_card_free(channel->card); } - most_deregister_aim(&audio_aim); + most_deregister_component(&audio_aim); } module_init(audio_init); diff --git a/drivers/staging/most/video/video.c b/drivers/staging/most/video/video.c index 2b8b3aed1ec2..3a97a2443c0b 100644 --- a/drivers/staging/most/video/video.c +++ b/drivers/staging/most/video/video.c @@ -572,7 +572,7 @@ static struct core_component aim_info = { static int __init aim_init(void) { spin_lock_init(&list_lock); - return most_register_aim(&aim_info); + return most_register_component(&aim_info); } static void __exit aim_exit(void) @@ -581,7 +581,7 @@ static void __exit aim_exit(void) /* * As the mostcore currently doesn't call disconnect_channel() - * for linked channels while we call most_deregister_aim() + * for linked channels while we call most_deregister_component() * we simulate this call here. * This must be fixed in core. */ @@ -597,7 +597,7 @@ static void __exit aim_exit(void) } spin_unlock_irq(&list_lock); - most_deregister_aim(&aim_info); + most_deregister_component(&aim_info); BUG_ON(!list_empty(&video_devices)); } -- cgit From 81ce26b7c4553496b10083b975f1560059c8d747 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:05:02 +0100 Subject: staging: most: core: rename mod_list This patch renames the variable mod_list to comp_list. It is needed because modules that interface userspace are referred to as components. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index dd6cebf88e59..c5b809b50247 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -34,7 +34,7 @@ static struct mostcore { struct device_driver drv; struct bus_type bus; struct class *class; - struct list_head mod_list; + struct list_head comp_list; } mc; #define to_driver(d) container_of(d, struct mostcore, drv); @@ -535,7 +535,7 @@ static struct core_component *match_module(char *name) { struct core_component *aim; - list_for_each_entry(aim, &mc.mod_list, list) { + list_for_each_entry(aim, &mc.comp_list, list) { if (!strcmp(aim->name, name)) return aim; } @@ -581,7 +581,7 @@ static ssize_t modules_show(struct device_driver *drv, char *buf) struct core_component *aim; int offs = 0; - list_for_each_entry(aim, &mc.mod_list, list) { + list_for_each_entry(aim, &mc.comp_list, list) { offs += snprintf(buf + offs, PAGE_SIZE - offs, "%s\n", aim->name); } @@ -1309,7 +1309,7 @@ int most_register_component(struct core_component *aim) pr_err("Bad driver\n"); return -EINVAL; } - list_add_tail(&aim->list, &mc.mod_list); + list_add_tail(&aim->list, &mc.comp_list); pr_info("registered new application interfacing module %s\n", aim->name); return 0; } @@ -1559,7 +1559,7 @@ static int __init most_init(void) int err; pr_info("init()\n"); - INIT_LIST_HEAD(&mc.mod_list); + INIT_LIST_HEAD(&mc.comp_list); ida_init(&mdev_id); mc.bus.name = "most", -- cgit From 5a5abf0200703f0148e04471e9fbe170d23be4ad Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:05:03 +0100 Subject: staging: most: core: rename aim variables This patch replaces the 'aim' substrings of variable names with 'comp'. It is needed because of the renaming of AIM modules to components. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.c | 172 ++++++++++++++++++++++---------------------- 1 file changed, 87 insertions(+), 85 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index c5b809b50247..ebfa97c504a2 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -40,7 +40,7 @@ static struct mostcore { #define to_driver(d) container_of(d, struct mostcore, drv); struct pipe { - struct core_component *aim; + struct core_component *comp; int refs; int num_buffers; }; @@ -533,11 +533,11 @@ static const struct attribute_group *interface_attr_groups[] = { */ static struct core_component *match_module(char *name) { - struct core_component *aim; + struct core_component *comp; - list_for_each_entry(aim, &mc.comp_list, list) { - if (!strcmp(aim->name, name)) - return aim; + list_for_each_entry(comp, &mc.comp_list, list) { + if (!strcmp(comp->name, name)) + return comp; } return NULL; } @@ -550,19 +550,19 @@ int print_links(struct device *dev, void *data) struct most_interface *iface = to_most_interface(dev); list_for_each_entry(c, &iface->p->channel_list, list) { - if (c->pipe0.aim) { + if (c->pipe0.comp) { offs += snprintf(buf + offs, PAGE_SIZE - offs, "%s:%s:%s\n", - c->pipe0.aim->name, + c->pipe0.comp->name, dev_name(&iface->dev), dev_name(&c->dev)); } - if (c->pipe1.aim) { + if (c->pipe1.comp) { offs += snprintf(buf + offs, PAGE_SIZE - offs, "%s:%s:%s\n", - c->pipe1.aim->name, + c->pipe1.comp->name, dev_name(&iface->dev), dev_name(&c->dev)); } @@ -578,12 +578,12 @@ static ssize_t links_show(struct device_driver *drv, char *buf) static ssize_t modules_show(struct device_driver *drv, char *buf) { - struct core_component *aim; + struct core_component *comp; int offs = 0; - list_for_each_entry(aim, &mc.comp_list, list) { + list_for_each_entry(comp, &mc.comp_list, list) { offs += snprintf(buf + offs, PAGE_SIZE - offs, "%s\n", - aim->name); + comp->name); } return offs; } @@ -661,24 +661,25 @@ static struct most_channel *get_channel(char *mdev, char *mdev_ch) } static -inline int link_channel_to_aim(struct most_channel *c, struct core_component *aim, - char *aim_param) +inline int link_channel_to_aim(struct most_channel *c, + struct core_component *comp, + char *comp_param) { int ret; - struct core_component **aim_ptr; + struct core_component **comp_ptr; - if (!c->pipe0.aim) - aim_ptr = &c->pipe0.aim; - else if (!c->pipe1.aim) - aim_ptr = &c->pipe1.aim; + if (!c->pipe0.comp) + comp_ptr = &c->pipe0.comp; + else if (!c->pipe1.comp) + comp_ptr = &c->pipe1.comp; else return -ENOSPC; - *aim_ptr = aim; - ret = aim->probe_channel(c->iface, c->channel_id, - &c->cfg, aim_param); + *comp_ptr = comp; + ret = comp->probe_channel(c->iface, c->channel_id, + &c->cfg, comp_param); if (ret) { - *aim_ptr = NULL; + *comp_ptr = NULL; return ret; } @@ -712,33 +713,33 @@ static ssize_t add_link_store(struct device_driver *drv, size_t len) { struct most_channel *c; - struct core_component *aim; + struct core_component *comp; char buffer[STRING_SIZE]; char *mdev; char *mdev_ch; - char *aim_name; - char *aim_param; + char *comp_name; + char *comp_param; char devnod_buf[STRING_SIZE]; int ret; size_t max_len = min_t(size_t, len + 1, STRING_SIZE); strlcpy(buffer, buf, max_len); - ret = split_string(buffer, &mdev, &mdev_ch, &aim_name, &aim_param); + ret = split_string(buffer, &mdev, &mdev_ch, &comp_name, &comp_param); if (ret) return ret; - aim = match_module(aim_name); - if (!aim_param || *aim_param == 0) { + comp = match_module(comp_name); + if (!comp_param || *comp_param == 0) { snprintf(devnod_buf, sizeof(devnod_buf), "%s-%s", mdev, mdev_ch); - aim_param = devnod_buf; + comp_param = devnod_buf; } c = get_channel(mdev, mdev_ch); if (!c) return -ENODEV; - ret = link_channel_to_aim(c, aim, aim_param); + ret = link_channel_to_aim(c, comp, comp_param); if (ret) return ret; @@ -760,29 +761,29 @@ static ssize_t remove_link_store(struct device_driver *drv, size_t len) { struct most_channel *c; - struct core_component *aim; + struct core_component *comp; char buffer[STRING_SIZE]; char *mdev; char *mdev_ch; - char *aim_name; + char *comp_name; int ret; size_t max_len = min_t(size_t, len + 1, STRING_SIZE); strlcpy(buffer, buf, max_len); - ret = split_string(buffer, &mdev, &mdev_ch, &aim_name, NULL); + ret = split_string(buffer, &mdev, &mdev_ch, &comp_name, NULL); if (ret) return ret; - aim = match_module(aim_name); + comp = match_module(comp_name); c = get_channel(mdev, mdev_ch); if (!c) return -ENODEV; - if (aim->disconnect_channel(c->iface, c->channel_id)) + if (comp->disconnect_channel(c->iface, c->channel_id)) return -EIO; - if (c->pipe0.aim == aim) - c->pipe0.aim = NULL; - if (c->pipe1.aim == aim) - c->pipe1.aim = NULL; + if (c->pipe0.comp == comp) + c->pipe0.comp = NULL; + if (c->pipe1.comp == comp) + c->pipe1.comp = NULL; return len; } @@ -939,11 +940,11 @@ static void arm_mbo(struct mbo *mbo) list_add_tail(&mbo->list, &c->fifo); spin_unlock_irqrestore(&c->fifo_lock, flags); - if (c->pipe0.refs && c->pipe0.aim->tx_completion) - c->pipe0.aim->tx_completion(c->iface, c->channel_id); + if (c->pipe0.refs && c->pipe0.comp->tx_completion) + c->pipe0.comp->tx_completion(c->iface, c->channel_id); - if (c->pipe1.refs && c->pipe1.aim->tx_completion) - c->pipe1.aim->tx_completion(c->iface, c->channel_id); + if (c->pipe1.refs && c->pipe1.comp->tx_completion) + c->pipe1.comp->tx_completion(c->iface, c->channel_id); } /** @@ -1040,7 +1041,8 @@ static void most_write_completion(struct mbo *mbo) arm_mbo(mbo); } -int channel_has_mbo(struct most_interface *iface, int id, struct core_component *aim) +int channel_has_mbo(struct most_interface *iface, int id, + struct core_component *comp) { struct most_channel *c = iface->p->channel[id]; unsigned long flags; @@ -1050,8 +1052,8 @@ int channel_has_mbo(struct most_interface *iface, int id, struct core_component return -EINVAL; if (c->pipe0.refs && c->pipe1.refs && - ((aim == c->pipe0.aim && c->pipe0.num_buffers <= 0) || - (aim == c->pipe1.aim && c->pipe1.num_buffers <= 0))) + ((comp == c->pipe0.comp && c->pipe0.num_buffers <= 0) || + (comp == c->pipe1.comp && c->pipe1.num_buffers <= 0))) return 0; spin_lock_irqsave(&c->fifo_lock, flags); @@ -1070,7 +1072,7 @@ EXPORT_SYMBOL_GPL(channel_has_mbo); * Returns a pointer to MBO on success or NULL otherwise. */ struct mbo *most_get_mbo(struct most_interface *iface, int id, - struct core_component *aim) + struct core_component *comp) { struct mbo *mbo; struct most_channel *c; @@ -1082,13 +1084,13 @@ struct mbo *most_get_mbo(struct most_interface *iface, int id, return NULL; if (c->pipe0.refs && c->pipe1.refs && - ((aim == c->pipe0.aim && c->pipe0.num_buffers <= 0) || - (aim == c->pipe1.aim && c->pipe1.num_buffers <= 0))) + ((comp == c->pipe0.comp && c->pipe0.num_buffers <= 0) || + (comp == c->pipe1.comp && c->pipe1.num_buffers <= 0))) return NULL; - if (aim == c->pipe0.aim) + if (comp == c->pipe0.comp) num_buffers_ptr = &c->pipe0.num_buffers; - else if (aim == c->pipe1.aim) + else if (comp == c->pipe1.comp) num_buffers_ptr = &c->pipe1.num_buffers; else num_buffers_ptr = &dummy_num_buffers; @@ -1153,12 +1155,12 @@ static void most_read_completion(struct mbo *mbo) if (atomic_sub_and_test(1, &c->mbo_nq_level)) c->is_starving = 1; - if (c->pipe0.refs && c->pipe0.aim->rx_completion && - c->pipe0.aim->rx_completion(mbo) == 0) + if (c->pipe0.refs && c->pipe0.comp->rx_completion && + c->pipe0.comp->rx_completion(mbo) == 0) return; - if (c->pipe1.refs && c->pipe1.aim->rx_completion && - c->pipe1.aim->rx_completion(mbo) == 0) + if (c->pipe1.refs && c->pipe1.comp->rx_completion && + c->pipe1.comp->rx_completion(mbo) == 0) return; most_put_mbo(mbo); @@ -1175,7 +1177,7 @@ static void most_read_completion(struct mbo *mbo) * Returns 0 on success or error code otherwise. */ int most_start_channel(struct most_interface *iface, int id, - struct core_component *aim) + struct core_component *comp) { int num_buffer; int ret; @@ -1186,7 +1188,7 @@ int most_start_channel(struct most_interface *iface, int id, mutex_lock(&c->start_mutex); if (c->pipe0.refs + c->pipe1.refs > 0) - goto out; /* already started by other aim */ + goto out; /* already started by other comp */ if (!try_module_get(iface->mod)) { pr_info("failed to acquire HDM lock\n"); @@ -1225,9 +1227,9 @@ int most_start_channel(struct most_interface *iface, int id, atomic_set(&c->mbo_ref, num_buffer); out: - if (aim == c->pipe0.aim) + if (comp == c->pipe0.comp) c->pipe0.refs++; - if (aim == c->pipe1.aim) + if (comp == c->pipe1.comp) c->pipe1.refs++; mutex_unlock(&c->start_mutex); return 0; @@ -1245,7 +1247,7 @@ EXPORT_SYMBOL_GPL(most_start_channel); * @id: channel ID */ int most_stop_channel(struct most_interface *iface, int id, - struct core_component *aim) + struct core_component *comp) { struct most_channel *c; @@ -1290,9 +1292,9 @@ int most_stop_channel(struct most_interface *iface, int id, c->is_poisoned = false; out: - if (aim == c->pipe0.aim) + if (comp == c->pipe0.comp) c->pipe0.refs--; - if (aim == c->pipe1.aim) + if (comp == c->pipe1.comp) c->pipe1.refs--; mutex_unlock(&c->start_mutex); return 0; @@ -1303,14 +1305,14 @@ EXPORT_SYMBOL_GPL(most_stop_channel); * most_register_component - registers an AIM (driver) with the core * @aim: instance of AIM to be registered */ -int most_register_component(struct core_component *aim) +int most_register_component(struct core_component *comp) { - if (!aim) { + if (!comp) { pr_err("Bad driver\n"); return -EINVAL; } - list_add_tail(&aim->list, &mc.comp_list); - pr_info("registered new application interfacing module %s\n", aim->name); + list_add_tail(&comp->list, &mc.comp_list); + pr_info("registered new application interfacing module %s\n", comp->name); return 0; } EXPORT_SYMBOL_GPL(most_register_component); @@ -1319,16 +1321,16 @@ static int disconnect_channels(struct device *dev, void *data) { struct most_interface *iface; struct most_channel *c, *tmp; - struct core_component *aim = data; + struct core_component *comp = data; iface = to_most_interface(dev); list_for_each_entry_safe(c, tmp, &iface->p->channel_list, list) { - if (c->pipe0.aim == aim || c->pipe1.aim == aim) - aim->disconnect_channel(c->iface, c->channel_id); - if (c->pipe0.aim == aim) - c->pipe0.aim = NULL; - if (c->pipe1.aim == aim) - c->pipe1.aim = NULL; + if (c->pipe0.comp == comp || c->pipe1.comp == comp) + comp->disconnect_channel(c->iface, c->channel_id); + if (c->pipe0.comp == comp) + c->pipe0.comp = NULL; + if (c->pipe1.comp == comp) + c->pipe1.comp = NULL; } return 0; } @@ -1337,16 +1339,16 @@ static int disconnect_channels(struct device *dev, void *data) * most_deregister_component - deregisters an AIM (driver) with the core * @aim: AIM to be removed */ -int most_deregister_component(struct core_component *aim) +int most_deregister_component(struct core_component *comp) { - if (!aim) { + if (!comp) { pr_err("Bad driver\n"); return -EINVAL; } - bus_for_each_dev(&mc.bus, NULL, aim, disconnect_channels); - list_del(&aim->list); - pr_info("deregistering module %s\n", aim->name); + bus_for_each_dev(&mc.bus, NULL, comp, disconnect_channels); + list_del(&comp->list); + pr_info("deregistering module %s\n", comp->name); return 0; } EXPORT_SYMBOL_GPL(most_deregister_component); @@ -1484,14 +1486,14 @@ void most_deregister_interface(struct most_interface *iface) pr_info("deregistering MOST device %s (%s)\n", dev_name(&iface->dev), iface->description); for (i = 0; i < iface->num_channels; i++) { c = iface->p->channel[i]; - if (c->pipe0.aim) - c->pipe0.aim->disconnect_channel(c->iface, + if (c->pipe0.comp) + c->pipe0.comp->disconnect_channel(c->iface, c->channel_id); - if (c->pipe1.aim) - c->pipe1.aim->disconnect_channel(c->iface, + if (c->pipe1.comp) + c->pipe1.comp->disconnect_channel(c->iface, c->channel_id); - c->pipe0.aim = NULL; - c->pipe1.aim = NULL; + c->pipe0.comp = NULL; + c->pipe1.comp = NULL; list_del(&c->list); device_unregister(&c->dev); kfree(c); -- cgit From db09fe0d3ab0bada23cb7a0be991a158a8fc5dcc Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:05:04 +0100 Subject: staging: most: core: rename function link_channel_to_aim This patch renames the function link_channel_to_aim to link_channel_to_component. It is needed because userspace interfacing modules are referred to as components. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index ebfa97c504a2..174c88a4bd9e 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -661,9 +661,9 @@ static struct most_channel *get_channel(char *mdev, char *mdev_ch) } static -inline int link_channel_to_aim(struct most_channel *c, - struct core_component *comp, - char *comp_param) +inline int link_channel_to_component(struct most_channel *c, + struct core_component *comp, + char *comp_param) { int ret; struct core_component **comp_ptr; @@ -739,7 +739,7 @@ static ssize_t add_link_store(struct device_driver *drv, if (!c) return -ENODEV; - ret = link_channel_to_aim(c, comp, comp_param); + ret = link_channel_to_component(c, comp, comp_param); if (ret) return ret; -- cgit From 11b3348f3b21b749f8bbc966f61b6ea6c9019403 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:05:05 +0100 Subject: staging: most: net: remove aim designators This patch renames the all aim designators with comp. It is needed because userspace interfacing modules are referred to as components. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/net/net.c | 44 +++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 22 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/net/net.c b/drivers/staging/most/net/net.c index cbe0dda011b4..38d6fe94ac6b 100644 --- a/drivers/staging/most/net/net.c +++ b/drivers/staging/most/net/net.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Networking AIM - Networking Application Interface Module for MostCore + * Networking component - Networking Application Interface Module for MostCore * * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG */ @@ -68,7 +68,7 @@ struct net_dev_context { static struct list_head net_devices = LIST_HEAD_INIT(net_devices); static struct mutex probe_disc_mt; /* ch->linked = true, most_nd_open */ static struct spinlock list_lock; /* list_head, ch->linked = false, dev_hold */ -static struct core_component aim; +static struct core_component comp; static int skb_to_mamac(const struct sk_buff *skb, struct mbo *mbo) { @@ -178,15 +178,15 @@ static int most_nd_open(struct net_device *dev) mutex_lock(&probe_disc_mt); - if (most_start_channel(nd->iface, nd->rx.ch_id, &aim)) { + if (most_start_channel(nd->iface, nd->rx.ch_id, &comp)) { netdev_err(dev, "most_start_channel() failed\n"); ret = -EBUSY; goto unlock; } - if (most_start_channel(nd->iface, nd->tx.ch_id, &aim)) { + if (most_start_channel(nd->iface, nd->tx.ch_id, &comp)) { netdev_err(dev, "most_start_channel() failed\n"); - most_stop_channel(nd->iface, nd->rx.ch_id, &aim); + most_stop_channel(nd->iface, nd->rx.ch_id, &comp); ret = -EBUSY; goto unlock; } @@ -212,8 +212,8 @@ static int most_nd_stop(struct net_device *dev) netif_stop_queue(dev); if (nd->iface->request_netinfo) nd->iface->request_netinfo(nd->iface, nd->tx.ch_id, NULL); - most_stop_channel(nd->iface, nd->rx.ch_id, &aim); - most_stop_channel(nd->iface, nd->tx.ch_id, &aim); + most_stop_channel(nd->iface, nd->rx.ch_id, &comp); + most_stop_channel(nd->iface, nd->tx.ch_id, &comp); return 0; } @@ -225,7 +225,7 @@ static netdev_tx_t most_nd_start_xmit(struct sk_buff *skb, struct mbo *mbo; int ret; - mbo = most_get_mbo(nd->iface, nd->tx.ch_id, &aim); + mbo = most_get_mbo(nd->iface, nd->tx.ch_id, &comp); if (!mbo) { netif_stop_queue(dev); @@ -290,8 +290,8 @@ static struct net_dev_context *get_net_dev_hold(struct most_interface *iface) return nd; } -static int aim_probe_channel(struct most_interface *iface, int channel_idx, - struct most_channel_config *ccfg, char *name) +static int comp_probe_channel(struct most_interface *iface, int channel_idx, + struct most_channel_config *ccfg, char *name) { struct net_dev_context *nd; struct net_dev_channel *ch; @@ -346,8 +346,8 @@ unlock: return ret; } -static int aim_disconnect_channel(struct most_interface *iface, - int channel_idx) +static int comp_disconnect_channel(struct most_interface *iface, + int channel_idx) { struct net_dev_context *nd; struct net_dev_channel *ch; @@ -393,8 +393,8 @@ unlock: return ret; } -static int aim_resume_tx_channel(struct most_interface *iface, - int channel_idx) +static int comp_resume_tx_channel(struct most_interface *iface, + int channel_idx) { struct net_dev_context *nd; @@ -412,7 +412,7 @@ put_nd: return 0; } -static int aim_rx_data(struct mbo *mbo) +static int comp_rx_data(struct mbo *mbo) { const u32 zero = 0; struct net_dev_context *nd; @@ -494,24 +494,24 @@ put_nd: return ret; } -static struct core_component aim = { +static struct core_component comp = { .name = "net", - .probe_channel = aim_probe_channel, - .disconnect_channel = aim_disconnect_channel, - .tx_completion = aim_resume_tx_channel, - .rx_completion = aim_rx_data, + .probe_channel = comp_probe_channel, + .disconnect_channel = comp_disconnect_channel, + .tx_completion = comp_resume_tx_channel, + .rx_completion = comp_rx_data, }; static int __init most_net_init(void) { spin_lock_init(&list_lock); mutex_init(&probe_disc_mt); - return most_register_component(&aim); + return most_register_component(&comp); } static void __exit most_net_exit(void) { - most_deregister_component(&aim); + most_deregister_component(&comp); } /** -- cgit From 1f95cf02a32d500b7aaa846e197f90e2f8b1828d Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:05:06 +0100 Subject: staging: most: sound: remove aim designator This patch removes all 'aim' designators and replaces them with 'comp'. It is needd because userspace interfacing modules are referred to as components. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/sound/sound.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/sound/sound.c b/drivers/staging/most/sound/sound.c index a3a46a269f43..e93b8047561c 100644 --- a/drivers/staging/most/sound/sound.c +++ b/drivers/staging/most/sound/sound.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * sound.c - Audio Application Interface Module for Mostcore + * sound.c - Sound component for Mostcore * * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG */ @@ -21,7 +21,7 @@ #define DRIVER_NAME "sound" static struct list_head dev_list; -static struct core_component audio_aim; +static struct core_component comp; /** * struct channel - private structure to keep channel specific data @@ -234,7 +234,7 @@ static int playback_thread(void *data) kthread_should_stop() || (channel->is_stream_running && (mbo = most_get_mbo(channel->iface, channel->id, - &audio_aim)))); + &comp)))); if (!mbo) continue; @@ -277,7 +277,7 @@ static int pcm_open(struct snd_pcm_substream *substream) } } - if (most_start_channel(channel->iface, channel->id, &audio_aim)) { + if (most_start_channel(channel->iface, channel->id, &comp)) { pr_err("most_start_channel() failed!\n"); if (cfg->direction == MOST_CH_TX) kthread_stop(channel->playback_task); @@ -304,7 +304,7 @@ static int pcm_close(struct snd_pcm_substream *substream) if (channel->cfg->direction == MOST_CH_TX) kthread_stop(channel->playback_task); - most_stop_channel(channel->iface, channel->id, &audio_aim); + most_stop_channel(channel->iface, channel->id, &comp); return 0; } @@ -719,7 +719,7 @@ static int audio_tx_completion(struct most_interface *iface, int channel_id) /** * Initialization of the struct core_component */ -static struct core_component audio_aim = { +static struct core_component comp = { .name = DRIVER_NAME, .probe_channel = audio_probe_channel, .disconnect_channel = audio_disconnect_channel, @@ -733,7 +733,7 @@ static int __init audio_init(void) INIT_LIST_HEAD(&dev_list); - return most_register_component(&audio_aim); + return most_register_component(&comp); } static void __exit audio_exit(void) @@ -747,7 +747,7 @@ static void __exit audio_exit(void) snd_card_free(channel->card); } - most_deregister_component(&audio_aim); + most_deregister_component(&comp); } module_init(audio_init); -- cgit From 1b10a0316e2d5e278a3cff14adcfcde71792f016 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:05:07 +0100 Subject: staging: most: video: remove aim designators This patch takes the 'aim' prefixes and variable names off the module and uses component instead. It is needed because userspace interfacing modules are referred to as components. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/video/video.c | 152 ++++++++++++++++++------------------- 1 file changed, 76 insertions(+), 76 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/video/video.c b/drivers/staging/most/video/video.c index 3a97a2443c0b..62b789e71399 100644 --- a/drivers/staging/most/video/video.c +++ b/drivers/staging/most/video/video.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * V4L2 AIM - V4L2 Application Interface Module for MostCore + * V4L2 Component - V4L2 Application Interface Module for MostCore * * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG */ @@ -23,9 +23,9 @@ #include "most/core.h" -#define V4L2_AIM_MAX_INPUT 1 +#define V4L2_CMP_MAX_INPUT 1 -static struct core_component aim_info; +static struct core_component comp; struct most_video_dev { struct most_interface *iface; @@ -46,7 +46,7 @@ struct most_video_dev { wait_queue_head_t wait_data; }; -struct aim_fh { +struct comp_fh { /* must be the first field of this struct! */ struct v4l2_fh fh; struct most_video_dev *mdev; @@ -66,14 +66,14 @@ static inline struct mbo *get_top_mbo(struct most_video_dev *mdev) return list_first_entry(&mdev->pending_mbos, struct mbo, list); } -static int aim_vdev_open(struct file *filp) +static int comp_vdev_open(struct file *filp) { int ret; struct video_device *vdev = video_devdata(filp); struct most_video_dev *mdev = video_drvdata(filp); - struct aim_fh *fh; + struct comp_fh *fh; - v4l2_info(&mdev->v4l2_dev, "aim_vdev_open()\n"); + v4l2_info(&mdev->v4l2_dev, "comp_vdev_open()\n"); switch (vdev->vfl_type) { case VFL_TYPE_GRABBER: @@ -98,7 +98,7 @@ static int aim_vdev_open(struct file *filp) v4l2_fh_add(&fh->fh); - ret = most_start_channel(mdev->iface, mdev->ch_idx, &aim_info); + ret = most_start_channel(mdev->iface, mdev->ch_idx, &comp); if (ret) { v4l2_err(&mdev->v4l2_dev, "most_start_channel() failed\n"); goto err_rm; @@ -116,13 +116,13 @@ err_dec: return ret; } -static int aim_vdev_close(struct file *filp) +static int comp_vdev_close(struct file *filp) { - struct aim_fh *fh = filp->private_data; + struct comp_fh *fh = filp->private_data; struct most_video_dev *mdev = fh->mdev; struct mbo *mbo, *tmp; - v4l2_info(&mdev->v4l2_dev, "aim_vdev_close()\n"); + v4l2_info(&mdev->v4l2_dev, "comp_vdev_close()\n"); /* * We need to put MBOs back before we call most_stop_channel() @@ -142,7 +142,7 @@ static int aim_vdev_close(struct file *filp) spin_lock_irq(&mdev->list_lock); } spin_unlock_irq(&mdev->list_lock); - most_stop_channel(mdev->iface, mdev->ch_idx, &aim_info); + most_stop_channel(mdev->iface, mdev->ch_idx, &comp); mdev->mute = false; v4l2_fh_del(&fh->fh); @@ -153,10 +153,10 @@ static int aim_vdev_close(struct file *filp) return 0; } -static ssize_t aim_vdev_read(struct file *filp, char __user *buf, - size_t count, loff_t *pos) +static ssize_t comp_vdev_read(struct file *filp, char __user *buf, + size_t count, loff_t *pos) { - struct aim_fh *fh = filp->private_data; + struct comp_fh *fh = filp->private_data; struct most_video_dev *mdev = fh->mdev; int ret = 0; @@ -203,9 +203,9 @@ static ssize_t aim_vdev_read(struct file *filp, char __user *buf, return ret; } -static unsigned int aim_vdev_poll(struct file *filp, poll_table *wait) +static unsigned int comp_vdev_poll(struct file *filp, poll_table *wait) { - struct aim_fh *fh = filp->private_data; + struct comp_fh *fh = filp->private_data; struct most_video_dev *mdev = fh->mdev; unsigned int mask = 0; @@ -218,7 +218,7 @@ static unsigned int aim_vdev_poll(struct file *filp, poll_table *wait) return mask; } -static void aim_set_format_struct(struct v4l2_format *f) +static void comp_set_format_struct(struct v4l2_format *f) { f->fmt.pix.width = 8; f->fmt.pix.height = 8; @@ -230,8 +230,8 @@ static void aim_set_format_struct(struct v4l2_format *f) f->fmt.pix.priv = 0; } -static int aim_set_format(struct most_video_dev *mdev, unsigned int cmd, - struct v4l2_format *format) +static int comp_set_format(struct most_video_dev *mdev, unsigned int cmd, + struct v4l2_format *format) { if (format->fmt.pix.pixelformat != V4L2_PIX_FMT_MPEG) return -EINVAL; @@ -239,7 +239,7 @@ static int aim_set_format(struct most_video_dev *mdev, unsigned int cmd, if (cmd == VIDIOC_TRY_FMT) return 0; - aim_set_format_struct(format); + comp_set_format_struct(format); return 0; } @@ -247,12 +247,12 @@ static int aim_set_format(struct most_video_dev *mdev, unsigned int cmd, static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct aim_fh *fh = priv; + struct comp_fh *fh = priv; struct most_video_dev *mdev = fh->mdev; v4l2_info(&mdev->v4l2_dev, "vidioc_querycap()\n"); - strlcpy(cap->driver, "v4l2_most_aim", sizeof(cap->driver)); + strlcpy(cap->driver, "v4l2_component", sizeof(cap->driver)); strlcpy(cap->card, "MOST", sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), "%s", mdev->iface->description); @@ -267,7 +267,7 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - struct aim_fh *fh = priv; + struct comp_fh *fh = priv; struct most_video_dev *mdev = fh->mdev; v4l2_info(&mdev->v4l2_dev, "vidioc_enum_fmt_vid_cap() %d\n", f->index); @@ -286,36 +286,36 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct aim_fh *fh = priv; + struct comp_fh *fh = priv; struct most_video_dev *mdev = fh->mdev; v4l2_info(&mdev->v4l2_dev, "vidioc_g_fmt_vid_cap()\n"); - aim_set_format_struct(f); + comp_set_format_struct(f); return 0; } static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct aim_fh *fh = priv; + struct comp_fh *fh = priv; struct most_video_dev *mdev = fh->mdev; - return aim_set_format(mdev, VIDIOC_TRY_FMT, f); + return comp_set_format(mdev, VIDIOC_TRY_FMT, f); } static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct aim_fh *fh = priv; + struct comp_fh *fh = priv; struct most_video_dev *mdev = fh->mdev; - return aim_set_format(mdev, VIDIOC_S_FMT, f); + return comp_set_format(mdev, VIDIOC_S_FMT, f); } static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) { - struct aim_fh *fh = priv; + struct comp_fh *fh = priv; struct most_video_dev *mdev = fh->mdev; v4l2_info(&mdev->v4l2_dev, "vidioc_g_std()\n"); @@ -327,10 +327,10 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *input) { - struct aim_fh *fh = priv; + struct comp_fh *fh = priv; struct most_video_dev *mdev = fh->mdev; - if (input->index >= V4L2_AIM_MAX_INPUT) + if (input->index >= V4L2_CMP_MAX_INPUT) return -EINVAL; strcpy(input->name, "MOST Video"); @@ -344,7 +344,7 @@ static int vidioc_enum_input(struct file *file, void *priv, static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) { - struct aim_fh *fh = priv; + struct comp_fh *fh = priv; struct most_video_dev *mdev = fh->mdev; *i = mdev->ctrl_input; return 0; @@ -352,23 +352,23 @@ static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) static int vidioc_s_input(struct file *file, void *priv, unsigned int index) { - struct aim_fh *fh = priv; + struct comp_fh *fh = priv; struct most_video_dev *mdev = fh->mdev; v4l2_info(&mdev->v4l2_dev, "vidioc_s_input(%d)\n", index); - if (index >= V4L2_AIM_MAX_INPUT) + if (index >= V4L2_CMP_MAX_INPUT) return -EINVAL; mdev->ctrl_input = index; return 0; } -static const struct v4l2_file_operations aim_fops = { +static const struct v4l2_file_operations comp_fops = { .owner = THIS_MODULE, - .open = aim_vdev_open, - .release = aim_vdev_close, - .read = aim_vdev_read, - .poll = aim_vdev_poll, + .open = comp_vdev_open, + .release = comp_vdev_close, + .read = comp_vdev_read, + .poll = comp_vdev_poll, .unlocked_ioctl = video_ioctl2, }; @@ -384,8 +384,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_s_input = vidioc_s_input, }; -static const struct video_device aim_videodev_template = { - .fops = &aim_fops, +static const struct video_device comp_videodev_template = { + .fops = &comp_fops, .release = video_device_release, .ioctl_ops = &video_ioctl_ops, .tvnorms = V4L2_STD_UNKNOWN, @@ -393,7 +393,7 @@ static const struct video_device aim_videodev_template = { /**************************************************************************/ -static struct most_video_dev *get_aim_dev( +static struct most_video_dev *get_comp_dev( struct most_interface *iface, int channel_idx) { struct most_video_dev *mdev; @@ -410,11 +410,11 @@ static struct most_video_dev *get_aim_dev( return NULL; } -static int aim_rx_data(struct mbo *mbo) +static int comp_rx_data(struct mbo *mbo) { unsigned long flags; struct most_video_dev *mdev = - get_aim_dev(mbo->ifp, mbo->hdm_channel_id); + get_comp_dev(mbo->ifp, mbo->hdm_channel_id); if (!mdev) return -EIO; @@ -431,11 +431,11 @@ static int aim_rx_data(struct mbo *mbo) return 0; } -static int aim_register_videodev(struct most_video_dev *mdev) +static int comp_register_videodev(struct most_video_dev *mdev) { int ret; - v4l2_info(&mdev->v4l2_dev, "aim_register_videodev()\n"); + v4l2_info(&mdev->v4l2_dev, "comp_register_videodev()\n"); init_waitqueue_head(&mdev->wait_data); @@ -445,7 +445,7 @@ static int aim_register_videodev(struct most_video_dev *mdev) return -ENOMEM; /* Fill the video capture device struct */ - *mdev->vdev = aim_videodev_template; + *mdev->vdev = comp_videodev_template; mdev->vdev->v4l2_dev = &mdev->v4l2_dev; mdev->vdev->lock = &mdev->lock; snprintf(mdev->vdev->name, sizeof(mdev->vdev->name), "MOST: %s", @@ -463,14 +463,14 @@ static int aim_register_videodev(struct most_video_dev *mdev) return ret; } -static void aim_unregister_videodev(struct most_video_dev *mdev) +static void comp_unregister_videodev(struct most_video_dev *mdev) { - v4l2_info(&mdev->v4l2_dev, "aim_unregister_videodev()\n"); + v4l2_info(&mdev->v4l2_dev, "comp_unregister_videodev()\n"); video_unregister_device(mdev->vdev); } -static void aim_v4l2_dev_release(struct v4l2_device *v4l2_dev) +static void comp_v4l2_dev_release(struct v4l2_device *v4l2_dev) { struct most_video_dev *mdev = container_of(v4l2_dev, struct most_video_dev, v4l2_dev); @@ -479,13 +479,13 @@ static void aim_v4l2_dev_release(struct v4l2_device *v4l2_dev) kfree(mdev); } -static int aim_probe_channel(struct most_interface *iface, int channel_idx, - struct most_channel_config *ccfg, char *name) +static int comp_probe_channel(struct most_interface *iface, int channel_idx, + struct most_channel_config *ccfg, char *name) { int ret; - struct most_video_dev *mdev = get_aim_dev(iface, channel_idx); + struct most_video_dev *mdev = get_comp_dev(iface, channel_idx); - pr_info("aim_probe_channel(%s)\n", name); + pr_info("comp_probe_channel(%s)\n", name); if (mdev) { pr_err("channel already linked\n"); @@ -513,7 +513,7 @@ static int aim_probe_channel(struct most_interface *iface, int channel_idx, INIT_LIST_HEAD(&mdev->pending_mbos); mdev->iface = iface; mdev->ch_idx = channel_idx; - mdev->v4l2_dev.release = aim_v4l2_dev_release; + mdev->v4l2_dev.release = comp_v4l2_dev_release; /* Create the v4l2_device */ strlcpy(mdev->v4l2_dev.name, name, sizeof(mdev->v4l2_dev.name)); @@ -524,14 +524,14 @@ static int aim_probe_channel(struct most_interface *iface, int channel_idx, return ret; } - ret = aim_register_videodev(mdev); + ret = comp_register_videodev(mdev); if (ret) goto err_unreg; spin_lock_irq(&list_lock); list_add(&mdev->list, &video_devices); spin_unlock_irq(&list_lock); - v4l2_info(&mdev->v4l2_dev, "aim_probe_channel() done\n"); + v4l2_info(&mdev->v4l2_dev, "comp_probe_channel() done\n"); return 0; err_unreg: @@ -540,42 +540,42 @@ err_unreg: return ret; } -static int aim_disconnect_channel(struct most_interface *iface, +static int comp_disconnect_channel(struct most_interface *iface, int channel_idx) { - struct most_video_dev *mdev = get_aim_dev(iface, channel_idx); + struct most_video_dev *mdev = get_comp_dev(iface, channel_idx); if (!mdev) { pr_err("no such channel is linked\n"); return -ENOENT; } - v4l2_info(&mdev->v4l2_dev, "aim_disconnect_channel()\n"); + v4l2_info(&mdev->v4l2_dev, "comp_disconnect_channel()\n"); spin_lock_irq(&list_lock); list_del(&mdev->list); spin_unlock_irq(&list_lock); - aim_unregister_videodev(mdev); + comp_unregister_videodev(mdev); v4l2_device_disconnect(&mdev->v4l2_dev); v4l2_device_put(&mdev->v4l2_dev); return 0; } -static struct core_component aim_info = { - .name = "v4l", - .probe_channel = aim_probe_channel, - .disconnect_channel = aim_disconnect_channel, - .rx_completion = aim_rx_data, +static struct core_component comp_info = { + .name = "video", + .probe_channel = comp_probe_channel, + .disconnect_channel = comp_disconnect_channel, + .rx_completion = comp_rx_data, }; -static int __init aim_init(void) +static int __init comp_init(void) { spin_lock_init(&list_lock); - return most_register_component(&aim_info); + return most_register_component(&comp); } -static void __exit aim_exit(void) +static void __exit comp_exit(void) { struct most_video_dev *mdev, *tmp; @@ -590,20 +590,20 @@ static void __exit aim_exit(void) list_del(&mdev->list); spin_unlock_irq(&list_lock); - aim_unregister_videodev(mdev); + comp_unregister_videodev(mdev); v4l2_device_disconnect(&mdev->v4l2_dev); v4l2_device_put(&mdev->v4l2_dev); spin_lock_irq(&list_lock); } spin_unlock_irq(&list_lock); - most_deregister_component(&aim_info); + most_deregister_component(&comp_info); BUG_ON(!list_empty(&video_devices)); } -module_init(aim_init); -module_exit(aim_exit); +module_init(comp_init); +module_exit(comp_exit); -MODULE_DESCRIPTION("V4L2 Application Interface Module for MostCore"); +MODULE_DESCRIPTION("V4L2 Component Module for MostCore"); MODULE_AUTHOR("Andrey Shvetsov "); MODULE_LICENSE("GPL"); -- cgit From ef0fbbbb9a6004af07bbb76aac718dd6dfc2d80e Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:05:08 +0100 Subject: staging: most: cdev: rename struct aim_channel This patch renames the structure aim_channel to comp_channel. It is needed to complete the process of changing the module designator from AIM to Component. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/cdev/cdev.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/cdev/cdev.c b/drivers/staging/most/cdev/cdev.c index 2447fbfd201e..7507b38f8e35 100644 --- a/drivers/staging/most/cdev/cdev.c +++ b/drivers/staging/most/cdev/cdev.c @@ -24,7 +24,7 @@ static struct ida minor_id; static unsigned int major; static struct core_component cdev_aim; -struct aim_channel { +struct comp_channel { wait_queue_head_t wq; spinlock_t unlink; /* synchronization lock to unlink channels */ struct cdev cdev; @@ -40,16 +40,16 @@ struct aim_channel { struct list_head list; }; -#define to_channel(d) container_of(d, struct aim_channel, cdev) +#define to_channel(d) container_of(d, struct comp_channel, cdev) static struct list_head channel_list; static spinlock_t ch_list_lock; -static inline bool ch_has_mbo(struct aim_channel *c) +static inline bool ch_has_mbo(struct comp_channel *c) { return channel_has_mbo(c->iface, c->channel_id, &cdev_aim) > 0; } -static inline bool ch_get_mbo(struct aim_channel *c, struct mbo **mbo) +static inline bool ch_get_mbo(struct comp_channel *c, struct mbo **mbo) { if (!kfifo_peek(&c->fifo, mbo)) { *mbo = most_get_mbo(c->iface, c->channel_id, &cdev_aim); @@ -59,9 +59,9 @@ static inline bool ch_get_mbo(struct aim_channel *c, struct mbo **mbo) return *mbo; } -static struct aim_channel *get_channel(struct most_interface *iface, int id) +static struct comp_channel *get_channel(struct most_interface *iface, int id) { - struct aim_channel *c, *tmp; + struct comp_channel *c, *tmp; unsigned long flags; int found_channel = 0; @@ -78,7 +78,7 @@ static struct aim_channel *get_channel(struct most_interface *iface, int id) return c; } -static void stop_channel(struct aim_channel *c) +static void stop_channel(struct comp_channel *c) { struct mbo *mbo; @@ -87,7 +87,7 @@ static void stop_channel(struct aim_channel *c) most_stop_channel(c->iface, c->channel_id, &cdev_aim); } -static void destroy_cdev(struct aim_channel *c) +static void destroy_cdev(struct comp_channel *c) { unsigned long flags; @@ -98,7 +98,7 @@ static void destroy_cdev(struct aim_channel *c) spin_unlock_irqrestore(&ch_list_lock, flags); } -static void destroy_channel(struct aim_channel *c) +static void destroy_channel(struct comp_channel *c) { ida_simple_remove(&minor_id, MINOR(c->devno)); kfifo_free(&c->fifo); @@ -115,7 +115,7 @@ static void destroy_channel(struct aim_channel *c) */ static int aim_open(struct inode *inode, struct file *filp) { - struct aim_channel *c; + struct comp_channel *c; int ret; c = to_channel(inode->i_cdev); @@ -159,7 +159,7 @@ static int aim_open(struct inode *inode, struct file *filp) */ static int aim_close(struct inode *inode, struct file *filp) { - struct aim_channel *c = to_channel(inode->i_cdev); + struct comp_channel *c = to_channel(inode->i_cdev); mutex_lock(&c->io_mutex); spin_lock(&c->unlink); @@ -188,7 +188,7 @@ static ssize_t aim_write(struct file *filp, const char __user *buf, int ret; size_t to_copy, left; struct mbo *mbo = NULL; - struct aim_channel *c = filp->private_data; + struct comp_channel *c = filp->private_data; mutex_lock(&c->io_mutex); while (c->dev && !ch_get_mbo(c, &mbo)) { @@ -241,7 +241,7 @@ aim_read(struct file *filp, char __user *buf, size_t count, loff_t *offset) { size_t to_copy, not_copied, copied; struct mbo *mbo; - struct aim_channel *c = filp->private_data; + struct comp_channel *c = filp->private_data; mutex_lock(&c->io_mutex); while (c->dev && !kfifo_peek(&c->fifo, &mbo)) { @@ -283,7 +283,7 @@ aim_read(struct file *filp, char __user *buf, size_t count, loff_t *offset) static unsigned int aim_poll(struct file *filp, poll_table *wait) { - struct aim_channel *c = filp->private_data; + struct comp_channel *c = filp->private_data; unsigned int mask = 0; poll_wait(filp, &c->wq, wait); @@ -320,7 +320,7 @@ static const struct file_operations channel_fops = { */ static int aim_disconnect_channel(struct most_interface *iface, int channel_id) { - struct aim_channel *c; + struct comp_channel *c; if (!iface) { pr_info("Bad interface pointer\n"); @@ -356,7 +356,7 @@ static int aim_disconnect_channel(struct most_interface *iface, int channel_id) */ static int aim_rx_completion(struct mbo *mbo) { - struct aim_channel *c; + struct comp_channel *c; if (!mbo) return -EINVAL; @@ -389,7 +389,7 @@ static int aim_rx_completion(struct mbo *mbo) */ static int aim_tx_completion(struct most_interface *iface, int channel_id) { - struct aim_channel *c; + struct comp_channel *c; if (!iface) { pr_info("Bad interface pointer\n"); @@ -421,7 +421,7 @@ static int aim_tx_completion(struct most_interface *iface, int channel_id) static int aim_probe(struct most_interface *iface, int channel_id, struct most_channel_config *cfg, char *name) { - struct aim_channel *c; + struct comp_channel *c; unsigned long cl_flags; int retval; int current_minor; @@ -534,7 +534,7 @@ dest_ida: static void __exit mod_exit(void) { - struct aim_channel *c, *tmp; + struct comp_channel *c, *tmp; pr_info("exit module\n"); -- cgit From 90c8d77fcb24d7611cfff5931fcd8f0a3b756c3a Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:05:09 +0100 Subject: staging: most: cdev: rename variable aim_devno This patch renames the variable aim_devno to comp_devno. It is needed to complete the process of changing the module designator from AIM to Component. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/cdev/cdev.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/cdev/cdev.c b/drivers/staging/most/cdev/cdev.c index 7507b38f8e35..e39fca614593 100644 --- a/drivers/staging/most/cdev/cdev.c +++ b/drivers/staging/most/cdev/cdev.c @@ -18,7 +18,7 @@ #include #include "most/core.h" -static dev_t aim_devno; +static dev_t comp_devno; static struct class *aim_class; static struct ida minor_id; static unsigned int major; @@ -507,10 +507,10 @@ static int __init mod_init(void) spin_lock_init(&ch_list_lock); ida_init(&minor_id); - err = alloc_chrdev_region(&aim_devno, 0, 50, "cdev"); + err = alloc_chrdev_region(&comp_devno, 0, 50, "cdev"); if (err < 0) goto dest_ida; - major = MAJOR(aim_devno); + major = MAJOR(comp_devno); aim_class = class_create(THIS_MODULE, "most_cdev_aim"); if (IS_ERR(aim_class)) { @@ -526,7 +526,7 @@ static int __init mod_init(void) dest_class: class_destroy(aim_class); free_cdev: - unregister_chrdev_region(aim_devno, 1); + unregister_chrdev_region(comp_devno, 1); dest_ida: ida_destroy(&minor_id); return err; @@ -545,7 +545,7 @@ static void __exit mod_exit(void) destroy_channel(c); } class_destroy(aim_class); - unregister_chrdev_region(aim_devno, 1); + unregister_chrdev_region(comp_devno, 1); ida_destroy(&minor_id); } -- cgit From eaf03a2875bdd85c3b0cf45cea5844bdf6f2dc79 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:05:10 +0100 Subject: staging: most: cdev: rename class instance aim_class This patch renames the instance aim_class of struct class to comp_class. It is needed to complete the process of changing the module designator from AIM to Component. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/cdev/cdev.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/cdev/cdev.c b/drivers/staging/most/cdev/cdev.c index e39fca614593..dfc6a6abbd54 100644 --- a/drivers/staging/most/cdev/cdev.c +++ b/drivers/staging/most/cdev/cdev.c @@ -19,7 +19,7 @@ #include "most/core.h" static dev_t comp_devno; -static struct class *aim_class; +static struct class *comp_class; static struct ida minor_id; static unsigned int major; static struct core_component cdev_aim; @@ -91,7 +91,7 @@ static void destroy_cdev(struct comp_channel *c) { unsigned long flags; - device_destroy(aim_class, c->devno); + device_destroy(comp_class, c->devno); cdev_del(&c->cdev); spin_lock_irqsave(&ch_list_lock, flags); list_del(&c->list); @@ -464,7 +464,7 @@ static int aim_probe(struct most_interface *iface, int channel_id, spin_lock_irqsave(&ch_list_lock, cl_flags); list_add_tail(&c->list, &channel_list); spin_unlock_irqrestore(&ch_list_lock, cl_flags); - c->dev = device_create(aim_class, + c->dev = device_create(comp_class, NULL, c->devno, NULL, @@ -512,10 +512,10 @@ static int __init mod_init(void) goto dest_ida; major = MAJOR(comp_devno); - aim_class = class_create(THIS_MODULE, "most_cdev_aim"); - if (IS_ERR(aim_class)) { + comp_class = class_create(THIS_MODULE, "most_cdev_aim"); + if (IS_ERR(comp_class)) { pr_err("no udev support\n"); - err = PTR_ERR(aim_class); + err = PTR_ERR(comp_class); goto free_cdev; } err = most_register_component(&cdev_aim); @@ -524,7 +524,7 @@ static int __init mod_init(void) return 0; dest_class: - class_destroy(aim_class); + class_destroy(comp_class); free_cdev: unregister_chrdev_region(comp_devno, 1); dest_ida: @@ -544,7 +544,7 @@ static void __exit mod_exit(void) destroy_cdev(c); destroy_channel(c); } - class_destroy(aim_class); + class_destroy(comp_class); unregister_chrdev_region(comp_devno, 1); ida_destroy(&minor_id); } -- cgit From 6822ba8a6739011f37a51f18c159ce5d35681cc6 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:05:11 +0100 Subject: staging: most: cdev: rename variable cdev_aim This patch renames the variable cdev_aim to cdev_comp. It is needed to complete the process of changing the module designator from AIM to Component. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/cdev/cdev.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/cdev/cdev.c b/drivers/staging/most/cdev/cdev.c index dfc6a6abbd54..8a4ed753beae 100644 --- a/drivers/staging/most/cdev/cdev.c +++ b/drivers/staging/most/cdev/cdev.c @@ -22,7 +22,7 @@ static dev_t comp_devno; static struct class *comp_class; static struct ida minor_id; static unsigned int major; -static struct core_component cdev_aim; +static struct core_component cdev_comp; struct comp_channel { wait_queue_head_t wq; @@ -46,13 +46,13 @@ static spinlock_t ch_list_lock; static inline bool ch_has_mbo(struct comp_channel *c) { - return channel_has_mbo(c->iface, c->channel_id, &cdev_aim) > 0; + return channel_has_mbo(c->iface, c->channel_id, &cdev_comp) > 0; } static inline bool ch_get_mbo(struct comp_channel *c, struct mbo **mbo) { if (!kfifo_peek(&c->fifo, mbo)) { - *mbo = most_get_mbo(c->iface, c->channel_id, &cdev_aim); + *mbo = most_get_mbo(c->iface, c->channel_id, &cdev_comp); if (*mbo) kfifo_in(&c->fifo, mbo, 1); } @@ -84,7 +84,7 @@ static void stop_channel(struct comp_channel *c) while (kfifo_out((struct kfifo *)&c->fifo, &mbo, 1)) most_put_mbo(mbo); - most_stop_channel(c->iface, c->channel_id, &cdev_aim); + most_stop_channel(c->iface, c->channel_id, &cdev_comp); } static void destroy_cdev(struct comp_channel *c) @@ -143,7 +143,7 @@ static int aim_open(struct inode *inode, struct file *filp) } c->mbo_offs = 0; - ret = most_start_channel(c->iface, c->channel_id, &cdev_aim); + ret = most_start_channel(c->iface, c->channel_id, &cdev_comp); if (!ret) c->access_ref = 1; mutex_unlock(&c->io_mutex); @@ -489,7 +489,7 @@ error_alloc_channel: return retval; } -static struct core_component cdev_aim = { +static struct core_component cdev_comp = { .name = "cdev", .probe_channel = aim_probe, .disconnect_channel = aim_disconnect_channel, @@ -512,13 +512,13 @@ static int __init mod_init(void) goto dest_ida; major = MAJOR(comp_devno); - comp_class = class_create(THIS_MODULE, "most_cdev_aim"); + comp_class = class_create(THIS_MODULE, "most_cdev_comp"); if (IS_ERR(comp_class)) { pr_err("no udev support\n"); err = PTR_ERR(comp_class); goto free_cdev; } - err = most_register_component(&cdev_aim); + err = most_register_component(&cdev_comp); if (err) goto dest_class; return 0; @@ -538,7 +538,7 @@ static void __exit mod_exit(void) pr_info("exit module\n"); - most_deregister_component(&cdev_aim); + most_deregister_component(&cdev_comp); list_for_each_entry_safe(c, tmp, &channel_list, list) { destroy_cdev(c); -- cgit From b7937dc41362f73cb1ed228fdd3ac32a69b17794 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:05:12 +0100 Subject: staging: most: fix comment sections This patch updates and corrects the comment sections of the code. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/cdev/cdev.c | 4 +- drivers/staging/most/core.c | 108 ++++++++++++++++--------------------- drivers/staging/most/core.h | 39 ++++++-------- drivers/staging/most/net/net.c | 4 +- drivers/staging/most/sound/sound.c | 2 +- drivers/staging/most/video/video.c | 4 +- 6 files changed, 70 insertions(+), 91 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/cdev/cdev.c b/drivers/staging/most/cdev/cdev.c index 8a4ed753beae..266e2bb6ecf7 100644 --- a/drivers/staging/most/cdev/cdev.c +++ b/drivers/staging/most/cdev/cdev.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * cdev.c - Application interfacing module for character devices + * cdev.c - Character device component for Mostcore * * Copyright (C) 2013-2015 Microchip Technology Germany II GmbH & Co. KG */ @@ -553,4 +553,4 @@ module_init(mod_init); module_exit(mod_exit); MODULE_AUTHOR("Christian Gromm "); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("character device AIM for mostcore"); +MODULE_DESCRIPTION("character device component for mostcore"); diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index 174c88a4bd9e..29dc89c85e33 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -103,14 +103,9 @@ static const struct { _mbo; \ }) -/* ___ ___ - * ___C H A N N E L___ - */ - /** * most_free_mbo_coherent - free an MBO and its coherent buffer - * @mbo: buffer to be released - * + * @mbo: most buffer */ static void most_free_mbo_coherent(struct mbo *mbo) { @@ -469,9 +464,6 @@ static const struct attribute_group *channel_attr_groups[] = { NULL, }; -/* ___ ___ - * ___I N S T A N C E___ - */ static ssize_t description_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -528,9 +520,6 @@ static const struct attribute_group *interface_attr_groups[] = { NULL, }; -/* ___ ___ - * ___A I M___ - */ static struct core_component *match_module(char *name) { struct core_component *comp; @@ -588,26 +577,26 @@ static ssize_t modules_show(struct device_driver *drv, char *buf) return offs; } /** - * split_string - parses and changes string in the buffer buf and - * splits it into two mandatory and one optional substrings. + * split_string - parses buf and extracts ':' separated substrings. * * @buf: complete string from attribute 'add_channel' - * @a: address of pointer to 1st substring (=instance name) - * @b: address of pointer to 2nd substring (=channel name) - * @c: optional address of pointer to 3rd substring (=user defined name) + * @a: storage for 1st substring (=interface name) + * @b: storage for 2nd substring (=channel name) + * @c: storage for 3rd substring (=component name) + * @d: storage optional 4th substring (=user defined name) * * Examples: * - * Input: "mdev0:ch6:my_channel\n" or - * "mdev0:ch6:my_channel" + * Input: "mdev0:ch6:cdev:my_channel\n" or + * "mdev0:ch6:cdev:my_channel" * - * Output: *a -> "mdev0", *b -> "ch6", *c -> "my_channel" + * Output: *a -> "mdev0", *b -> "ch6", *c -> "cdev" *d -> "my_channel" * - * Input: "mdev1:ep81\n" - * Output: *a -> "mdev1", *b -> "ep81", *c -> "" + * Input: "mdev1:ep81:cdev\n" + * Output: *a -> "mdev1", *b -> "ep81", *c -> "cdev" *d -> "" * * Input: "mdev1:ep81" - * Output: *a -> "mdev1", *b -> "ep81", *c == NULL + * Output: *a -> "mdev1", *b -> "ep81", *c -> "cdev" *d == NULL */ static int split_string(char *buf, char **a, char **b, char **c, char **d) { @@ -637,11 +626,9 @@ static int match_bus_dev(struct device *dev, void *data) } /** - * get_channel - get pointer to channel object - * @mdev: name of the device instance - * @mdev_ch: name of the respective channel - * - * This retrieves the pointer to a channel object. + * get_channel - get pointer to channel + * @mdev: name of the device interface + * @mdev_ch: name of channel */ static struct most_channel *get_channel(char *mdev, char *mdev_ch) { @@ -687,23 +674,22 @@ inline int link_channel_to_component(struct most_channel *c, } /** - * add_link_store - store() function for add_link attribute - * @aim_obj: pointer to AIM object - * @attr: its attributes + * add_link_store - store function for add_link attribute + * @drv: device driver * @buf: buffer * @len: buffer length * * This parses the string given by buf and splits it into - * three substrings. Note: third substring is optional. In case a cdev - * AIM is loaded the optional 3rd substring will make up the name of + * four substrings. Note: last substring is optional. In case a cdev + * component is loaded the optional 4th substring will make up the name of * device node in the /dev directory. If omitted, the device node will * inherit the channel's name within sysfs. * - * Searches for a pair of device and channel and probes the AIM + * Searches for (device, channel) pair and probes the component * * Example: - * (1) echo "mdev0:ch6:my_rxchannel" >add_link - * (2) echo "mdev1:ep81" >add_link + * (1) echo "mdev0:ch6:cdev:my_rxchannel" >add_link + * (2) echo "mdev1:ep81:cdev" >add_link * * (1) would create the device node /dev/my_rxchannel * (2) would create the device node /dev/mdev1-ep81 @@ -748,8 +734,7 @@ static ssize_t add_link_store(struct device_driver *drv, /** * remove_link_store - store function for remove_link attribute - * @aim_obj: pointer to AIM object - * @attr: its attributes + * @drv: device driver * @buf: buffer * @len: buffer length * @@ -811,10 +796,6 @@ static const struct attribute_group *module_attr_groups[] = { NULL, }; -/* ___ ___ - * ___C O R E___ - */ - int most_match(struct device *dev, struct device_driver *drv) { if (!strcmp(dev_name(dev), "most")) @@ -913,14 +894,14 @@ static int run_enqueue_thread(struct most_channel *c, int channel_id) /** * arm_mbo - recycle MBO for further usage - * @mbo: buffer object + * @mbo: most buffer * * This puts an MBO back to the list to have it ready for up coming * tx transactions. * * In case the MBO belongs to a channel that recently has been * poisoned, the MBO is scheduled to be trashed. - * Calls the completion handler of an attached AIM. + * Calls the completion handler of an attached component. */ static void arm_mbo(struct mbo *mbo) { @@ -1007,7 +988,7 @@ _exit: /** * most_submit_mbo - submits an MBO to fifo - * @mbo: pointer to the MBO + * @mbo: most buffer */ void most_submit_mbo(struct mbo *mbo) { @@ -1021,7 +1002,7 @@ EXPORT_SYMBOL_GPL(most_submit_mbo); /** * most_write_completion - write completion handler - * @mbo: pointer to MBO + * @mbo: most buffer * * This recycles the MBO for further usage. In case the channel has been * poisoned, the MBO is scheduled to be trashed. @@ -1067,6 +1048,7 @@ EXPORT_SYMBOL_GPL(channel_has_mbo); * most_get_mbo - get pointer to an MBO of pool * @iface: pointer to interface instance * @id: channel ID + * @comp: driver component * * This attempts to get a free buffer out of the channel fifo. * Returns a pointer to MBO on success or NULL otherwise. @@ -1112,7 +1094,7 @@ EXPORT_SYMBOL_GPL(most_get_mbo); /** * most_put_mbo - return buffer to pool - * @mbo: buffer object + * @mbo: most buffer */ void most_put_mbo(struct mbo *mbo) { @@ -1129,13 +1111,13 @@ EXPORT_SYMBOL_GPL(most_put_mbo); /** * most_read_completion - read completion handler - * @mbo: pointer to MBO + * @mbo: most buffer * * This function is called by the HDM when data has been received from the * hardware and copied to the buffer of the MBO. * * In case the channel has been poisoned it puts the buffer in the trash queue. - * Otherwise, it passes the buffer to an AIM for further processing. + * Otherwise, it passes the buffer to an component for further processing. */ static void most_read_completion(struct mbo *mbo) { @@ -1170,6 +1152,7 @@ static void most_read_completion(struct mbo *mbo) * most_start_channel - prepares a channel for communication * @iface: pointer to interface instance * @id: channel ID + * @comp: driver component * * This prepares the channel for usage. Cross-checks whether the * channel's been properly configured. @@ -1188,7 +1171,7 @@ int most_start_channel(struct most_interface *iface, int id, mutex_lock(&c->start_mutex); if (c->pipe0.refs + c->pipe1.refs > 0) - goto out; /* already started by other comp */ + goto out; /* already started by another component */ if (!try_module_get(iface->mod)) { pr_info("failed to acquire HDM lock\n"); @@ -1245,6 +1228,7 @@ EXPORT_SYMBOL_GPL(most_start_channel); * most_stop_channel - stops a running channel * @iface: pointer to interface instance * @id: channel ID + * @comp: driver component */ int most_stop_channel(struct most_interface *iface, int id, struct core_component *comp) @@ -1302,17 +1286,17 @@ out: EXPORT_SYMBOL_GPL(most_stop_channel); /** - * most_register_component - registers an AIM (driver) with the core - * @aim: instance of AIM to be registered + * most_register_component - registers a driver component with the core + * @comp: driver component */ int most_register_component(struct core_component *comp) { if (!comp) { - pr_err("Bad driver\n"); + pr_err("Bad component\n"); return -EINVAL; } list_add_tail(&comp->list, &mc.comp_list); - pr_info("registered new application interfacing module %s\n", comp->name); + pr_info("registered new core component %s\n", comp->name); return 0; } EXPORT_SYMBOL_GPL(most_register_component); @@ -1336,19 +1320,19 @@ static int disconnect_channels(struct device *dev, void *data) } /** - * most_deregister_component - deregisters an AIM (driver) with the core - * @aim: AIM to be removed + * most_deregister_component - deregisters a driver component with the core + * @comp: driver component */ int most_deregister_component(struct core_component *comp) { if (!comp) { - pr_err("Bad driver\n"); + pr_err("Bad component\n"); return -EINVAL; } bus_for_each_dev(&mc.bus, NULL, comp, disconnect_channels); list_del(&comp->list); - pr_info("deregistering module %s\n", comp->name); + pr_info("deregistering component %s\n", comp->name); return 0; } EXPORT_SYMBOL_GPL(most_deregister_component); @@ -1365,7 +1349,7 @@ static void release_channel(struct device *dev) /** * most_register_interface - registers an interface with core - * @iface: pointer to the instance of the interface description. + * @iface: device interface * * Allocates and initializes a new interface instance and all of its channels. * Returns a pointer to kobject or an error pointer. @@ -1451,7 +1435,7 @@ int most_register_interface(struct most_interface *iface) mutex_init(&c->nq_mutex); list_add_tail(&c->list, &iface->p->channel_list); } - pr_info("registered new MOST device mdev%d (%s)\n", + pr_info("registered new device mdev%d (%s)\n", id, iface->description); return 0; @@ -1473,7 +1457,7 @@ EXPORT_SYMBOL_GPL(most_register_interface); /** * most_deregister_interface - deregisters an interface with core - * @iface: pointer to the interface instance description. + * @iface: device interface * * Before removing an interface instance from the list, all running * channels are stopped and poisoned. @@ -1483,7 +1467,7 @@ void most_deregister_interface(struct most_interface *iface) int i; struct most_channel *c; - pr_info("deregistering MOST device %s (%s)\n", dev_name(&iface->dev), iface->description); + pr_info("deregistering device %s (%s)\n", dev_name(&iface->dev), iface->description); for (i = 0; i < iface->num_channels; i++) { c = iface->p->channel[i]; if (c->pipe0.comp) diff --git a/drivers/staging/most/core.h b/drivers/staging/most/core.h index 5a3a5c5c3347..52db7fa15f65 100644 --- a/drivers/staging/most/core.h +++ b/drivers/staging/most/core.h @@ -1,18 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 /* - * most.h - Interface between MostCore, - * Hardware Dependent Module (HDM) and Application Interface Module (AIM). + * most.h - API for component and adapter drivers * * Copyright (C) 2013-2015, Microchip Technology Germany II GmbH & Co. KG */ -/* - * Authors: - * Andrey Shvetsov - * Christian Gromm - * Sebastian Graf - */ - #ifndef __MOST_CORE_H__ #define __MOST_CORE_H__ @@ -74,22 +66,22 @@ enum mbo_status_flags { * The value is bitwise OR-combination of the values from the * enumeration most_channel_data_type. Zero is allowed value and means * "channel may not be used". - * @num_buffer_packet: Maximum number of buffers supported by this channel + * @num_buffers_packet: Maximum number of buffers supported by this channel * for packet data types (Async,Control,QoS) * @buffer_size_packet: Maximum buffer size supported by this channel * for packet data types (Async,Control,QoS) - * @num_buffer_streaming: Maximum number of buffers supported by this channel + * @num_buffers_streaming: Maximum number of buffers supported by this channel * for streaming data types (Sync,AV Packetized) * @buffer_size_streaming: Maximum buffer size supported by this channel * for streaming data types (Sync,AV Packetized) * @name_suffix: Optional suffix providean by an HDM that is attached to the * regular channel name. * - * Describes the capabilities of a MostCore channel like supported Data Types + * Describes the capabilities of a MOST channel like supported Data Types * and directions. This information is provided by an HDM for the MostCore. * * The Core creates read only sysfs attribute files in - * /sys/devices/virtual/most/mostcore/devices/mdev-#/mdev#-ch#/ with the + * /sys/devices/most/mdev#// with the * following attributes: * -available_directions * -available_datatypes @@ -124,7 +116,7 @@ struct most_channel_capability { * @packets_per_xact: number of MOST frames that are packet inside one USB * packet. This is USB specific * - * Describes the configuration for a MostCore channel. This information is + * Describes the configuration for a MOST channel. This information is * provided from the MostCore to a HDM (like the Medusa PCIe Interface) as a * parameter of the "configure" function call. */ @@ -148,6 +140,7 @@ struct most_channel_config { * * @list: list head for use by the mbo's current owner * @ifp: (in) associated interface instance + * @num_buffers_ptr: amount of pool buffers * @hdm_channel_id: (in) HDM channel instance * @virt_address: (in) kernel virtual address of the buffer * @bus_address: (in) bus address of the buffer @@ -156,15 +149,15 @@ struct most_channel_config { * @status: (out) transfer status * @complete: (in) completion routine * - * The MostCore allocates and initializes the MBO. + * The core allocates and initializes the MBO. * - * The HDM receives MBO for transfer from MostCore with the call to enqueue(). + * The HDM receives MBO for transfer from the core with the call to enqueue(). * The HDM copies the data to- or from the buffer depending on configured * channel direction, set "processed_length" and "status" and completes * the transfer procedure by calling the completion routine. * - * At the end the MostCore deallocates the MBO or recycles it for further - * transfers for the same or different HDM. + * Finally, the MBO is being deallocated or recycled for further + * transfers of the same or a different HDM. * * Directions of usage: * The core driver should never access any MBO fields (even if marked @@ -197,10 +190,12 @@ struct mbo { /** * Interface instance description. * - * Describes one instance of an interface like Medusa PCIe or Vantage USB. + * Describes an interface of a MOST device the core driver is bound to. * This structure is allocated and initialized in the HDM. MostCore may not * modify this structure. * + * @dev: the actual device + * @mod: module * @interface Interface type. \sa most_interface_type. * @description PRELIMINARY. * Unique description of the device instance from point of view of the @@ -255,13 +250,13 @@ struct most_interface { #define to_most_interface(d) container_of(d, struct most_interface, dev) /** - * struct most_aim - identifies MOST device driver to mostcore - * @name: Driver name + * struct core_component - identifies a loadable component for the mostcore + * @list: list_head + * @name: component name * @probe_channel: function for core to notify driver about channel connection * @disconnect_channel: callback function to disconnect a certain channel * @rx_completion: completion handler for received packets * @tx_completion: completion handler for transmitted packets - * @context: context pointer to be used by mostcore */ struct core_component { struct list_head list; diff --git a/drivers/staging/most/net/net.c b/drivers/staging/most/net/net.c index 38d6fe94ac6b..33606584e50e 100644 --- a/drivers/staging/most/net/net.c +++ b/drivers/staging/most/net/net.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Networking component - Networking Application Interface Module for MostCore + * net.c - Networking component for Mostcore * * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG */ @@ -557,4 +557,4 @@ module_init(most_net_init); module_exit(most_net_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Andrey Shvetsov "); -MODULE_DESCRIPTION("Networking Application Interface Module for MostCore"); +MODULE_DESCRIPTION("Networking Component Module for Mostcore"); diff --git a/drivers/staging/most/sound/sound.c b/drivers/staging/most/sound/sound.c index e93b8047561c..83cec21c85b8 100644 --- a/drivers/staging/most/sound/sound.c +++ b/drivers/staging/most/sound/sound.c @@ -755,4 +755,4 @@ module_exit(audio_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Christian Gromm "); -MODULE_DESCRIPTION("Audio Application Interface Module for MostCore"); +MODULE_DESCRIPTION("Sound Component Module for Mostcore"); diff --git a/drivers/staging/most/video/video.c b/drivers/staging/most/video/video.c index 62b789e71399..4be1e0dc9dd2 100644 --- a/drivers/staging/most/video/video.c +++ b/drivers/staging/most/video/video.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * V4L2 Component - V4L2 Application Interface Module for MostCore + * video.c - V4L2 component for Mostcore * * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG */ @@ -604,6 +604,6 @@ static void __exit comp_exit(void) module_init(comp_init); module_exit(comp_exit); -MODULE_DESCRIPTION("V4L2 Component Module for MostCore"); +MODULE_DESCRIPTION("V4L2 Component Module for Mostcore"); MODULE_AUTHOR("Andrey Shvetsov "); MODULE_LICENSE("GPL"); -- cgit From fdbdc0e6fe496ad3690be5b3c5c2dba1953ed976 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:05:13 +0100 Subject: staging: most: core: denote modules as components Substrings containing 'module' are replaced with 'component' by this patch. It is needed to complete the process of changing the module designator from AIM to Component. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index 29dc89c85e33..600007e7f55c 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -520,7 +520,7 @@ static const struct attribute_group *interface_attr_groups[] = { NULL, }; -static struct core_component *match_module(char *name) +static struct core_component *match_component(char *name) { struct core_component *comp; @@ -565,7 +565,7 @@ static ssize_t links_show(struct device_driver *drv, char *buf) return strlen(buf); } -static ssize_t modules_show(struct device_driver *drv, char *buf) +static ssize_t components_show(struct device_driver *drv, char *buf) { struct core_component *comp; int offs = 0; @@ -714,7 +714,7 @@ static ssize_t add_link_store(struct device_driver *drv, ret = split_string(buffer, &mdev, &mdev_ch, &comp_name, &comp_param); if (ret) return ret; - comp = match_module(comp_name); + comp = match_component(comp_name); if (!comp_param || *comp_param == 0) { snprintf(devnod_buf, sizeof(devnod_buf), "%s-%s", mdev, mdev_ch); @@ -758,7 +758,7 @@ static ssize_t remove_link_store(struct device_driver *drv, ret = split_string(buffer, &mdev, &mdev_ch, &comp_name, NULL); if (ret) return ret; - comp = match_module(comp_name); + comp = match_component(comp_name); c = get_channel(mdev, mdev_ch); if (!c) return -ENODEV; @@ -775,24 +775,24 @@ static ssize_t remove_link_store(struct device_driver *drv, #define DRV_ATTR(_name) (&driver_attr_##_name.attr) static DRIVER_ATTR_RO(links); -static DRIVER_ATTR_RO(modules); +static DRIVER_ATTR_RO(components); static DRIVER_ATTR_WO(add_link); static DRIVER_ATTR_WO(remove_link); -static struct attribute *module_attrs[] = { +static struct attribute *mc_attrs[] = { DRV_ATTR(links), - DRV_ATTR(modules), + DRV_ATTR(components), DRV_ATTR(add_link), DRV_ATTR(remove_link), NULL, }; -static struct attribute_group module_attr_group = { - .attrs = module_attrs, +static struct attribute_group mc_attr_group = { + .attrs = mc_attrs, }; -static const struct attribute_group *module_attr_groups[] = { - &module_attr_group, +static const struct attribute_group *mc_attr_groups[] = { + &mc_attr_group, NULL, }; @@ -1552,7 +1552,7 @@ static int __init most_init(void) mc.bus.match = most_match, mc.drv.name = "most_core", mc.drv.bus = &mc.bus, - mc.drv.groups = module_attr_groups; + mc.drv.groups = mc_attr_groups; err = bus_register(&mc.bus); if (err) { -- cgit From 7e0d35423350d118982096d151140616bbad147d Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:05:14 +0100 Subject: staging: most: core: fix formatting This patch fixes coding style violations. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.c | 7 +------ drivers/staging/most/video/video.c | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index 600007e7f55c..839e6cf4fbc0 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -277,7 +277,6 @@ static ssize_t set_number_of_buffers_store(struct device *dev, size_t count) { struct most_channel *c = to_channel(dev); - int ret = kstrtou16(buf, 0, &c->cfg.num_buffers); if (ret) @@ -663,13 +662,11 @@ inline int link_channel_to_component(struct most_channel *c, return -ENOSPC; *comp_ptr = comp; - ret = comp->probe_channel(c->iface, c->channel_id, - &c->cfg, comp_param); + ret = comp->probe_channel(c->iface, c->channel_id, &c->cfg, comp_param); if (ret) { *comp_ptr = NULL; return ret; } - return 0; } @@ -710,7 +707,6 @@ static ssize_t add_link_store(struct device_driver *drv, size_t max_len = min_t(size_t, len + 1, STRING_SIZE); strlcpy(buffer, buf, max_len); - ret = split_string(buffer, &mdev, &mdev_ch, &comp_name, &comp_param); if (ret) return ret; @@ -728,7 +724,6 @@ static ssize_t add_link_store(struct device_driver *drv, ret = link_channel_to_component(c, comp, comp_param); if (ret) return ret; - return len; } diff --git a/drivers/staging/most/video/video.c b/drivers/staging/most/video/video.c index 4be1e0dc9dd2..098873851646 100644 --- a/drivers/staging/most/video/video.c +++ b/drivers/staging/most/video/video.c @@ -541,7 +541,7 @@ err_unreg: } static int comp_disconnect_channel(struct most_interface *iface, - int channel_idx) + int channel_idx) { struct most_video_dev *mdev = get_comp_dev(iface, channel_idx); -- cgit From 8f20f2dca8b390b4fe9e40ed6196c805a3f07b66 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:05:15 +0100 Subject: staging: most: usb: clear functional stall on OUT endpoint For the MOST packet channel there are two dedicated USB endpoints. But internally the hardware has actually one channel for data forwarding from and to MOST. To have the hardware clean up its state machine correctly in case of an error, both USB pipes need to be reset. This patch triggers the host to also clear the OUT endpoint's halt condition in case an IN endpoint has signaled to be stalled. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/usb/usb.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/usb/usb.c b/drivers/staging/most/usb/usb.c index 87e7fec2af8a..1169635454f3 100644 --- a/drivers/staging/most/usb/usb.c +++ b/drivers/staging/most/usb/usb.c @@ -809,6 +809,21 @@ static void wq_clear_halt(struct work_struct *wq_obj) if (usb_clear_halt(mdev->usb_device, pipe)) dev_warn(&mdev->usb_device->dev, "Failed to reset endpoint.\n"); + /* If the functional Stall condition has been set on an + * asynchronous rx channel, we need to clear the tx channel + * too, since the hardware runs its clean-up sequence on both + * channels, as they are physically one on the network. + * + * The USB interface that exposes the asynchronous channels + * contains always two endpoints, and two only. + */ + if (mdev->conf[channel].data_type == MOST_CH_ASYNC && + mdev->conf[channel].direction == MOST_CH_RX) { + int peer = 1 - channel; + int snd_pipe = usb_sndbulkpipe(mdev->usb_device, + mdev->ep_address[peer]); + usb_clear_halt(mdev->usb_device, snd_pipe); + } mdev->is_channel_healthy[channel] = true; most_resume_enqueue(&mdev->iface, channel); mutex_unlock(&mdev->io_mutex); -- cgit From 4e6d561d51f91ad465e127b6b5fabf1f1db01fc5 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:05:16 +0100 Subject: staging: most: core: fix data type This patch fixes the type used to manage the channels of an registered MOST interface. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.h b/drivers/staging/most/core.h index 52db7fa15f65..74a29163b68a 100644 --- a/drivers/staging/most/core.h +++ b/drivers/staging/most/core.h @@ -232,7 +232,7 @@ struct most_interface { struct module *mod; enum most_interface_type interface; const char *description; - int num_channels; + unsigned int num_channels; struct most_channel_capability *channel_vector; int (*configure)(struct most_interface *iface, int channel_idx, struct most_channel_config *channel_config); -- cgit From 3ba5515bf4eecbec08fedde5e35bdabb153c83be Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:05:17 +0100 Subject: staging: most: core: check value returned by match function This patch adds a check for the pointer returned by the function match_component. It is needed to prevent a NULL pointer dereference in case the provided component name does not match any list entry. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index 839e6cf4fbc0..6e7dcb5999bd 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -711,6 +711,8 @@ static ssize_t add_link_store(struct device_driver *drv, if (ret) return ret; comp = match_component(comp_name); + if (!comp) + return -ENODEV; if (!comp_param || *comp_param == 0) { snprintf(devnod_buf, sizeof(devnod_buf), "%s-%s", mdev, mdev_ch); @@ -754,6 +756,8 @@ static ssize_t remove_link_store(struct device_driver *drv, if (ret) return ret; comp = match_component(comp_name); + if (!comp) + return -ENODEV; c = get_channel(mdev, mdev_ch); if (!c) return -ENODEV; -- cgit From 2525ef557c731d463914db2e52fc48fa6a407fe5 Mon Sep 17 00:00:00 2001 From: Andrey Shvetsov Date: Tue, 21 Nov 2017 15:05:18 +0100 Subject: staging: most: update driver usage file This patch keeps the usage file up to date. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- .../staging/most/Documentation/driver_usage.txt | 192 +++++++++++---------- 1 file changed, 105 insertions(+), 87 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/Documentation/driver_usage.txt b/drivers/staging/most/Documentation/driver_usage.txt index a4dc0c348fbc..bb9b4e870199 100644 --- a/drivers/staging/most/Documentation/driver_usage.txt +++ b/drivers/staging/most/Documentation/driver_usage.txt @@ -23,20 +23,29 @@ audio/video streaming. Therefore, the driver perfectly fits to the mission of Automotive Grade Linux to create open source software solutions for automotive applications. -The driver consists basically of three layers. The hardware layer, the -core layer and the application layer. The core layer consists of the core -module only. This module handles the communication flow through all three -layers, the configuration of the driver, the configuration interface -representation in sysfs, and the buffer management. -For each of the other two layers a selection of modules is provided. These -modules can arbitrarily be combined to meet the needs of the desired -system architecture. A module of the hardware layer is referred to as an -HDM (hardware dependent module). Each module of this layer handles exactly -one of the peripheral interfaces of a network interface controller (e.g. -USB, MediaLB, I2C). A module of the application layer is referred to as an -AIM (application interfacing module). The modules of this layer give access -to MOST via one the following ways: character devices, ALSA, Networking or -V4L2. +The MOST driver uses module stacking to divide the associated modules into +three layers. From bottom up these layers are: the adapter layer, the core +layer and the application layer. The core layer implements the MOST +subsystem and consists basically of the module core.c and its API. It +registers the MOST bus with the kernel's device model, handles the data +routing through all three layers, the configuration of the driver, the +representation of the configuration interface in sysfs and the buffer +management. + +For each of the other two layers a set of modules is provided. Those can be +arbitrarily combined with the core to meet the connectivity of the desired +system architecture. + +A module of the adapter layer is basically a device driver for a different +subsystem. It is registered with the core to connect the MOST subsystem to +the attached network interface controller hardware. Hence, a given module +of this layer is designed to handle exactly one of the peripheral +interfaces (e.g. USB, MediaLB, I2C) the hardware provides. + +A module of the application layer is referred to as a core comoponent, +which kind of extends the core by providing connectivity to the user space. +Applications, then, can access a MOST network via character devices, an +ALSA soundcard, a Network adapter or a V4L2 capture device. To physically access MOST, an Intelligent Network Interface Controller (INIC) is needed. For more information on available controllers visit: @@ -44,15 +53,14 @@ www.microchip.com - Section 1.1 Hardware Layer + Section 1.1 Adapter Layer -The hardware layer contains so called hardware dependent modules (HDM). For each -peripheral interface the hardware supports the driver has a suitable module -that handles the interface. - -The HDMs encapsulate the peripheral interface specific knowledge of the driver -and provides an easy way of extending the number of supported interfaces. -Currently the following HDMs are available: +The adapter layer contains a pool of device drivers. For each peripheral +interface the hardware supports there is one suitable module that handles +the interface. Adapter drivers encapsulate the peripheral interface +specific knowledge of the MOST driver stack and provide an easy way of +extending the number of supported interfaces. Currently the following +interfaces are available: 1) MediaLB (DIM2) Host wants to communicate with hardware via MediaLB. @@ -63,26 +71,34 @@ Currently the following HDMs are available: 3) USB Host wants to communicate with the hardware via USB. +Once an adapter driver recognizes a MOST device being attached, it +registers it with the core, which, in turn, assigns the necessary members +of the embedded struct device (e.g. the bus this device belongs to and +attribute groups) and registers it with the kernel's device model. - Section 1.2 Core Layer - -The core layer contains the mostcore module only, which processes the driver -configuration via sysfs, buffer management and data forwarding. + Section 1.2 Core Layer +This layer implements the MOST subsystem. It contains the core module and +the header file most.h that exposes the API of the core. When inserted in +the kernel, it registers the MOST bus_type with the kernel's device model +and registers itself as a device driver for this bus. Besides these meta +tasks the core populates the configuration directory for a registered MOST +device (represented by struct most_interface) in sysfs and processes the +configuration of the device's interface. The core layer also handles the +buffer management and the data/message routing. - Section 1.2 Application Layer -The application layer contains so called application interfacing modules (AIM). -Depending on how the driver should interface to the application, one or more -suitable modules can be selected. + Section 1.3 Application Layer -The AIMs encapsulate the application interface specific knowledge of the driver -and provides access to user space or other kernel subsystems. -Currently the following AIMs are available +This layer contains a pool of device drivers that are components of the +core designed to make up the userspace experience of the MOST driver stack. +Depending on how an application is meant to interface the driver, one or +more modules of this pool can be registered with the core. Currently the +following components are available 1) Character Device - Applications can access the driver by means of character devices. + Userspace can access the driver by means of character devices. 2) Networking Standard networking applications (e.g. iperf) can by used to access @@ -97,84 +113,86 @@ Currently the following AIMs are available used to access the driver via the ALSA subsystem. + Section 2 Usage of the MOST Driver - Section 2 Configuration + Section 2.1 Configuration -See ABI/sysfs-class-most.txt +See ABI/sysfs-bus-most.txt + Section 2.2 Routing Channels - Section 3 USB Padding +To connect a configured channel to a certain core component and make it +accessible for user space applications, the driver attribute 'add_link' is +used. The configuration string passed to it has the following format: -When transceiving synchronous or isochronous data, the number of packets per USB -transaction and the sub-buffer size need to be configured. These values -are needed for the driver to process buffer padding, as expected by hardware, -which is for performance optimization purposes of the USB transmission. + "device_name:channel_name:component_name:link_name[.param]" -When transmitting synchronous data the allocated channel width needs to be -written to 'set_subbuffer_size'. Additionally, the number of MOST frames that -should travel to the host within one USB transaction need to be written to -'packets_per_xact'. +It is the concatenation of up to four substrings separated by a colon. The +substrings contain the names of the MOST interface, the channel, the +component driver and a custom name with which the link is going to be +referenced with. Since some components need additional information, the +link name can be extended with a component-specific parameter (separated by +a dot). In case the character device component is loaded, the handle would +also appear as a device node in the /dev directory. -Internally the synchronous threshold is calculated as follows: +Cdev component example: + $ echo "mdev0:ep_81:cdev:my_rx_channel" >$(DRV_DIR)/add_link - frame_size = set_subbuffer_size * packets_per_xact -In case 'packets_per_xact' is set to 0xFF the maximum number of packets, -allocated within one MOST frame, is calculated that fit into _one_ 512 byte -USB full packet. +Sound component example: - frame_size = floor(MTU_USB / bandwidth_sync) * bandwidth_sync +The sound component needs an additional parameter to determine the audio +resolution that is going to be used. The following formats are available: -This frame_size is the number of synchronous data within an USB transaction, -which renders MTU_USB - frame_size bytes for padding. + - "1x8" (Mono) + - "2x16" (16-bit stereo) + - "2x24" (24-bit stereo) + - "2x32" (32-bit stereo) + - "6x16" (16-bit surround 5.1) -When transmitting isochronous AVP data the desired packet size needs to be -written to 'set_subbuffer_size' and hardware will always expect two isochronous -packets within one USB transaction. This renders + $ echo "mdev0:ep_81:sound:most51_playback.6x16" >$(DRV_DIR)/add_link - MTU_USB - (2 * set_subbuffer_size) -bytes for padding. - -Note that at least 2 times set_subbuffer_size bytes for isochronous data or -set_subbuffer_size times packts_per_xact bytes for synchronous data need to be -put in the transmission buffer and passed to the driver. -Since HDMs are allowed to change a chosen configuration to best fit its -constraints, it is recommended to always double check the configuration and read -back the previously written files. + Section 2.3 USB Padding +When transceiving synchronous or isochronous data, the number of packets +per USB transaction and the sub-buffer size need to be configured. These +values are needed for the driver to process buffer padding, as expected by +hardware, which is for performance optimization purposes of the USB +transmission. +When transmitting synchronous data the allocated channel width needs to be +written to 'set_subbuffer_size'. Additionally, the number of MOST frames +that should travel to the host within one USB transaction need to be +written to 'packets_per_xact'. - Section 4 Routing Channels +The driver, then, calculates the synchronous threshold as follows: -To connect a channel that has been configured as outlined above to an AIM and -make it accessible to user space applications, the attribute file 'add_link' is -used. To actually bind a channel to the AIM a string needs to be written to the -file that complies with the following syntax: + frame_size = set_subbuffer_size * packets_per_xact - "most_device:channel_name:link_name[.param]" +In case 'packets_per_xact' is set to 0xFF the maximum number of packets, +allocated within one MOST frame, is calculated that fit into _one_ 512 byte +USB full packet. -The example above links the channel "channel_name" of the device "most_device" -to the AIM. In case the AIM interfaces the VFS this would also create a device -node "link_name" in the /dev directory. The parameter "param" is an AIM dependent -string, which can be omitted in case the used AIM does not make any use of it. + frame_size = floor(MTU_USB / bandwidth_sync) * bandwidth_sync -Cdev AIM example: - $ echo "mdev0:ep_81:my_rx_channel" >add_link - $ echo "mdev0:ep_81" >add_link +This frame_size is the number of synchronous data within an USB +transaction, which renders MTU_USB - frame_size bytes for padding. +When transmitting isochronous AVP data the desired packet size needs to be +written to 'set_subbuffer_size' and hardware will always expect two +isochronous packets within one USB transaction. This renders -Sound/ALSA AIM example: + MTU_USB - (2 * set_subbuffer_size) -The sound/ALSA AIM needs an additional parameter to determine the audio resolution -that is going to be used. The following strings can be used: +bytes for padding. - - "1x8" (Mono) - - "2x16" (16-bit stereo) - - "2x24" (24-bit stereo) - - "2x32" (32-bit stereo) +Note that at least (2 * set_subbuffer_size) bytes for isochronous data or +(set_subbuffer_size * packts_per_xact) bytes for synchronous data need to +be put in the transmission buffer and passed to the driver. - $ echo "mdev0:ep_81:audio_rx.2x16" >add_link - $ echo "mdev0:ep_81" >add_link +Since adapter drivers are allowed to change a chosen configuration to best +fit its constraints, it is recommended to always double check the +configuration and read back the previously written files. -- cgit From 1fd923f38610a802f4b6f4dc30ada9c80cd52d9e Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:05:19 +0100 Subject: staging: most: cdev: replace function prefix This patch replaces the function prefixes aim_* with comp_*. It is needed to complete the process of changing the module designator from AIM to Component. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/cdev/cdev.c | 58 ++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 29 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/cdev/cdev.c b/drivers/staging/most/cdev/cdev.c index 266e2bb6ecf7..6f58eee5c8d5 100644 --- a/drivers/staging/most/cdev/cdev.c +++ b/drivers/staging/most/cdev/cdev.c @@ -106,14 +106,14 @@ static void destroy_channel(struct comp_channel *c) } /** - * aim_open - implements the syscall to open the device + * comp_open - implements the syscall to open the device * @inode: inode pointer * @filp: file pointer * * This stores the channel pointer in the private data field of * the file structure and activates the channel within the core. */ -static int aim_open(struct inode *inode, struct file *filp) +static int comp_open(struct inode *inode, struct file *filp) { struct comp_channel *c; int ret; @@ -151,13 +151,13 @@ static int aim_open(struct inode *inode, struct file *filp) } /** - * aim_close - implements the syscall to close the device + * comp_close - implements the syscall to close the device * @inode: inode pointer * @filp: file pointer * * This stops the channel within the core. */ -static int aim_close(struct inode *inode, struct file *filp) +static int comp_close(struct inode *inode, struct file *filp) { struct comp_channel *c = to_channel(inode->i_cdev); @@ -176,14 +176,14 @@ static int aim_close(struct inode *inode, struct file *filp) } /** - * aim_write - implements the syscall to write to the device + * comp_write - implements the syscall to write to the device * @filp: file pointer * @buf: pointer to user buffer * @count: number of bytes to write * @offset: offset from where to start writing */ -static ssize_t aim_write(struct file *filp, const char __user *buf, - size_t count, loff_t *offset) +static ssize_t comp_write(struct file *filp, const char __user *buf, + size_t count, loff_t *offset) { int ret; size_t to_copy, left; @@ -230,14 +230,14 @@ unlock: } /** - * aim_read - implements the syscall to read from the device + * comp_read - implements the syscall to read from the device * @filp: file pointer * @buf: pointer to user buffer * @count: number of bytes to read * @offset: offset from where to start reading */ static ssize_t -aim_read(struct file *filp, char __user *buf, size_t count, loff_t *offset) +comp_read(struct file *filp, char __user *buf, size_t count, loff_t *offset) { size_t to_copy, not_copied, copied; struct mbo *mbo; @@ -281,7 +281,7 @@ aim_read(struct file *filp, char __user *buf, size_t count, loff_t *offset) return copied; } -static unsigned int aim_poll(struct file *filp, poll_table *wait) +static unsigned int comp_poll(struct file *filp, poll_table *wait) { struct comp_channel *c = filp->private_data; unsigned int mask = 0; @@ -303,22 +303,22 @@ static unsigned int aim_poll(struct file *filp, poll_table *wait) */ static const struct file_operations channel_fops = { .owner = THIS_MODULE, - .read = aim_read, - .write = aim_write, - .open = aim_open, - .release = aim_close, - .poll = aim_poll, + .read = comp_read, + .write = comp_write, + .open = comp_open, + .release = comp_close, + .poll = comp_poll, }; /** - * aim_disconnect_channel - disconnect a channel + * comp_disconnect_channel - disconnect a channel * @iface: pointer to interface instance * @channel_id: channel index * * This frees allocated memory and removes the cdev that represents this * channel in user space. */ -static int aim_disconnect_channel(struct most_interface *iface, int channel_id) +static int comp_disconnect_channel(struct most_interface *iface, int channel_id) { struct comp_channel *c; @@ -348,13 +348,13 @@ static int aim_disconnect_channel(struct most_interface *iface, int channel_id) } /** - * aim_rx_completion - completion handler for rx channels + * comp_rx_completion - completion handler for rx channels * @mbo: pointer to buffer object that has completed * * This searches for the channel linked to this MBO and stores it in the local * fifo buffer. */ -static int aim_rx_completion(struct mbo *mbo) +static int comp_rx_completion(struct mbo *mbo) { struct comp_channel *c; @@ -381,13 +381,13 @@ static int aim_rx_completion(struct mbo *mbo) } /** - * aim_tx_completion - completion handler for tx channels + * comp_tx_completion - completion handler for tx channels * @iface: pointer to interface instance * @channel_id: channel index/ID * * This wakes sleeping processes in the wait-queue. */ -static int aim_tx_completion(struct most_interface *iface, int channel_id) +static int comp_tx_completion(struct most_interface *iface, int channel_id) { struct comp_channel *c; @@ -408,7 +408,7 @@ static int aim_tx_completion(struct most_interface *iface, int channel_id) } /** - * aim_probe - probe function of the driver module + * comp_probe - probe function of the driver module * @iface: pointer to interface instance * @channel_id: channel index/ID * @cfg: pointer to actual channel configuration @@ -418,8 +418,8 @@ static int aim_tx_completion(struct most_interface *iface, int channel_id) * * Returns 0 on success or error code otherwise. */ -static int aim_probe(struct most_interface *iface, int channel_id, - struct most_channel_config *cfg, char *name) +static int comp_probe(struct most_interface *iface, int channel_id, + struct most_channel_config *cfg, char *name) { struct comp_channel *c; unsigned long cl_flags; @@ -427,7 +427,7 @@ static int aim_probe(struct most_interface *iface, int channel_id, int current_minor; if ((!iface) || (!cfg) || (!name)) { - pr_info("Probing AIM with bad arguments"); + pr_info("Probing component with bad arguments"); return -EINVAL; } c = get_channel(iface, channel_id); @@ -491,10 +491,10 @@ error_alloc_channel: static struct core_component cdev_comp = { .name = "cdev", - .probe_channel = aim_probe, - .disconnect_channel = aim_disconnect_channel, - .rx_completion = aim_rx_completion, - .tx_completion = aim_tx_completion, + .probe_channel = comp_probe, + .disconnect_channel = comp_disconnect_channel, + .rx_completion = comp_rx_completion, + .tx_completion = comp_tx_completion, }; static int __init mod_init(void) -- cgit From c73d915dd293a42f45df1d1c19fc85f93ba48784 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:05:20 +0100 Subject: staging: most: cdev: bundle module variables in structure This patch creates the structure comp to put the module variables for encapsulation purposes. For an improved readability some variables are renamed. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/cdev/cdev.c | 88 ++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 45 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/cdev/cdev.c b/drivers/staging/most/cdev/cdev.c index 6f58eee5c8d5..dd9456fd2cd6 100644 --- a/drivers/staging/most/cdev/cdev.c +++ b/drivers/staging/most/cdev/cdev.c @@ -18,11 +18,13 @@ #include #include "most/core.h" -static dev_t comp_devno; -static struct class *comp_class; -static struct ida minor_id; -static unsigned int major; -static struct core_component cdev_comp; +static struct cdev_component { + dev_t devno; + struct ida minor_id; + unsigned int major; + struct class *class; + struct core_component cc; +} comp; struct comp_channel { wait_queue_head_t wq; @@ -46,13 +48,13 @@ static spinlock_t ch_list_lock; static inline bool ch_has_mbo(struct comp_channel *c) { - return channel_has_mbo(c->iface, c->channel_id, &cdev_comp) > 0; + return channel_has_mbo(c->iface, c->channel_id, &comp.cc) > 0; } static inline bool ch_get_mbo(struct comp_channel *c, struct mbo **mbo) { if (!kfifo_peek(&c->fifo, mbo)) { - *mbo = most_get_mbo(c->iface, c->channel_id, &cdev_comp); + *mbo = most_get_mbo(c->iface, c->channel_id, &comp.cc); if (*mbo) kfifo_in(&c->fifo, mbo, 1); } @@ -84,14 +86,14 @@ static void stop_channel(struct comp_channel *c) while (kfifo_out((struct kfifo *)&c->fifo, &mbo, 1)) most_put_mbo(mbo); - most_stop_channel(c->iface, c->channel_id, &cdev_comp); + most_stop_channel(c->iface, c->channel_id, &comp.cc); } static void destroy_cdev(struct comp_channel *c) { unsigned long flags; - device_destroy(comp_class, c->devno); + device_destroy(comp.class, c->devno); cdev_del(&c->cdev); spin_lock_irqsave(&ch_list_lock, flags); list_del(&c->list); @@ -100,7 +102,7 @@ static void destroy_cdev(struct comp_channel *c) static void destroy_channel(struct comp_channel *c) { - ida_simple_remove(&minor_id, MINOR(c->devno)); + ida_simple_remove(&comp.minor_id, MINOR(c->devno)); kfifo_free(&c->fifo); kfree(c); } @@ -143,7 +145,7 @@ static int comp_open(struct inode *inode, struct file *filp) } c->mbo_offs = 0; - ret = most_start_channel(c->iface, c->channel_id, &cdev_comp); + ret = most_start_channel(c->iface, c->channel_id, &comp.cc); if (!ret) c->access_ref = 1; mutex_unlock(&c->io_mutex); @@ -434,7 +436,7 @@ static int comp_probe(struct most_interface *iface, int channel_id, if (c) return -EEXIST; - current_minor = ida_simple_get(&minor_id, 0, 0, GFP_KERNEL); + current_minor = ida_simple_get(&comp.minor_id, 0, 0, GFP_KERNEL); if (current_minor < 0) return current_minor; @@ -444,7 +446,7 @@ static int comp_probe(struct most_interface *iface, int channel_id, goto error_alloc_channel; } - c->devno = MKDEV(major, current_minor); + c->devno = MKDEV(comp.major, current_minor); cdev_init(&c->cdev, &channel_fops); c->cdev.owner = THIS_MODULE; cdev_add(&c->cdev, c->devno, 1); @@ -464,11 +466,7 @@ static int comp_probe(struct most_interface *iface, int channel_id, spin_lock_irqsave(&ch_list_lock, cl_flags); list_add_tail(&c->list, &channel_list); spin_unlock_irqrestore(&ch_list_lock, cl_flags); - c->dev = device_create(comp_class, - NULL, - c->devno, - NULL, - "%s", name); + c->dev = device_create(comp.class, NULL, c->devno, NULL, "%s", name); if (IS_ERR(c->dev)) { retval = PTR_ERR(c->dev); @@ -485,16 +483,18 @@ error_alloc_kfifo: cdev_del(&c->cdev); kfree(c); error_alloc_channel: - ida_simple_remove(&minor_id, current_minor); + ida_simple_remove(&comp.minor_id, current_minor); return retval; } -static struct core_component cdev_comp = { - .name = "cdev", - .probe_channel = comp_probe, - .disconnect_channel = comp_disconnect_channel, - .rx_completion = comp_rx_completion, - .tx_completion = comp_tx_completion, +static struct cdev_component comp = { + .cc = { + .name = "cdev", + .probe_channel = comp_probe, + .disconnect_channel = comp_disconnect_channel, + .rx_completion = comp_rx_completion, + .tx_completion = comp_tx_completion, + }, }; static int __init mod_init(void) @@ -503,32 +503,30 @@ static int __init mod_init(void) pr_info("init()\n"); + comp.class = class_create(THIS_MODULE, "most_cdev"); + if (IS_ERR(comp.class)) { + pr_info("No udev support.\n"); + return PTR_ERR(comp.class); + } + INIT_LIST_HEAD(&channel_list); spin_lock_init(&ch_list_lock); - ida_init(&minor_id); + ida_init(&comp.minor_id); - err = alloc_chrdev_region(&comp_devno, 0, 50, "cdev"); + err = alloc_chrdev_region(&comp.devno, 0, 50, "cdev"); if (err < 0) goto dest_ida; - major = MAJOR(comp_devno); - - comp_class = class_create(THIS_MODULE, "most_cdev_comp"); - if (IS_ERR(comp_class)) { - pr_err("no udev support\n"); - err = PTR_ERR(comp_class); - goto free_cdev; - } - err = most_register_component(&cdev_comp); + comp.major = MAJOR(comp.devno); + err = most_register_component(&comp.cc); if (err) - goto dest_class; + goto free_cdev; return 0; -dest_class: - class_destroy(comp_class); free_cdev: - unregister_chrdev_region(comp_devno, 1); + unregister_chrdev_region(comp.devno, 1); dest_ida: - ida_destroy(&minor_id); + ida_destroy(&comp.minor_id); + class_destroy(comp.class); return err; } @@ -538,15 +536,15 @@ static void __exit mod_exit(void) pr_info("exit module\n"); - most_deregister_component(&cdev_comp); + most_deregister_component(&comp.cc); list_for_each_entry_safe(c, tmp, &channel_list, list) { destroy_cdev(c); destroy_channel(c); } - class_destroy(comp_class); - unregister_chrdev_region(comp_devno, 1); - ida_destroy(&minor_id); + unregister_chrdev_region(comp.devno, 1); + ida_destroy(&comp.minor_id); + class_destroy(comp.class); } module_init(mod_init); -- cgit From eb9e2bc80d1451b116f9964a84a49f89d38b1e05 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:05:21 +0100 Subject: staging: most: core: remove class generation This patch stops the core from generating a module owned class and registering it with the kernel. It is needed, because there is no need for a default MOST class to be present in the kernel. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index 6e7dcb5999bd..bfd1a028ccad 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -33,7 +33,6 @@ static struct mostcore { struct device dev; struct device_driver drv; struct bus_type bus; - struct class *class; struct list_head comp_list; } mc; @@ -1558,17 +1557,10 @@ static int __init most_init(void) pr_info("Cannot register most bus\n"); return err; } - mc.class = class_create(THIS_MODULE, "most"); - if (IS_ERR(mc.class)) { - pr_info("No udev support.\n"); - err = PTR_ERR(mc.class); - goto exit_bus; - } - err = driver_register(&mc.drv); if (err) { pr_info("Cannot register core driver\n"); - goto exit_class; + goto exit_bus; } mc.dev.init_name = "most_bus"; mc.dev.release = release_most_sub; @@ -1581,8 +1573,6 @@ static int __init most_init(void) exit_driver: driver_unregister(&mc.drv); -exit_class: - class_destroy(mc.class); exit_bus: bus_unregister(&mc.bus); return err; @@ -1593,7 +1583,6 @@ static void __exit most_exit(void) pr_info("exit core module\n"); device_unregister(&mc.dev); driver_unregister(&mc.drv); - class_destroy(mc.class); bus_unregister(&mc.bus); ida_destroy(&mdev_id); } -- cgit From e7e3ce04588c7f4919627f0742bc6cbe832036a4 Mon Sep 17 00:00:00 2001 From: Andrey Shvetsov Date: Tue, 21 Nov 2017 15:05:22 +0100 Subject: staging: most: core: fix list traversing This patch fixes the offset and data handling when traversing the list of devices that are attached to the bus. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index bfd1a028ccad..c1fba5ba7541 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -529,10 +529,16 @@ static struct core_component *match_component(char *name) return NULL; } +struct show_links_data { + int offs; + char *buf; +}; + int print_links(struct device *dev, void *data) { - int offs = 0; - char *buf = data; + struct show_links_data *d = data; + int offs = d->offs; + char *buf = d->buf; struct most_channel *c; struct most_interface *iface = to_most_interface(dev); @@ -554,13 +560,16 @@ int print_links(struct device *dev, void *data) dev_name(&c->dev)); } } + d->offs = offs; return 0; } static ssize_t links_show(struct device_driver *drv, char *buf) { - bus_for_each_dev(&mc.bus, NULL, buf, print_links); - return strlen(buf); + struct show_links_data d = { .buf = buf }; + + bus_for_each_dev(&mc.bus, NULL, &d, print_links); + return d.offs; } static ssize_t components_show(struct device_driver *drv, char *buf) -- cgit From b7ed2ff0bb4a896ec4e470a1c4faa88de1a589cc Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:05:23 +0100 Subject: staging: most: add ABI documentation This patchg adds the sysfs-bus-most.txt file to the source tree. It is needed to have an ABI description of the driver's sysfs interface. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- .../most/Documentation/ABI/sysfs-bus-most.txt | 313 +++++++++++++++++++++ 1 file changed, 313 insertions(+) create mode 100644 drivers/staging/most/Documentation/ABI/sysfs-bus-most.txt (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/Documentation/ABI/sysfs-bus-most.txt b/drivers/staging/most/Documentation/ABI/sysfs-bus-most.txt new file mode 100644 index 000000000000..d8fa841e3742 --- /dev/null +++ b/drivers/staging/most/Documentation/ABI/sysfs-bus-most.txt @@ -0,0 +1,313 @@ +What: /sys/bus/most/devices/.../description +Date: March 2017 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + Provides information about the interface type and the physical + location of the device. Hardware attached via USB, for instance, + might return +Users: + +What: /sys/bus/most/devices/.../interface +Date: March 2017 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + Indicates the type of peripheral interface the device uses. +Users: + +What: /sys/bus/most/devices/.../dci +Date: June 2016 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + If the network interface controller is attached via USB, a dci + directory is created that allows applications to read and + write the controller's DCI registers. +Users: + +What: /sys/bus/most/devices/.../dci/arb_address +Date: June 2016 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + This is used to set an arbitrary DCI register address an + application wants to read from or write to. +Users: + +What: /sys/bus/most/devices/.../dci/arb_value +Date: June 2016 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + This is used to read and write the DCI register whose address + is stored in arb_address. +Users: + +What: /sys/bus/most/devices/.../dci/mep_eui48_hi +Date: June 2016 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + This is used to check and configure the MAC address. +Users: + +What: /sys/bus/most/devices/.../dci/mep_eui48_lo +Date: June 2016 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + This is used to check and configure the MAC address. +Users: + +What: /sys/bus/most/devices/.../dci/mep_eui48_mi +Date: June 2016 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + This is used to check and configure the MAC address. +Users: + +What: /sys/bus/most/devices/.../dci/mep_filter +Date: June 2016 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + This is used to check and configure the MEP filter address. +Users: + +What: /sys/bus/most/devices/.../dci/mep_hash0 +Date: June 2016 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + This is used to check and configure the MEP hash table. +Users: + +What: /sys/bus/most/devices/.../dci/mep_hash1 +Date: June 2016 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + This is used to check and configure the MEP hash table. +Users: + +What: /sys/bus/most/devices/.../dci/mep_hash2 +Date: June 2016 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + This is used to check and configure the MEP hash table. +Users: + +What: /sys/bus/most/devices/.../dci/mep_hash3 +Date: June 2016 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + This is used to check and configure the MEP hash table. +Users: + +What: /sys/bus/most/devices/.../dci/ni_state +Date: June 2016 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + Indicates the current network interface state. +Users: + +What: /sys/bus/most/devices/.../dci/node_address +Date: June 2016 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + Indicates the current node address. +Users: + +What: /sys/bus/most/devices/.../dci/node_position +Date: June 2016 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + Indicates the current node position. +Users: + +What: /sys/bus/most/devices/.../dci/packet_bandwidth +Date: June 2016 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + Indicates the configured packet bandwidth. +Users: + +What: /sys/bus/most/devices/.../dci/sync_ep +Date: June 2016 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + Triggers the controller's synchronization process for a certain + endpoint. +Users: + +What: /sys/bus/most/devices/...// +Date: March 2017 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + For every channel of the device a directory is created, whose + name is dictated by the HDM. This enables an application to + collect information about the channel's capabilities and + configure it. +Users: + +What: /sys/bus/most/devices/...//available_datatypes +Date: March 2017 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + Indicates the data types the current channel can transport. +Users: + +What: /sys/bus/most/devices/...//available_directions +Date: March 2017 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + Indicates the directions the current channel is capable of. +Users: + +What: /sys/bus/most/devices/...//number_of_packet_buffers +Date: March 2017 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + Indicates the number of packet buffers the current channel can + handle. +Users: + +What: /sys/bus/most/devices/...//number_of_stream_buffers +Date: March 2017 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + Indicates the number of streaming buffers the current channel can + handle. +Users: + +What: /sys/bus/most/devices/...//size_of_packet_buffer +Date: March 2017 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + Indicates the size of a packet buffer the current channel can + handle. +Users: + +What: /sys/bus/most/devices/...//size_of_stream_buffer +Date: March 2017 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + Indicates the size of a streaming buffer the current channel can + handle. +Users: + +What: /sys/bus/most/devices/...//set_number_of_buffers +Date: March 2017 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + This is to configure the number of buffers of the current channel. +Users: + +What: /sys/bus/most/devices/...//set_buffer_size +Date: March 2017 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + This is to configure the size of a buffer of the current channel. +Users: + +What: /sys/bus/most/devices/...//set_direction +Date: March 2017 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + This is to configure the direction of the current channel. + The following strings will be accepted: + 'dir_tx', + 'dir_rx' +Users: + +What: /sys/bus/most/devices/...//set_datatype +Date: March 2017 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + This is to configure the data type of the current channel. + The following strings will be accepted: + 'control', + 'async', + 'sync', + 'isoc_avp' +Users: + +What: /sys/bus/most/devices/...//set_subbuffer_size +Date: March 2017 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + This is to configure the subbuffer size of the current channel. +Users: + +What: /sys/bus/most/devices/...//set_packets_per_xact +Date: March 2017 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + This is to configure the number of packets per transaction of + the current channel. This is only needed network interface + controller is attached via USB. +Users: + +What: /sys/bus/most/devices/...//channel_starving +Date: March 2017 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + Indicates whether current channel ran out of buffers. +Users: + +What: /sys/bus/most/drivers/mostcore/add_link +Date: March 2017 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + This is used to link a channel to a component of the + mostcore. A link created by writing to this file is + referred to as pipe. +Users: + +What: /sys/bus/most/drivers/mostcore/remove_link +Date: March 2017 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + This is used to unlink a channel from a component. +Users: + +What: /sys/bus/most/drivers/mostcore/components +Date: March 2017 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + This is used to retrieve a list of registered components. +Users: + +What: /sys/bus/most/drivers/mostcore/links +Date: March 2017 +KernelVersion: 4.15 +Contact: Christian Gromm +Description: + This is used to retrieve a list of established links. +Users: -- cgit From 3d9c54b5f9177049f9edeb1227bdab7adf77686f Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Tue, 21 Nov 2017 15:05:24 +0100 Subject: staging: most: usb: fix show/store function names This patch renames the show/store functions of the USB module. It is needed to make the module meet the established naming convention. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/usb/usb.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/usb/usb.c b/drivers/staging/most/usb/usb.c index 1169635454f3..c72e9c3bacec 100644 --- a/drivers/staging/most/usb/usb.c +++ b/drivers/staging/most/usb/usb.c @@ -887,7 +887,7 @@ static int get_stat_reg_addr(const struct regs *regs, int size, #define get_static_reg_addr(regs, name, reg_addr) \ get_stat_reg_addr(regs, ARRAY_SIZE(regs), name, reg_addr) -static ssize_t show_value(struct device *dev, struct device_attribute *attr, +static ssize_t value_show(struct device *dev, struct device_attribute *attr, char *buf) { const char *name = attr->attr.name; @@ -912,7 +912,7 @@ static ssize_t show_value(struct device *dev, struct device_attribute *attr, return snprintf(buf, PAGE_SIZE, "%04x\n", val); } -static ssize_t store_value(struct device *dev, struct device_attribute *attr, +static ssize_t value_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { u16 val; @@ -945,21 +945,21 @@ static ssize_t store_value(struct device *dev, struct device_attribute *attr, return count; } -DEVICE_ATTR(ni_state, 0444, show_value, NULL); -DEVICE_ATTR(packet_bandwidth, 0444, show_value, NULL); -DEVICE_ATTR(node_address, 0444, show_value, NULL); -DEVICE_ATTR(node_position, 0444, show_value, NULL); -DEVICE_ATTR(sync_ep, 0200, NULL, store_value); -DEVICE_ATTR(mep_filter, 0644, show_value, store_value); -DEVICE_ATTR(mep_hash0, 0644, show_value, store_value); -DEVICE_ATTR(mep_hash1, 0644, show_value, store_value); -DEVICE_ATTR(mep_hash2, 0644, show_value, store_value); -DEVICE_ATTR(mep_hash3, 0644, show_value, store_value); -DEVICE_ATTR(mep_eui48_hi, 0644, show_value, store_value); -DEVICE_ATTR(mep_eui48_mi, 0644, show_value, store_value); -DEVICE_ATTR(mep_eui48_lo, 0644, show_value, store_value); -DEVICE_ATTR(arb_address, 0644, show_value, store_value); -DEVICE_ATTR(arb_value, 0644, show_value, store_value); +DEVICE_ATTR(ni_state, 0444, value_show, NULL); +DEVICE_ATTR(packet_bandwidth, 0444, value_show, NULL); +DEVICE_ATTR(node_address, 0444, value_show, NULL); +DEVICE_ATTR(node_position, 0444, value_show, NULL); +DEVICE_ATTR(sync_ep, 0200, NULL, value_store); +DEVICE_ATTR(mep_filter, 0644, value_show, value_store); +DEVICE_ATTR(mep_hash0, 0644, value_show, value_store); +DEVICE_ATTR(mep_hash1, 0644, value_show, value_store); +DEVICE_ATTR(mep_hash2, 0644, value_show, value_store); +DEVICE_ATTR(mep_hash3, 0644, value_show, value_store); +DEVICE_ATTR(mep_eui48_hi, 0644, value_show, value_store); +DEVICE_ATTR(mep_eui48_mi, 0644, value_show, value_store); +DEVICE_ATTR(mep_eui48_lo, 0644, value_show, value_store); +DEVICE_ATTR(arb_address, 0644, value_show, value_store); +DEVICE_ATTR(arb_value, 0644, value_show, value_store); static struct attribute *dci_attrs[] = { &dev_attr_ni_state.attr, -- cgit From 8fc2a66b094efc3d0ac04d20f25b259e9b654bcb Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Mon, 27 Nov 2017 15:20:04 +0100 Subject: staging: most: fix Makefile This patch fixes the names of the CONFIG symbols and the subfolders make is supposed to enter in order to build the selected modules. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/Makefile | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/Makefile b/drivers/staging/most/Makefile index 7f6aa9c515fa..f8bcf488ecf2 100644 --- a/drivers/staging/most/Makefile +++ b/drivers/staging/most/Makefile @@ -3,10 +3,10 @@ obj-$(CONFIG_MOST) += most_core.o most_core-y := core.o ccflags-y += -Idrivers/staging/ -obj-$(CONFIG_AIM_CDEV) += aim-cdev/ -obj-$(CONFIG_AIM_NETWORK) += aim-network/ -obj-$(CONFIG_AIM_SOUND) += aim-sound/ -obj-$(CONFIG_AIM_V4L2) += aim-v4l2/ -obj-$(CONFIG_HDM_DIM2) += hdm-dim2/ -obj-$(CONFIG_HDM_I2C) += hdm-i2c/ -obj-$(CONFIG_HDM_USB) += hdm-usb/ +obj-$(CONFIG_MOST_CDEV) += cdev/ +obj-$(CONFIG_MOST_NET) += net/ +obj-$(CONFIG_MOST_SOUND) += sound/ +obj-$(CONFIG_MOST_VIDEO) += video/ +obj-$(CONFIG_MOST_DIM2) += dim2/ +obj-$(CONFIG_MOST_I2C) += i2c/ +obj-$(CONFIG_MOST_USB) += usb/ -- cgit From fc10bf7e59008f401d9bd4248721f16bd6c98439 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Mon, 27 Nov 2017 15:20:05 +0100 Subject: staging: most: remove legacy folders This patch removes the legacy folders of the modules. It is needed to clean up the driver's source tree. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/aim-cdev/Makefile | 4 ---- drivers/staging/most/aim-network/Makefile | 4 ---- drivers/staging/most/aim-sound/Makefile | 4 ---- drivers/staging/most/aim-v4l2/Makefile | 5 ----- drivers/staging/most/hdm-dim2/Makefile | 5 ----- drivers/staging/most/hdm-i2c/Makefile | 3 --- drivers/staging/most/hdm-usb/Makefile | 4 ---- 7 files changed, 29 deletions(-) delete mode 100644 drivers/staging/most/aim-cdev/Makefile delete mode 100644 drivers/staging/most/aim-network/Makefile delete mode 100644 drivers/staging/most/aim-sound/Makefile delete mode 100644 drivers/staging/most/aim-v4l2/Makefile delete mode 100644 drivers/staging/most/hdm-dim2/Makefile delete mode 100644 drivers/staging/most/hdm-i2c/Makefile delete mode 100644 drivers/staging/most/hdm-usb/Makefile (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/aim-cdev/Makefile b/drivers/staging/most/aim-cdev/Makefile deleted file mode 100644 index b7afcb40997d..000000000000 --- a/drivers/staging/most/aim-cdev/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -obj-$(CONFIG_AIM_CDEV) += aim_cdev.o - -aim_cdev-objs := cdev.o -ccflags-y += -Idrivers/staging/ diff --git a/drivers/staging/most/aim-network/Makefile b/drivers/staging/most/aim-network/Makefile deleted file mode 100644 index a874aac8d285..000000000000 --- a/drivers/staging/most/aim-network/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -obj-$(CONFIG_AIM_NETWORK) += aim_network.o - -aim_network-objs := networking.o -ccflags-y += -Idrivers/staging/ diff --git a/drivers/staging/most/aim-sound/Makefile b/drivers/staging/most/aim-sound/Makefile deleted file mode 100644 index d41b85baa83f..000000000000 --- a/drivers/staging/most/aim-sound/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -obj-$(CONFIG_AIM_SOUND) += aim_sound.o - -aim_sound-objs := sound.o -ccflags-y += -Idrivers/staging/ diff --git a/drivers/staging/most/aim-v4l2/Makefile b/drivers/staging/most/aim-v4l2/Makefile deleted file mode 100644 index a8e8b4930355..000000000000 --- a/drivers/staging/most/aim-v4l2/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -obj-$(CONFIG_AIM_V4L2) += aim_v4l2.o - -aim_v4l2-objs := video.o - -ccflags-y += -Idrivers/staging/ diff --git a/drivers/staging/most/hdm-dim2/Makefile b/drivers/staging/most/hdm-dim2/Makefile deleted file mode 100644 index b66492bf7674..000000000000 --- a/drivers/staging/most/hdm-dim2/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -obj-$(CONFIG_HDM_DIM2) += hdm_dim2.o - -hdm_dim2-objs := dim2_hdm.o dim2_hal.o dim2_sysfs.o -ccflags-y += -Idrivers/staging/ -ccflags-y += -Idrivers/staging/most/aim-network/ diff --git a/drivers/staging/most/hdm-i2c/Makefile b/drivers/staging/most/hdm-i2c/Makefile deleted file mode 100644 index 6ddc78aae3d9..000000000000 --- a/drivers/staging/most/hdm-i2c/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-$(CONFIG_HDM_I2C) += hdm_i2c.o - -ccflags-y += -Idrivers/staging/ diff --git a/drivers/staging/most/hdm-usb/Makefile b/drivers/staging/most/hdm-usb/Makefile deleted file mode 100644 index 4fea7c2a7755..000000000000 --- a/drivers/staging/most/hdm-usb/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -obj-$(CONFIG_HDM_USB) += hdm_usb.o - -ccflags-y += -Idrivers/staging/ -ccflags-y += -Idrivers/staging/most/aim-network/ -- cgit From f15e3ad3ef22de652f5eccf34938f981af8672d3 Mon Sep 17 00:00:00 2001 From: Christian Gromm Date: Mon, 27 Nov 2017 15:20:06 +0100 Subject: staging: most: make DEVICE_ATTR structures static In order to limit the scope of the DEVICE_ATTR structure this patch adds the keywork static. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/dim2/sysfs.c | 2 +- drivers/staging/most/usb/usb.c | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/dim2/sysfs.c b/drivers/staging/most/dim2/sysfs.c index 7ead7030c6b8..c85b2cdcdca3 100644 --- a/drivers/staging/most/dim2/sysfs.c +++ b/drivers/staging/most/dim2/sysfs.c @@ -21,7 +21,7 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%s\n", state ? "locked" : ""); } -DEVICE_ATTR_RO(state); +static DEVICE_ATTR_RO(state); static struct attribute *dev_attrs[] = { &dev_attr_state.attr, diff --git a/drivers/staging/most/usb/usb.c b/drivers/staging/most/usb/usb.c index c72e9c3bacec..8d23075ae165 100644 --- a/drivers/staging/most/usb/usb.c +++ b/drivers/staging/most/usb/usb.c @@ -945,21 +945,21 @@ static ssize_t value_store(struct device *dev, struct device_attribute *attr, return count; } -DEVICE_ATTR(ni_state, 0444, value_show, NULL); -DEVICE_ATTR(packet_bandwidth, 0444, value_show, NULL); -DEVICE_ATTR(node_address, 0444, value_show, NULL); -DEVICE_ATTR(node_position, 0444, value_show, NULL); -DEVICE_ATTR(sync_ep, 0200, NULL, value_store); -DEVICE_ATTR(mep_filter, 0644, value_show, value_store); -DEVICE_ATTR(mep_hash0, 0644, value_show, value_store); -DEVICE_ATTR(mep_hash1, 0644, value_show, value_store); -DEVICE_ATTR(mep_hash2, 0644, value_show, value_store); -DEVICE_ATTR(mep_hash3, 0644, value_show, value_store); -DEVICE_ATTR(mep_eui48_hi, 0644, value_show, value_store); -DEVICE_ATTR(mep_eui48_mi, 0644, value_show, value_store); -DEVICE_ATTR(mep_eui48_lo, 0644, value_show, value_store); -DEVICE_ATTR(arb_address, 0644, value_show, value_store); -DEVICE_ATTR(arb_value, 0644, value_show, value_store); +static DEVICE_ATTR(ni_state, 0444, value_show, NULL); +static DEVICE_ATTR(packet_bandwidth, 0444, value_show, NULL); +static DEVICE_ATTR(node_address, 0444, value_show, NULL); +static DEVICE_ATTR(node_position, 0444, value_show, NULL); +static DEVICE_ATTR(sync_ep, 0200, NULL, value_store); +static DEVICE_ATTR(mep_filter, 0644, value_show, value_store); +static DEVICE_ATTR(mep_hash0, 0644, value_show, value_store); +static DEVICE_ATTR(mep_hash1, 0644, value_show, value_store); +static DEVICE_ATTR(mep_hash2, 0644, value_show, value_store); +static DEVICE_ATTR(mep_hash3, 0644, value_show, value_store); +static DEVICE_ATTR(mep_eui48_hi, 0644, value_show, value_store); +static DEVICE_ATTR(mep_eui48_mi, 0644, value_show, value_store); +static DEVICE_ATTR(mep_eui48_lo, 0644, value_show, value_store); +static DEVICE_ATTR(arb_address, 0644, value_show, value_store); +static DEVICE_ATTR(arb_value, 0644, value_show, value_store); static struct attribute *dci_attrs[] = { &dev_attr_ni_state.attr, -- cgit From 845c31de4ce7e2329b93fec9b54f1121240d0e5f Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sun, 10 Dec 2017 23:20:58 +0000 Subject: staging: most: core: make functions print_links and most_match static The functions print_links and most_match static are local to the source and do not need to be in global scope, so make them static. Cleans up sparse warnings: symbol 'print_links' was not declared. Should it be static? symbol 'most_match' was not declared. Should it be static? Signed-off-by: Colin Ian King Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index c1fba5ba7541..5ba2cd5ec1b0 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -534,7 +534,7 @@ struct show_links_data { char *buf; }; -int print_links(struct device *dev, void *data) +static int print_links(struct device *dev, void *data) { struct show_links_data *d = data; int offs = d->offs; @@ -803,7 +803,7 @@ static const struct attribute_group *mc_attr_groups[] = { NULL, }; -int most_match(struct device *dev, struct device_driver *drv) +static int most_match(struct device *dev, struct device_driver *drv) { if (!strcmp(dev_name(dev), "most")) return 0; -- cgit From 37d641ef37782acbdb861d3d18a46c39104da3ff Mon Sep 17 00:00:00 2001 From: Ravi Eluri Date: Fri, 22 Dec 2017 13:55:44 +0530 Subject: staging: most: Avoid trailing semicolon for macros Fixes checkpatch warning: macros should not use a trailing semicolon. Signed-off-by: Ravi Eluri Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/core.c b/drivers/staging/most/core.c index 5ba2cd5ec1b0..3dda8d81bf0b 100644 --- a/drivers/staging/most/core.c +++ b/drivers/staging/most/core.c @@ -36,7 +36,7 @@ static struct mostcore { struct list_head comp_list; } mc; -#define to_driver(d) container_of(d, struct mostcore, drv); +#define to_driver(d) container_of(d, struct mostcore, drv) struct pipe { struct core_component *comp; -- cgit From 9917b209f8e641b8649c638fba2fc2b8aacb150f Mon Sep 17 00:00:00 2001 From: Ravi Eluri Date: Fri, 22 Dec 2017 15:39:02 +0530 Subject: staging: most: Fix identifiers to function parameters fixed "function definition argument should have an identifier name", with appropriate identifier names. Pointed out by checkpatch. Signed-off-by: Ravi Eluri Signed-off-by: Suniel Mahesh Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/dim2/dim2.c | 4 ++-- drivers/staging/most/usb/usb.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/dim2/dim2.c b/drivers/staging/most/dim2/dim2.c index 2bd40abbc8c6..21e3fb48bdb4 100644 --- a/drivers/staging/most/dim2/dim2.c +++ b/drivers/staging/most/dim2/dim2.c @@ -101,8 +101,8 @@ struct dim2_hdm { unsigned char link_state; int atx_idx; struct medialb_bus bus; - void (*on_netinfo)(struct most_interface *, - unsigned char, unsigned char *); + void (*on_netinfo)(struct most_interface *most_iface, + unsigned char link_state, unsigned char *addrs); }; #define iface_to_hdm(iface) container_of(iface, struct dim2_hdm, most_iface) diff --git a/drivers/staging/most/usb/usb.c b/drivers/staging/most/usb/usb.c index 8d23075ae165..31f184cfcd69 100644 --- a/drivers/staging/most/usb/usb.c +++ b/drivers/staging/most/usb/usb.c @@ -117,8 +117,8 @@ struct most_dev { struct mutex io_mutex; struct timer_list link_stat_timer; struct work_struct poll_work_obj; - void (*on_netinfo)(struct most_interface *, unsigned char, - unsigned char *); + void (*on_netinfo)(struct most_interface *most_iface, + unsigned char link_state, unsigned char *addrs); }; #define to_mdev(d) container_of(d, struct most_dev, iface) -- cgit From 6724ed7f28d6e83956a7219a9a02a4d620ef65d8 Mon Sep 17 00:00:00 2001 From: George Edward Bulmer Date: Fri, 29 Dec 2017 21:26:20 +0000 Subject: staging: most: convert macro to static function This fixes checkpatch warning: CHECK: Macro argument reuse 'buf' - possible side effects? Signed-off-by: George Edward Bulmer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/net/net.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/net/net.c b/drivers/staging/most/net/net.c index 33606584e50e..30d816b7e165 100644 --- a/drivers/staging/most/net/net.c +++ b/drivers/staging/most/net/net.c @@ -46,10 +46,12 @@ ((len) > MEP_HDR_LEN && \ EXTRACT_BIT_SET(PMS_FIFONO, (buf)[3]) == PMS_FIFONO_MEP) -#define PMS_IS_MAMAC(buf, len) \ - ((len) > MDP_HDR_LEN && \ - EXTRACT_BIT_SET(PMS_FIFONO, (buf)[3]) == PMS_FIFONO_MDP && \ - EXTRACT_BIT_SET(PMS_TELID, (buf)[14]) == PMS_TELID_UNSEGM_MAMAC) +static inline bool pms_is_mamac(char *buf, u32 len) +{ + return (len > MDP_HDR_LEN && + EXTRACT_BIT_SET(PMS_FIFONO, buf[3]) == PMS_FIFONO_MDP && + EXTRACT_BIT_SET(PMS_TELID, buf[14]) == PMS_TELID_UNSEGM_MAMAC); +} struct net_dev_channel { bool linked; @@ -435,7 +437,7 @@ static int comp_rx_data(struct mbo *mbo) dev = nd->dev; if (nd->is_mamac) { - if (!PMS_IS_MAMAC(buf, len)) { + if (!pms_is_mamac(buf, len)) { ret = -EIO; goto put_nd; } -- cgit From 4d7ce7c05353d19c5f7963db76e45656640115ee Mon Sep 17 00:00:00 2001 From: Sidong Yang Date: Sun, 21 Jan 2018 16:05:26 +0000 Subject: staging: most: replace function name to __func__ Fix checkpatch.pl warning message about logging code. Previous code contains hard coded function name. Fix this code by using __func__ macro. Signed-off-by: Sidong Yang Signed-off-by: Greg Kroah-Hartman --- drivers/staging/most/dim2/dim2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/staging/most') diff --git a/drivers/staging/most/dim2/dim2.c b/drivers/staging/most/dim2/dim2.c index 21e3fb48bdb4..f9bc7dea75b8 100644 --- a/drivers/staging/most/dim2/dim2.c +++ b/drivers/staging/most/dim2/dim2.c @@ -151,7 +151,7 @@ void dimcb_io_write(u32 __iomem *ptr32, u32 value) */ void dimcb_on_error(u8 error_id, const char *error_message) { - pr_err("dimcb_on_error: error_id - %d, error_message - %s\n", error_id, + pr_err("%s: error_id - %d, error_message - %s\n", __func__, error_id, error_message); } -- cgit