diff --git a/automotive/can/aidl/Android.bp b/automotive/can/aidl/Android.bp new file mode 100644 index 0000000000..67333b9730 --- /dev/null +++ b/automotive/can/aidl/Android.bp @@ -0,0 +1,40 @@ +// +// Copyright (C) 2022 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "hardware_interfaces_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["hardware_interfaces_license"], +} + +aidl_interface { + name: "android.hardware.automotive.can", + vendor_available: true, + srcs: ["android/hardware/automotive/can/*.aidl"], + stability: "vintf", + host_supported: true, + backend: { + java: { + enabled: false, + }, + rust: { + enabled: true, + }, + }, +} diff --git a/automotive/can/aidl/aidl_api/android.hardware.automotive.can/current/android/hardware/automotive/can/BusConfig.aidl b/automotive/can/aidl/aidl_api/android.hardware.automotive.can/current/android/hardware/automotive/can/BusConfig.aidl new file mode 100644 index 0000000000..0212e00f4c --- /dev/null +++ b/automotive/can/aidl/aidl_api/android.hardware.automotive.can/current/android/hardware/automotive/can/BusConfig.aidl @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.automotive.can; +@VintfStability +parcelable BusConfig { + String name; + android.hardware.automotive.can.BusConfig.InterfaceId interfaceId; + int bitrate; + union InterfaceId { + android.hardware.automotive.can.VirtualInterface virtualif; + android.hardware.automotive.can.NativeInterface nativeif; + android.hardware.automotive.can.SlcanInterface slcan; + android.hardware.automotive.can.IndexedInterface indexed; + } +} diff --git a/automotive/can/aidl/aidl_api/android.hardware.automotive.can/current/android/hardware/automotive/can/ICanController.aidl b/automotive/can/aidl/aidl_api/android.hardware.automotive.can/current/android/hardware/automotive/can/ICanController.aidl new file mode 100644 index 0000000000..5d032f27ab --- /dev/null +++ b/automotive/can/aidl/aidl_api/android.hardware.automotive.can/current/android/hardware/automotive/can/ICanController.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.automotive.can; +@VintfStability +interface ICanController { + android.hardware.automotive.can.InterfaceType[] getSupportedInterfaceTypes(); + String getInterfaceName(in String busName); + String upBus(in android.hardware.automotive.can.BusConfig config); + void downBus(in String name); +} diff --git a/automotive/can/aidl/aidl_api/android.hardware.automotive.can/current/android/hardware/automotive/can/IndexedInterface.aidl b/automotive/can/aidl/aidl_api/android.hardware.automotive.can/current/android/hardware/automotive/can/IndexedInterface.aidl new file mode 100644 index 0000000000..1b00adf1e1 --- /dev/null +++ b/automotive/can/aidl/aidl_api/android.hardware.automotive.can/current/android/hardware/automotive/can/IndexedInterface.aidl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.automotive.can; +@VintfStability +parcelable IndexedInterface { + byte index; +} diff --git a/automotive/can/aidl/aidl_api/android.hardware.automotive.can/current/android/hardware/automotive/can/InterfaceType.aidl b/automotive/can/aidl/aidl_api/android.hardware.automotive.can/current/android/hardware/automotive/can/InterfaceType.aidl new file mode 100644 index 0000000000..44865aafb7 --- /dev/null +++ b/automotive/can/aidl/aidl_api/android.hardware.automotive.can/current/android/hardware/automotive/can/InterfaceType.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.automotive.can; +@Backing(type="byte") @VintfStability +enum InterfaceType { + VIRTUAL = 0, + NATIVE = 1, + SLCAN = 2, + INDEXED = 3, +} diff --git a/automotive/can/aidl/aidl_api/android.hardware.automotive.can/current/android/hardware/automotive/can/NativeInterface.aidl b/automotive/can/aidl/aidl_api/android.hardware.automotive.can/current/android/hardware/automotive/can/NativeInterface.aidl new file mode 100644 index 0000000000..5d6c119356 --- /dev/null +++ b/automotive/can/aidl/aidl_api/android.hardware.automotive.can/current/android/hardware/automotive/can/NativeInterface.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.automotive.can; +@VintfStability +parcelable NativeInterface { + android.hardware.automotive.can.NativeInterface.InterfaceId interfaceId; + union InterfaceId { + String ifname; + String[] serialno; + } +} diff --git a/automotive/can/aidl/aidl_api/android.hardware.automotive.can/current/android/hardware/automotive/can/Result.aidl b/automotive/can/aidl/aidl_api/android.hardware.automotive.can/current/android/hardware/automotive/can/Result.aidl new file mode 100644 index 0000000000..010792a054 --- /dev/null +++ b/automotive/can/aidl/aidl_api/android.hardware.automotive.can/current/android/hardware/automotive/can/Result.aidl @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.automotive.can; +@Backing(type="int") @VintfStability +enum Result { + OK = 0, + UNKNOWN_ERROR = 1, + INVALID_STATE = 2, + NOT_SUPPORTED = 3, + BAD_INTERFACE_ID = 4, + BAD_BITRATE = 5, + BAD_BUS_NAME = 6, + INTERFACE_DOWN = 7, +} diff --git a/automotive/can/aidl/aidl_api/android.hardware.automotive.can/current/android/hardware/automotive/can/SlcanInterface.aidl b/automotive/can/aidl/aidl_api/android.hardware.automotive.can/current/android/hardware/automotive/can/SlcanInterface.aidl new file mode 100644 index 0000000000..af0c07d22a --- /dev/null +++ b/automotive/can/aidl/aidl_api/android.hardware.automotive.can/current/android/hardware/automotive/can/SlcanInterface.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.automotive.can; +@VintfStability +parcelable SlcanInterface { + android.hardware.automotive.can.SlcanInterface.InterfaceId interfaceId; + union InterfaceId { + String ttyname; + String[] serialno; + } +} diff --git a/automotive/can/aidl/aidl_api/android.hardware.automotive.can/current/android/hardware/automotive/can/VirtualInterface.aidl b/automotive/can/aidl/aidl_api/android.hardware.automotive.can/current/android/hardware/automotive/can/VirtualInterface.aidl new file mode 100644 index 0000000000..c870612dd9 --- /dev/null +++ b/automotive/can/aidl/aidl_api/android.hardware.automotive.can/current/android/hardware/automotive/can/VirtualInterface.aidl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.automotive.can; +@VintfStability +parcelable VirtualInterface { + String ifname; +} diff --git a/automotive/can/aidl/android/hardware/automotive/can/BusConfig.aidl b/automotive/can/aidl/android/hardware/automotive/can/BusConfig.aidl new file mode 100644 index 0000000000..4e1027ba2d --- /dev/null +++ b/automotive/can/aidl/android/hardware/automotive/can/BusConfig.aidl @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.automotive.can; + +import android.hardware.automotive.can.IndexedInterface; +import android.hardware.automotive.can.NativeInterface; +import android.hardware.automotive.can.SlcanInterface; +import android.hardware.automotive.can.VirtualInterface; + +/** + * Configuration of the (physical or virtual) CAN bus. + * + * ISO TP and CAN FD support is dependent upon the hardware. + */ +@VintfStability +parcelable BusConfig { + /** + * Name by which a given bus may be referenced. + * + * It must consist of only alphanumeric characters and underscore + * (a-z, A-Z, 0-9, '_'), at least 1 and at most 32 characters long. + * + * This field is *not* meant to distinguish between hardware interfaces + * nor preselect parameters like bitrate. + * + * This field represents a more human-friendly name for a CAN bus: + * e.x. rather than /some/dev/can1234, "name" might be "BodyCAN" or "CCAN" + */ + String name; + + /** + * Hardware interface configuration. + * + * This union's discriminator has an equivalent enum {@see InterfaceType} to + * express compatibility via getSupportedInterfaceTypes(). + */ + union InterfaceId { + /** Virtual SocketCAN interface. */ + VirtualInterface virtualif; + + /** Native SocketCAN interface. */ + NativeInterface nativeif; + + /** Serial line CAN interface. */ + SlcanInterface slcan; + + /** + * Proprietary, device-specific interface. + * + * Non-SocketCAN interfaces should use this variant. + */ + IndexedInterface indexed; + } + + InterfaceId interfaceId; + + /** + * Bit rate for CAN communication. + * + * Typical bit rates are: 100000, 125000, 250000, 500000. + * + * For {@see interfaceId#virtual} interfaces, this value is ignored. + */ + int bitrate; +} diff --git a/automotive/can/aidl/android/hardware/automotive/can/ICanController.aidl b/automotive/can/aidl/android/hardware/automotive/can/ICanController.aidl new file mode 100644 index 0000000000..97c2674e8c --- /dev/null +++ b/automotive/can/aidl/android/hardware/automotive/can/ICanController.aidl @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.automotive.can; + +import android.hardware.automotive.can.BusConfig; +import android.hardware.automotive.can.InterfaceType; +import android.hardware.automotive.can.Result; + +/** + * Represents a CAN controller that's capable of configuring CAN bus interfaces. + * + * The goal of this service is to configure and manage CAN interfaces. + * + * Providing an ICanController interface to configure CAN buses is optional. + * A system can elect to configure CAN buses manually if the hardware is + * dedicated to a specific application. + */ +@VintfStability +interface ICanController { + /** + * Fetches the list of interface types supported by this HAL server. + * + * @return iftypes The list of supported interface types. + */ + InterfaceType[] getSupportedInterfaceTypes(); + + /** + * Gets the interface name given the name of the bus. This will + * + * @param busName Name of the CAN bus who's interface name we would like + * (e.x. BCAN, CCAN, HS3, BodyCAN, ...) + * @return name of the socketcan network interface corresponding to busName + * (e.x. can0, vcan5, ...) + */ + String getInterfaceName(in String busName); + + /** + * Bring up a CAN bus. + * + * @param config Configuration for the CAN bus. + * @return name of iface if successful + */ + String upBus(in BusConfig config); + + /** + * Bring down a CAN bus. + * + * @param name Name of the bus (@see BusConfig#name} to bring down. + */ + void downBus(in String name); +} diff --git a/automotive/can/aidl/android/hardware/automotive/can/IndexedInterface.aidl b/automotive/can/aidl/android/hardware/automotive/can/IndexedInterface.aidl new file mode 100644 index 0000000000..13e223ff3d --- /dev/null +++ b/automotive/can/aidl/android/hardware/automotive/can/IndexedInterface.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.automotive.can; + +@VintfStability +parcelable IndexedInterface { + /** Interface number, 0-based. */ + byte index; +} diff --git a/automotive/can/aidl/android/hardware/automotive/can/InterfaceType.aidl b/automotive/can/aidl/android/hardware/automotive/can/InterfaceType.aidl new file mode 100644 index 0000000000..b13648a8b6 --- /dev/null +++ b/automotive/can/aidl/android/hardware/automotive/can/InterfaceType.aidl @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.automotive.can; + +/** + * Type of an interface, an equivalent to BusConfig::InterfaceId + * union discriminator. Defines a number of specific standard hardware + * families and a generic catch-all type of {@see INDEXED}. + */ +@VintfStability +@Backing(type="byte") +enum InterfaceType { + /** Virtual SocketCAN interface. */ + VIRTUAL, + + /** Native SocketCAN interface. */ + NATIVE, + + /** Serial line CAN interface. */ + SLCAN, + + /** Proprietary, device-specific interface. */ + INDEXED, +} diff --git a/automotive/can/aidl/android/hardware/automotive/can/NativeInterface.aidl b/automotive/can/aidl/android/hardware/automotive/can/NativeInterface.aidl new file mode 100644 index 0000000000..30c24c3d55 --- /dev/null +++ b/automotive/can/aidl/android/hardware/automotive/can/NativeInterface.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.automotive.can; + +@VintfStability +parcelable NativeInterface { + union InterfaceId { + /** Interface name, such as can0. */ + String ifname; + + /** + * Alternatively to providing {@see ifname}, one may provide a list of + * interface serial number suffixes. If there happens to be a device + * (like USB2CAN) with a matching serial number suffix, the HAL service + * will locate it. + * + * Client may utilize this in two ways: by matching against the + * entire serial number, or the last few characters (usually + * one). The former is better for small-scale test deployments + * (with just a handful of vehicles), the latter is good for + * larger scale (where a small suffix list may support large + * test fleet). + */ + String[] serialno; + } + + InterfaceId interfaceId; +} diff --git a/automotive/can/aidl/android/hardware/automotive/can/Result.aidl b/automotive/can/aidl/android/hardware/automotive/can/Result.aidl new file mode 100644 index 0000000000..cdd0066328 --- /dev/null +++ b/automotive/can/aidl/android/hardware/automotive/can/Result.aidl @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.automotive.can; + +/** + * Possible error codes (or OK) for ICanController. + */ +@VintfStability +@Backing(type="int") +enum Result { + OK, + + /** + * General error class, if others are not applicable. + */ + UNKNOWN_ERROR, + + /** + * Up request was called out of order (i.e. trying to up the interface + * twice). + */ + INVALID_STATE, + + /** Interface type is not supported. */ + NOT_SUPPORTED, + + /** + * Provided interface ID (index, name, device path) doesn't exist or there + * is no device with a given serial number. + */ + BAD_INTERFACE_ID, + + /** Provided bit rate is not supported by the hardware. */ + BAD_BITRATE, + + /** + * Provided bus name ({@see BusConfig#name}) has invalid format or doesn't exist. + */ + BAD_BUS_NAME, + + /** + * The interface for the bus you are trying to interact with is currently + * down. As opposed to INVALID_STATE, this serves to warn the caller + * _before_ they attempt an invalid operation. + */ + INTERFACE_DOWN, +} diff --git a/automotive/can/aidl/android/hardware/automotive/can/SlcanInterface.aidl b/automotive/can/aidl/android/hardware/automotive/can/SlcanInterface.aidl new file mode 100644 index 0000000000..4ed85838df --- /dev/null +++ b/automotive/can/aidl/android/hardware/automotive/can/SlcanInterface.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.automotive.can; + +@VintfStability +parcelable SlcanInterface { + union InterfaceId { + /** Path to a device, such as /dev/ttyUSB0. */ + String ttyname; + + /** + * List of interface serial number suffixes. + * {@see Socketcan::serialno} + */ + String[] serialno; + } + + InterfaceId interfaceId; +} diff --git a/automotive/can/aidl/android/hardware/automotive/can/VirtualInterface.aidl b/automotive/can/aidl/android/hardware/automotive/can/VirtualInterface.aidl new file mode 100644 index 0000000000..7e1e5e17ca --- /dev/null +++ b/automotive/can/aidl/android/hardware/automotive/can/VirtualInterface.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.automotive.can; + +@VintfStability +parcelable VirtualInterface { + /** + * Interface name, such as vcan0. If the interface doesn't + * exist, HAL server must create it. + */ + String ifname; +} diff --git a/automotive/can/aidl/default/Android.bp b/automotive/can/aidl/default/Android.bp new file mode 100644 index 0000000000..d44cb91cf9 --- /dev/null +++ b/automotive/can/aidl/default/Android.bp @@ -0,0 +1,51 @@ +// +// Copyright (C) 2022 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "hardware_interfaces_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["hardware_interfaces_license"], +} + +cc_binary { + name: "android.hardware.automotive.can-service", + init_rc: ["android.hardware.automotive.can.rc"], + defaults: ["android.hardware.automotive.can@defaults"], + vendor: true, + relative_install_path: "hw", + srcs: [ + "CanBus.cpp", + "CanBusSlcan.cpp", + "CanBusNative.cpp", + "CanBusVirtual.cpp", + "CanController.cpp", + "service.cpp", + ], + shared_libs: [ + "android.hardware.automotive.can-V1-ndk", + "libbase", + "libbinder_ndk", + ], + static_libs: [ + "android.hardware.automotive.can@libnetdevice", + "android.hardware.automotive@libc++fs", + "libnl++", + ], + vintf_fragments: ["android.hardware.automotive.can.xml"], +} diff --git a/automotive/can/aidl/default/CanBus.cpp b/automotive/can/aidl/default/CanBus.cpp new file mode 100644 index 0000000000..d1f9b78d95 --- /dev/null +++ b/automotive/can/aidl/default/CanBus.cpp @@ -0,0 +1,93 @@ +/* + * Copyright 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CanBus.h" + +#include +#include + +namespace aidl::android::hardware::automotive::can { + +CanBus::CanBus(std::string_view ifname) : mIfname(ifname) {} + +CanBus::~CanBus() { + std::lock_guard lck(mIsUpGuard); + CHECK(!mIsUp) << "Interface is still up while being destroyed"; +} + +Result CanBus::preUp() { + return Result::OK; +} + +bool CanBus::postDown() { + return true; +} + +std::string CanBus::getIfaceName() { + return mIfname; +} + +Result CanBus::up() { + std::lock_guard lck(mIsUpGuard); + + if (mIsUp) { + LOG(WARNING) << "Interface is already up"; + return Result::INVALID_STATE; + } + + const auto preResult = preUp(); + if (preResult != Result::OK) return preResult; + + const auto isUp = ::android::netdevice::isUp(mIfname); + if (!isUp.has_value()) { + // preUp() should prepare the interface (either create or make sure it's there) + LOG(ERROR) << "Interface " << mIfname << " didn't get prepared"; + return Result::BAD_INTERFACE_ID; + } + + if (!*isUp && !::android::netdevice::up(mIfname)) { + LOG(ERROR) << "Can't bring " << mIfname << " up"; + return Result::UNKNOWN_ERROR; + } + mDownAfterUse = !*isUp; + + mIsUp = true; + return Result::OK; +} + +Result CanBus::down() { + std::lock_guard lck(mIsUpGuard); + + if (!mIsUp) { + LOG(WARNING) << "Interface is already down"; + return Result::INVALID_STATE; + } + mIsUp = false; + + Result success = Result::OK; + + if (mDownAfterUse && !::android::netdevice::down(mIfname)) { + LOG(ERROR) << "Can't bring " << mIfname << " down"; + // don't return yet, let's try to do best-effort cleanup + success = Result::UNKNOWN_ERROR; + } + + if (!postDown()) success = Result::UNKNOWN_ERROR; + + return success; +} + +} // namespace aidl::android::hardware::automotive::can diff --git a/automotive/can/aidl/default/CanBus.h b/automotive/can/aidl/default/CanBus.h new file mode 100644 index 0000000000..abdbe5148f --- /dev/null +++ b/automotive/can/aidl/default/CanBus.h @@ -0,0 +1,77 @@ +/* + * Copyright 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include +#include + +#include +#include + +namespace aidl::android::hardware::automotive::can { + +class CanBus { + public: + /** + * Some interface types (such as SLCAN) don't get an interface name until after being + * initialized, hence ifname is optional. + * + * You MUST ensure mIfname is initialized prior to the completion of preUp(). + */ + CanBus(std::string_view ifname = std::string_view{""}); + + virtual ~CanBus(); + + Result up(); + Result down(); + std::string getIfaceName(); + + protected: + /** + * Prepare the SocketCAN interface. + * + * After calling this method, mIfname network interface is available and ready to be brought up. + * + * \return true upon success and false upon failure + */ + virtual Result preUp(); + + /** + * Cleanup after bringing the interface down. + * + * This is a counterpart to preUp(). + * + * \return true upon success and false upon failure + */ + virtual bool postDown(); + + /** Network interface name. */ + std::string mIfname; + + private: + /** + * Guard for up flag is required to be held for entire time when the interface is being used + * because we don't want the interface to be torn down while executing that operation. + */ + std::mutex mIsUpGuard; + bool mIsUp GUARDED_BY(mIsUpGuard) = false; + + bool mDownAfterUse; +}; + +} // namespace aidl::android::hardware::automotive::can diff --git a/automotive/can/aidl/default/CanBusNative.cpp b/automotive/can/aidl/default/CanBusNative.cpp new file mode 100644 index 0000000000..8a7de99203 --- /dev/null +++ b/automotive/can/aidl/default/CanBusNative.cpp @@ -0,0 +1,54 @@ +/* + * Copyright 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CanBusNative.h" + +#include +#include +#include + +namespace aidl::android::hardware::automotive::can { + +using namespace ::android; + +CanBusNative::CanBusNative(const std::string& ifname, uint32_t bitrate) + : CanBus(ifname), mBitrate(bitrate) {} + +Result CanBusNative::preUp() { + if (!netdevice::exists(mIfname)) { + LOG(ERROR) << "Interface " << mIfname << " doesn't exist"; + return Result::BAD_INTERFACE_ID; + } + + if (mBitrate == 0) { + // interface is already up and we just want to register it + return Result::OK; + } + + if (!netdevice::down(mIfname)) { + LOG(ERROR) << "Can't bring " << mIfname << " down (to configure it)"; + return Result::UNKNOWN_ERROR; + } + + if (!netdevice::can::setBitrate(mIfname, mBitrate)) { + LOG(ERROR) << "Can't set bitrate " << mBitrate << " for " << mIfname; + return Result::BAD_BITRATE; + } + + return Result::OK; +} + +} // namespace aidl::android::hardware::automotive::can diff --git a/automotive/can/aidl/default/CanBusNative.h b/automotive/can/aidl/default/CanBusNative.h new file mode 100644 index 0000000000..32846c7109 --- /dev/null +++ b/automotive/can/aidl/default/CanBusNative.h @@ -0,0 +1,33 @@ +/* + * Copyright 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "CanBus.h" + +namespace aidl::android::hardware::automotive::can { + +class CanBusNative : public CanBus { + public: + CanBusNative(const std::string& ifname, uint32_t bitrate); + + protected: + virtual Result preUp() override; + + private: + const uint32_t mBitrate; +}; + +} // namespace aidl::android::hardware::automotive::can diff --git a/automotive/can/aidl/default/CanBusSlcan.cpp b/automotive/can/aidl/default/CanBusSlcan.cpp new file mode 100644 index 0000000000..6060419dbd --- /dev/null +++ b/automotive/can/aidl/default/CanBusSlcan.cpp @@ -0,0 +1,174 @@ +/* + * Copyright 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CanBusSlcan.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace aidl::android::hardware::automotive::can { + +using namespace std::string_view_literals; +using namespace ::android::base; + +namespace slcanprotocol { +static constexpr std::string_view kOpenCommand = "O\r"sv; +static constexpr std::string_view kCloseCommand = "C\r"sv; +static constexpr int kSlcanDiscipline = N_SLCAN; +static constexpr int kDefaultDiscipline = N_TTY; + +static const std::map kBitrateCommands = { + {10000, "C\rS0\r"sv}, {20000, "C\rS1\r"sv}, {50000, "C\rS2\r"sv}, + {100000, "C\rS3\r"sv}, {125000, "C\rS4\r"sv}, {250000, "C\rS5\r"sv}, + {500000, "C\rS6\r"sv}, {800000, "C\rS7\r"sv}, {1000000, "C\rS8\r"sv}}; +} // namespace slcanprotocol + +/** + * Serial Line CAN constructor + * \param string uartName - name of slcan device (e.x. /dev/ttyUSB0) + * \param uint32_t bitrate - speed of the CAN bus (125k = MSCAN, 500k = HSCAN) + */ +CanBusSlcan::CanBusSlcan(const std::string& uartName, uint32_t bitrate) + : CanBus(), mTtyPath(uartName), kBitrate(bitrate) {} + +/** helper function to update CanBusSlcan object's iface name */ +Result CanBusSlcan::updateIfaceName(unique_fd& uartFd) { + struct ifreq ifrequest = {}; + /* + * Fetching the iface name with an ioctl won't interfere with an open socketCAN iface attached + * to this tty. This is important in the event we are trying to register a SLCAN based iface + * that has already been configured and brought up. + */ + if (ioctl(uartFd.get(), SIOCGIFNAME, ifrequest.ifr_name) < 0) { + PLOG(ERROR) << "Failed to get the name of the created device"; + return Result::UNKNOWN_ERROR; + } + + // Update the CanBus object with name that was assigned to it + mIfname = ifrequest.ifr_name; + return Result::OK; +} + +Result CanBusSlcan::preUp() { + // verify valid bitrate and translate to serial command format + std::optional canBitrateCommand = std::nullopt; + if (kBitrate != 0) { + const auto lookupIt = slcanprotocol::kBitrateCommands.find(kBitrate); + if (lookupIt == slcanprotocol::kBitrateCommands.end()) { + return Result::BAD_BITRATE; + } + canBitrateCommand = lookupIt->second; + } + + /* Attempt to open the uart in r/w without blocking or becoming the + * controlling terminal */ + mFd = unique_fd(open(mTtyPath.c_str(), O_RDWR | O_NONBLOCK | O_NOCTTY | O_CLOEXEC)); + if (!mFd.ok()) { + PLOG(ERROR) << "SLCAN Failed to open " << mTtyPath; + return Result::BAD_INTERFACE_ID; + } + + // If the device is already up, update the iface name in our CanBusSlcan object + if (kBitrate == 0) { + return updateIfaceName(mFd); + } + + // blank terminal settings and pull them from the device + struct termios terminalSettings = {}; + if (tcgetattr(mFd.get(), &terminalSettings) < 0) { + PLOG(ERROR) << "Failed to read attrs of" << mTtyPath; + return Result::UNKNOWN_ERROR; + } + + // change settings to raw mode + cfmakeraw(&terminalSettings); + + // disable software flow control + terminalSettings.c_iflag &= ~IXOFF; + // enable hardware flow control + terminalSettings.c_cflag |= CRTSCTS; + + struct serial_struct serialSettings; + // get serial settings + if (ioctl(mFd.get(), TIOCGSERIAL, &serialSettings) < 0) { + PLOG(ERROR) << "Failed to read serial settings from " << mTtyPath; + return Result::UNKNOWN_ERROR; + } + // set low latency mode + serialSettings.flags |= ASYNC_LOW_LATENCY; + // apply serial settings + if (ioctl(mFd.get(), TIOCSSERIAL, &serialSettings) < 0) { + PLOG(ERROR) << "Failed to set low latency mode on " << mTtyPath; + return Result::UNKNOWN_ERROR; + } + + /* TCSADRAIN applies settings after we finish writing the rest of our + * changes (as opposed to TCSANOW, which changes immediately) */ + if (tcsetattr(mFd.get(), TCSADRAIN, &terminalSettings) < 0) { + PLOG(ERROR) << "Failed to apply terminal settings to " << mTtyPath; + return Result::UNKNOWN_ERROR; + } + + // apply speed setting for CAN + if (!WriteStringToFd(*canBitrateCommand, mFd)) { + PLOG(ERROR) << "Failed to apply CAN bitrate"; + return Result::UNKNOWN_ERROR; + } + + // TODO(b/144775286): set open flag & support listen only + if (!WriteStringToFd(slcanprotocol::kOpenCommand, mFd)) { + PLOG(ERROR) << "Failed to set open flag"; + return Result::UNKNOWN_ERROR; + } + + // set line discipline to slcan + if (ioctl(mFd.get(), TIOCSETD, &slcanprotocol::kSlcanDiscipline) < 0) { + PLOG(ERROR) << "Failed to set line discipline to slcan"; + return Result::UNKNOWN_ERROR; + } + + // Update the CanBus object with name that was assigned to it + return updateIfaceName(mFd); +} + +bool CanBusSlcan::postDown() { + // reset the line discipline to TTY mode + if (ioctl(mFd.get(), TIOCSETD, &slcanprotocol::kDefaultDiscipline) < 0) { + LOG(ERROR) << "Failed to reset line discipline!"; + return false; + } + + // issue the close command + if (!WriteStringToFd(slcanprotocol::kCloseCommand, mFd)) { + LOG(ERROR) << "Failed to close tty!"; + return false; + } + + // close our unique_fd + mFd.reset(); + + return true; +} + +} // namespace aidl::android::hardware::automotive::can diff --git a/automotive/can/aidl/default/CanBusSlcan.h b/automotive/can/aidl/default/CanBusSlcan.h new file mode 100644 index 0000000000..a1c908cc4f --- /dev/null +++ b/automotive/can/aidl/default/CanBusSlcan.h @@ -0,0 +1,40 @@ +/* + * Copyright 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "CanBus.h" + +#include + +namespace aidl::android::hardware::automotive::can { + +class CanBusSlcan : public CanBus { + public: + CanBusSlcan(const std::string& uartName, uint32_t bitrate); + + protected: + virtual Result preUp() override; + virtual bool postDown() override; + + private: + Result updateIfaceName(::android::base::unique_fd& uartFd); + + const std::string mTtyPath; + const uint32_t kBitrate; + ::android::base::unique_fd mFd; +}; + +} // namespace aidl::android::hardware::automotive::can diff --git a/automotive/can/aidl/default/CanBusVirtual.cpp b/automotive/can/aidl/default/CanBusVirtual.cpp new file mode 100644 index 0000000000..28a1258ad2 --- /dev/null +++ b/automotive/can/aidl/default/CanBusVirtual.cpp @@ -0,0 +1,52 @@ +/* + * Copyright 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CanBusVirtual.h" + +#include +#include + +namespace aidl::android::hardware::automotive::can { + +using namespace ::android; + +CanBusVirtual::CanBusVirtual(const std::string& ifname) : CanBus(ifname) {} + +Result CanBusVirtual::preUp() { + if (netdevice::exists(mIfname)) return Result::OK; + + LOG(DEBUG) << "Virtual interface " << mIfname << " doesn't exist, creating..."; + mWasCreated = true; + if (!netdevice::add(mIfname, "vcan")) { + LOG(ERROR) << "Can't create vcan interface " << mIfname; + return Result::UNKNOWN_ERROR; + } + + return Result::OK; +} + +bool CanBusVirtual::postDown() { + if (mWasCreated) { + mWasCreated = false; + if (!netdevice::del(mIfname)) { + LOG(ERROR) << "Couldn't remove vcan interface " << mIfname; + return false; + } + } + return true; +} + +} // namespace aidl::android::hardware::automotive::can diff --git a/automotive/can/aidl/default/CanBusVirtual.h b/automotive/can/aidl/default/CanBusVirtual.h new file mode 100644 index 0000000000..9c5d35d0bf --- /dev/null +++ b/automotive/can/aidl/default/CanBusVirtual.h @@ -0,0 +1,34 @@ +/* + * Copyright 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "CanBus.h" + +namespace aidl::android::hardware::automotive::can { + +class CanBusVirtual : public CanBus { + public: + CanBusVirtual(const std::string& ifname); + + protected: + virtual Result preUp() override; + virtual bool postDown() override; + + private: + bool mWasCreated = false; +}; + +} // namespace aidl::android::hardware::automotive::can diff --git a/automotive/can/aidl/default/CanController.cpp b/automotive/can/aidl/default/CanController.cpp new file mode 100644 index 0000000000..e4b5306815 --- /dev/null +++ b/automotive/can/aidl/default/CanController.cpp @@ -0,0 +1,333 @@ +/* + * Copyright 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CanController.h" + +#include "CanBusNative.h" +#include "CanBusSlcan.h" +#include "CanBusVirtual.h" + +#include +#include + +#include +#include +#include + +namespace aidl::android::hardware::automotive::can { + +namespace fs = ::android::hardware::automotive::filesystem; + +namespace fsErrors { +static const std::error_code ok; +static const std::error_code eperm(EPERM, std::generic_category()); +static const std::error_code enoent(ENOENT, std::generic_category()); +static const std::error_code eacces(EACCES, std::generic_category()); +} // namespace fsErrors + +/* In the /sys/devices tree, there are files called "serial", which contain the serial numbers + * for various devices. The exact location inside of this directory is dependent upon the + * hardware we are running on, so we have to start from /sys/devices and work our way down. */ +static const fs::path kDevPath("/sys/devices/"); +static const std::regex kTtyRe("^tty[A-Z]+[0-9]+$"); +static constexpr auto kOpts = ~(fs::directory_options::follow_directory_symlink | + fs::directory_options::skip_permission_denied); + +constexpr auto ok = &ndk::ScopedAStatus::ok; + +/** + * A helper object to associate the interface name and type of a USB to CAN adapter. + */ +struct UsbCanIface { + InterfaceType iftype; + std::string ifaceName; +}; + +static bool isValidName(const std::string& name) { + static const std::regex nameRE("^[a-zA-Z0-9_]{1,32}$"); + return std::regex_match(name, nameRE); +} + +/** + * Given a path, get the last element from it. + * + * \param itrPath - the path we want the last element of + * \return - the last element in the path (in string form). + */ +static std::string getLeaf(const fs::path& itrPath) { + /* end() returns an iterator one past the leaf of the path, so we've overshot + decrement (--) to go back one to the leaf + dereference and now we have our leaf. */ + return *(--(itrPath.end())); +} + +static ndk::ScopedAStatus resultToStatus(Result res, const std::string& msg = "") { + if (msg.empty()) { + return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(static_cast(res))); + } + return ndk::ScopedAStatus( + AStatus_fromServiceSpecificErrorWithMessage(static_cast(res), msg.c_str())); +} + +/** + * Given a UsbCanIface object, get the ifaceName given the serialPath. + * + * \param serialPath - Absolute path to a "serial" file for a given device in /sys. + * \return A populated UsbCanIface. On failure, nullopt is returned. + */ +static std::optional getIfaceName(const fs::path& serialPath) { + std::error_code fsStatus; + // Since the path is to a file called "serial", we need to search its parent directory. + fs::recursive_directory_iterator fsItr(serialPath.parent_path(), kOpts, fsStatus); + if (fsStatus != fsErrors::ok) { + LOG(ERROR) << "Failed to open " << serialPath.parent_path(); + return std::nullopt; + } + + for (; fsStatus == fsErrors::ok && fsItr != fs::recursive_directory_iterator(); + fsItr.increment(fsStatus)) { + /* We want either a directory called "net" or a directory that looks like tty, so + * skip files. */ + bool isDir = fsItr->is_directory(fsStatus); + if (fsStatus != fsErrors::ok || !isDir) continue; + + std::string currentDir = getLeaf(fsItr->path()); + if (currentDir == "net") { + /* This device is a SocketCAN device. The iface name is the only directory under + * net/. Multiple directories under net/ is an error.*/ + fs::directory_iterator netItr(fsItr->path(), kOpts, fsStatus); + if (fsStatus != fsErrors::ok) { + LOG(ERROR) << "Failed to open " << fsItr->path() << " to get net name!"; + return std::nullopt; + } + + // The leaf of our path should be the interface name. + std::string netName = getLeaf(netItr->path()); + + // Check if there is more than one item in net/ + netItr.increment(fsStatus); + if (fsStatus != fsErrors::ok) { + // It's possible we have a valid net name, but this is most likely an error. + LOG(ERROR) << "Failed to verify " << fsItr->path() << " has valid net name!"; + return std::nullopt; + } + if (netItr != fs::directory_iterator()) { + // There should never be more than one name under net/ + LOG(ERROR) << "Found more than one net name in " << fsItr->path() << "!"; + return std::nullopt; + } + return {{InterfaceType::NATIVE, netName}}; + } else if (std::regex_match(currentDir, kTtyRe)) { + // This device is a USB serial device, and currentDir is the tty name. + return {{InterfaceType::SLCAN, "/dev/" + currentDir}}; + } + } + + // check if the loop above exited due to a c++fs error. + if (fsStatus != fsErrors::ok) { + LOG(ERROR) << "Failed search filesystem: " << fsStatus; + } + return std::nullopt; +} + +/** + * A helper function to read the serial number from a "serial" file in /sys/devices/ + * + * \param serialnoPath - path to the file to read. + * \return the serial number, or nullopt on failure. + */ +static std::optional readSerialNo(const std::string& serialnoPath) { + std::ifstream serialnoStream(serialnoPath); + std::string serialno; + if (!serialnoStream.good()) { + LOG(ERROR) << "Failed to read serial number from " << serialnoPath; + return std::nullopt; + } + std::getline(serialnoStream, serialno); + return serialno; +} + +/** + * Searches for USB devices found in /sys/devices/, and attempts to find a device matching the + * provided list of serial numbers. + * + * \param configSerialnos - a list of serial number (suffixes) from the HAL config. + * \param iftype - the type of the interface to be located. + * \return a matching USB device. On failure, std::nullopt is returned. + */ +static std::optional findUsbDevice(const std::vector& configSerialnos) { + std::error_code fsStatus; + fs::recursive_directory_iterator fsItr(kDevPath, kOpts, fsStatus); + if (fsStatus != fsErrors::ok) { + LOG(ERROR) << "Failed to open " << kDevPath; + return std::nullopt; + } + + for (; fsStatus == fsErrors::ok && fsItr != fs::recursive_directory_iterator(); + fsItr.increment(fsStatus)) { + // We want to find a file called "serial", which is in a directory somewhere. Skip files. + bool isDir = fsItr->is_directory(fsStatus); + if (fsStatus != fsErrors::ok) { + LOG(ERROR) << "Failed check if " << fsStatus; + return std::nullopt; + } + if (!isDir) continue; + + auto serialnoPath = fsItr->path() / "serial"; + bool isReg = fs::is_regular_file(serialnoPath, fsStatus); + + /* Make sure we have permissions to this directory, ignore enoent, since the file + * "serial" may not exist, which is ok. */ + if (fsStatus == fsErrors::eperm || fsStatus == fsErrors::eacces) { + /* This means we don't have access to this directory. If we recurse into it, this + * will cause the iterator to loose its state and we'll crash. */ + fsItr.disable_recursion_pending(); + continue; + } + if (fsStatus == fsErrors::enoent) continue; + if (fsStatus != fsErrors::ok) { + LOG(WARNING) << "An unexpected error occurred while checking for serialno: " + << fsStatus; + continue; + } + if (!isReg) continue; + + // we found a serial number + auto serialno = readSerialNo(serialnoPath); + if (!serialno.has_value()) continue; + + // see if the serial number exists in the config + for (auto&& cfgSn : configSerialnos) { + if (serialno->ends_with(std::string(cfgSn))) { + auto ifaceInfo = getIfaceName(serialnoPath); + if (!ifaceInfo.has_value()) break; + return ifaceInfo; + } + } + } + if (fsStatus != fsErrors::ok) { + LOG(ERROR) << "Error searching filesystem: " << fsStatus; + return std::nullopt; + } + return std::nullopt; +} + +ndk::ScopedAStatus CanController::getSupportedInterfaceTypes( + std::vector* supportedTypes) { + *supportedTypes = {InterfaceType::VIRTUAL, InterfaceType::NATIVE, InterfaceType::SLCAN}; + return ok(); +} + +ndk::ScopedAStatus CanController::getInterfaceName(const std::string& busName, + std::string* ifaceName) { + *ifaceName = {}; + if (mBusesByName.find(busName) == mBusesByName.end()) { + return resultToStatus(Result::BAD_BUS_NAME, fmt::format("{} doesn't exist", busName)); + } + *ifaceName = std::string(mBusesByName[busName]->getIfaceName()); + return ok(); +} + +ndk::ScopedAStatus CanController::upBus(const BusConfig& config, std::string* ifaceName) { + if (!isValidName(config.name)) { + LOG(ERROR) << "Bus name " << config.name << " is invalid"; + return resultToStatus(Result::BAD_BUS_NAME, + fmt::format("{} is not a valid bus name", config.name)); + } else if (mBusesByName.find(config.name) != mBusesByName.end()) { + LOG(ERROR) << "A bus named " << config.name << " already exists!"; + return resultToStatus(Result::INVALID_STATE, + fmt::format("A bus named {} already exists", config.name)); + } + + if (config.interfaceId.getTag() == BusConfig::InterfaceId::Tag::virtualif) { + auto& virtualif = config.interfaceId.get(); + mBusesByName[config.name] = std::make_unique(virtualif.ifname); + } + + else if (config.interfaceId.getTag() == BusConfig::InterfaceId::Tag::nativeif) { + auto& nativeif = config.interfaceId.get(); + std::string ifaceName; + if (nativeif.interfaceId.getTag() == NativeInterface::InterfaceId::Tag::serialno) { + // Configure by serial number. + auto selectedDevice = findUsbDevice( + nativeif.interfaceId.get()); + // verify the returned device is the correct one + if (!selectedDevice.has_value() || selectedDevice->iftype != InterfaceType::NATIVE) { + return resultToStatus( + Result::BAD_INTERFACE_ID, + "Couldn't find a native socketcan device with the given serial number(s)"); + } + ifaceName = selectedDevice->ifaceName; + } else { + // configure by iface name. + ifaceName = nativeif.interfaceId.get(); + } + mBusesByName[config.name] = std::make_unique(ifaceName, config.bitrate); + } + + else if (config.interfaceId.getTag() == BusConfig::InterfaceId::Tag::slcan) { + auto& slcanif = config.interfaceId.get(); + std::string ttyName; + if (slcanif.interfaceId.getTag() == SlcanInterface::InterfaceId::Tag::serialno) { + // Configure by serial number. + auto selectedDevice = findUsbDevice( + slcanif.interfaceId.get()); + if (!selectedDevice.has_value() || selectedDevice->iftype != InterfaceType::SLCAN) { + return resultToStatus( + Result::BAD_INTERFACE_ID, + "Couldn't find a slcan device with the given serial number(s)"); + } + ttyName = selectedDevice->ifaceName; + } else { + // Configure by tty name. + ttyName = slcanif.interfaceId.get(); + } + mBusesByName[config.name] = std::make_unique(ttyName, config.bitrate); + } + + else if (config.interfaceId.getTag() == BusConfig::InterfaceId::Tag::indexed) { + return resultToStatus(Result::NOT_SUPPORTED, + "Indexed devices are not supported in this implementation"); + } else { + // this shouldn't happen. + return resultToStatus(Result::UNKNOWN_ERROR, "Unknown interface id type"); + } + + Result result = mBusesByName[config.name]->up(); + if (result != Result::OK) { + // the bus failed to come up, don't leave a broken entry in the map. + mBusesByName.erase(config.name); + return resultToStatus(result, fmt::format("CanBus::up failed for {}", config.name)); + } + + *ifaceName = mBusesByName[config.name]->getIfaceName(); + return ok(); +} + +ndk::ScopedAStatus CanController::downBus(const std::string& busName) { + if (mBusesByName.find(busName) == mBusesByName.end()) { + return resultToStatus( + Result::UNKNOWN_ERROR, + fmt::format("Couldn't bring down {}, because it doesn't exist", busName)); + } + Result result = mBusesByName[busName]->down(); + if (result != Result::OK) { + return resultToStatus(result, fmt::format("Couldn't bring down {}!", busName)); + } + mBusesByName.erase(busName); + return ok(); +} +} // namespace aidl::android::hardware::automotive::can diff --git a/automotive/can/aidl/default/CanController.h b/automotive/can/aidl/default/CanController.h new file mode 100644 index 0000000000..784906e589 --- /dev/null +++ b/automotive/can/aidl/default/CanController.h @@ -0,0 +1,45 @@ +/* + * Copyright 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "CanBus.h" + +#include + +#include +#include + +namespace aidl::android::hardware::automotive::can { + +class CanController : public BnCanController { + public: + ndk::ScopedAStatus getSupportedInterfaceTypes( + std::vector* supportedTypes) override; + + ndk::ScopedAStatus getInterfaceName(const std::string& busName, + std::string* ifaceName) override; + + ndk::ScopedAStatus upBus(const BusConfig& config, std::string* ifaceName) override; + + ndk::ScopedAStatus downBus(const std::string& busName) override; + + private: + std::map> mBusesByName = {}; +}; +} // namespace aidl::android::hardware::automotive::can diff --git a/automotive/can/aidl/default/android.hardware.automotive.can.rc b/automotive/can/aidl/default/android.hardware.automotive.can.rc new file mode 100644 index 0000000000..f8437528b3 --- /dev/null +++ b/automotive/can/aidl/default/android.hardware.automotive.can.rc @@ -0,0 +1,5 @@ +service android.hardware.automotive.can /vendor/bin/hw/android.hardware.automotive.can-service + class hal + capabilities NET_ADMIN + user vehicle_network + group system inet diff --git a/automotive/can/aidl/default/android.hardware.automotive.can.xml b/automotive/can/aidl/default/android.hardware.automotive.can.xml new file mode 100644 index 0000000000..873f3335b9 --- /dev/null +++ b/automotive/can/aidl/default/android.hardware.automotive.can.xml @@ -0,0 +1,26 @@ + + + + + + android.hardware.automotive.can + 1 + + ICanController + default + + + diff --git a/automotive/can/aidl/default/service.cpp b/automotive/can/aidl/default/service.cpp new file mode 100644 index 0000000000..eb45167e44 --- /dev/null +++ b/automotive/can/aidl/default/service.cpp @@ -0,0 +1,43 @@ +/* + * Copyright 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CanController.h" + +#include +#include +#include + +namespace android::hardware::automotive::can { + +using namespace std::string_literals; +using ::aidl::android::hardware::automotive::can::CanController; + +extern "C" int main() { + base::SetDefaultTag("CanController"); + base::SetMinimumLogSeverity(base::VERBOSE); + + LOG(VERBOSE) << "Starting up..."; + auto service = ndk::SharedRefBase::make(); + const auto instance = CanController::descriptor + "/default"s; + const auto status = AServiceManager_addService(service->asBinder().get(), instance.c_str()); + CHECK_EQ(status, STATUS_OK) << "Failed to add service " << instance; + LOG(VERBOSE) << "Started successfully!"; + + ABinderProcess_joinThreadPool(); + LOG(FATAL) << "CanController exited unexpectedly!"; + return EXIT_FAILURE; +} +} // namespace android::hardware::automotive::can diff --git a/automotive/can/aidl/default/tools/configurator/Android.bp b/automotive/can/aidl/default/tools/configurator/Android.bp new file mode 100644 index 0000000000..1169894a28 --- /dev/null +++ b/automotive/can/aidl/default/tools/configurator/Android.bp @@ -0,0 +1,43 @@ +// +// Copyright (C) 2022 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "hardware_interfaces_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["hardware_interfaces_license"], +} + +cc_binary { + name: "canhalconfigurator-aidl", + init_rc: ["canhalconfigurator-aidl.rc"], + defaults: ["android.hardware.automotive.can@defaults"], + srcs: [ + "canhalconfigurator.cpp", + "canprototools.cpp", + ], + shared_libs: [ + "libbase", + "libbinder_ndk", + "libprotobuf-cpp-full", + ], + static_libs: [ + "android.hardware.automotive.can-V1-ndk", + "android.hardware.automotive.can-aidl-config-format", + ], +} diff --git a/automotive/can/aidl/default/tools/configurator/canhalconfigurator-aidl.rc b/automotive/can/aidl/default/tools/configurator/canhalconfigurator-aidl.rc new file mode 100644 index 0000000000..e1b4d3548a --- /dev/null +++ b/automotive/can/aidl/default/tools/configurator/canhalconfigurator-aidl.rc @@ -0,0 +1,3 @@ +service canhalconfigurator /system/bin/canhalconfigurator-aidl + class core + oneshot diff --git a/automotive/can/aidl/default/tools/configurator/canhalconfigurator.cpp b/automotive/can/aidl/default/tools/configurator/canhalconfigurator.cpp new file mode 100644 index 0000000000..94e77b4a48 --- /dev/null +++ b/automotive/can/aidl/default/tools/configurator/canhalconfigurator.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "canbus_config.pb.h" +#include "canprototools.h" + +#include +#include +#include + +#include +#include + +namespace android::hardware::automotive::can { + +using namespace std::string_literals; +using ::aidl::android::hardware::automotive::can::ICanController; + +static constexpr std::string_view kDefaultConfigPath = "/etc/canbus_config.pb"; + +/** + * Takes output from parsed protobuf config and uses it to configure the CAN HAL. + * + * \param pb_cfg is an instance of the autogenerated protobuf object for our configuration. + * \return boolean status, true on success, false on failure. + */ +static bool processPbCfg(const config::CanBusConfig& pb_cfg) { + for (auto const& bus : pb_cfg.buses()) { + if (bus.name().empty()) { + LOG(ERROR) << "Invalid config: Bus config must have a valid name field"; + return false; + } + + auto busCfgMaybe = config::fromPbBus(bus); + if (!busCfgMaybe.has_value()) { + return false; + } + auto busCfg = *busCfgMaybe; + + const auto instance = ICanController::descriptor + "/default"s; + const auto service = ICanController::fromBinder( + ndk::SpAIBinder(AServiceManager_waitForService(instance.c_str()))); + if (service == nullptr) { + LOG(FATAL) << "Can't find CAN HAL! (has it started yet?)"; + return false; + } + + LOG(VERBOSE) << "Bringing up a " << busCfg.name << " @ " << busCfg.bitrate; + + std::string ifaceName; + const auto status = service->upBus(busCfg, &ifaceName); + if (!status.isOk() && status.getExceptionCode() != EX_SERVICE_SPECIFIC) { + LOG(FATAL) << "Binder transaction failed!" << status.getStatus(); + return false; + } else if (!status.isOk()) { + LOG(ERROR) << "upBus failed: " << config::resultStringFromStatus(status) << ": " + << status.getMessage(); + continue; + } + + LOG(INFO) << bus.name() << " has been successfully configured on " << ifaceName; + } + return true; +} + +/** + * This kicks off the CAN HAL configuration process. This starts the following: + * 1. Reading the config file + * 2. Setting up CAN buses + * 3. Handling services + * \param filepath is a string specifying the absolute path of the config file + * \return boolean status, true on success, false on failure + */ +static bool configuratorStart(const std::string& filepath) { + base::SetDefaultTag("CanConfigurator"); + auto pbCfg = config::parseConfigFile(filepath); + if (!pbCfg.has_value()) { + return false; + } + // process the rest of the config file data and configure the CAN buses. + if (!processPbCfg(*pbCfg)) { + return false; + } + LOG(INFO) << "CAN HAL has been configured!"; + return true; +} + +extern "C" int main(int argc, char* argv[]) { + std::string configFilepath = static_cast(kDefaultConfigPath); + + // allow for CLI specification of a config file. + if (argc == 2) { + configFilepath = argv[1]; + } else if (argc > 2) { + std::cerr << "usage: " << argv[0] << " [optional config filepath]"; + return 1; + } + + if (!configuratorStart(configFilepath)) { + return 1; + } + return 0; +} + +} // namespace android::hardware::automotive::can diff --git a/automotive/can/aidl/default/tools/configurator/canprototools.cpp b/automotive/can/aidl/default/tools/configurator/canprototools.cpp new file mode 100644 index 0000000000..2aea315391 --- /dev/null +++ b/automotive/can/aidl/default/tools/configurator/canprototools.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "canprototools.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace android::hardware::automotive::can::config { + +using ::aidl::android::hardware::automotive::can::BusConfig; +using ::aidl::android::hardware::automotive::can::IndexedInterface; +using ::aidl::android::hardware::automotive::can::InterfaceType; +using ::aidl::android::hardware::automotive::can::NativeInterface; +using ::aidl::android::hardware::automotive::can::Result; +using ::aidl::android::hardware::automotive::can::SlcanInterface; +using ::aidl::android::hardware::automotive::can::VirtualInterface; + +/** + * Helper function for parseConfigFile. readString is used to get the fist n characters (n) from an + * istream object (s) and return it as a string object. + * + * \param s istream of the file you intend to read. + * \param n streamsize object of the number of characters you'd like. + * \return optional string containing up to n characters from the stream(s) you provided. + */ +static std::optional readString(std::istream& s, std::streamsize n) { + char buff[n]; + auto got = s.read(buff, n).gcount(); + if (!s.good() && !s.eof()) return std::nullopt; + return std::string(buff, 0, std::min(n, got)); +} + +std::optional parseConfigFile(const std::string& filepath) { + std::ifstream cfg_stream(filepath); + + // text headers that would be present in a plaintext proto config file. + static const std::array text_headers = {"buses", "#", "controller"}; + auto cfg_file_snippet = readString(cfg_stream, 10); + + if (!cfg_file_snippet.has_value()) { + LOG(ERROR) << "Can't open " << filepath << " for reading"; + return std::nullopt; + } + cfg_stream.seekg(0); + + // check if any of the textHeaders are at the start of the config file. + bool text_format = false; + for (auto const& header : text_headers) { + if (cfg_file_snippet->compare(0, header.length(), header) == 0) { + text_format = true; + break; + } + } + + CanBusConfig config; + if (text_format) { + google::protobuf::io::IstreamInputStream pb_stream(&cfg_stream); + if (!google::protobuf::TextFormat::Parse(&pb_stream, &config)) { + LOG(ERROR) << "Failed to parse (text format) " << filepath; + return std::nullopt; + } + } else if (!config.ParseFromIstream(&cfg_stream)) { + LOG(ERROR) << "Failed to parse (binary format) " << filepath; + return std::nullopt; + } + return config; +} + +std::optional fromPbBus(const Bus& pb_bus) { + BusConfig bus_cfg = {}; + bus_cfg.name = pb_bus.name(); + + switch (pb_bus.iface_type_case()) { + case Bus::kNative: { + const std::string ifname = pb_bus.native().ifname(); + const std::vector serials = {pb_bus.native().serialno().begin(), + pb_bus.native().serialno().end()}; + if (ifname.empty() == serials.empty()) { + LOG(ERROR) << "Invalid config: native type bus must have an iface name xor a " + << "serial number"; + return std::nullopt; + } + bus_cfg.bitrate = pb_bus.bitrate(); + NativeInterface nativeif = {}; + if (!ifname.empty()) + nativeif.interfaceId.set(ifname); + if (!serials.empty()) + nativeif.interfaceId.set(serials); + bus_cfg.interfaceId.set(nativeif); + break; + } + case Bus::kSlcan: { + const std::string ttyname = pb_bus.slcan().ttyname(); + const std::vector serials = {pb_bus.slcan().serialno().begin(), + pb_bus.slcan().serialno().end()}; + if (ttyname.empty() == serials.empty()) { + LOG(ERROR) << "Invalid config: slcan type bus must have a tty name xor a serial " + << "number"; + return std::nullopt; + } + bus_cfg.bitrate = pb_bus.bitrate(); + SlcanInterface slcan = {}; + if (!ttyname.empty()) + slcan.interfaceId.set(ttyname); + if (!serials.empty()) + slcan.interfaceId.set(serials); + bus_cfg.interfaceId.set(slcan); + break; + } + case Bus::kVirtual: { + // Theoretically, we could just create the next available vcan iface. + const std::string ifname = pb_bus.virtual_().ifname(); + if (ifname.empty()) { + LOG(ERROR) << "Invalid config: native type bus must have an iface name"; + return std::nullopt; + } + VirtualInterface virtualif = {}; + virtualif.ifname = ifname; + bus_cfg.interfaceId.set(virtualif); + break; + } + case Bus::kIndexed: { + const uint8_t index = pb_bus.indexed().index(); + if (index > UINT8_MAX) { + LOG(ERROR) << "Interface index out of range: " << index; + return std::nullopt; + } + IndexedInterface indexedif = {}; + indexedif.index = index; + bus_cfg.interfaceId.set(indexedif); + break; + } + default: + LOG(ERROR) << "Invalid config: bad interface type for " << bus_cfg.name; + return std::nullopt; + } + return bus_cfg; +} + +std::optional getHalIftype(const Bus& pb_bus) { + switch (pb_bus.iface_type_case()) { + case Bus::kNative: + return InterfaceType::NATIVE; + case Bus::kSlcan: + return InterfaceType::SLCAN; + case Bus::kVirtual: + return InterfaceType::VIRTUAL; + case Bus::kIndexed: + return InterfaceType::INDEXED; + default: + return std::nullopt; + } +} + +std::string resultStringFromStatus(const ndk::ScopedAStatus& status) { + const auto res = static_cast(status.getServiceSpecificError()); + switch (res) { + case Result::OK: + return "OK"; + case Result::UNKNOWN_ERROR: + return "UNKNOWN_ERROR"; + case Result::INVALID_STATE: + return "INVALID_STATE"; + case Result::NOT_SUPPORTED: + return "NOT_SUPPORTED"; + case Result::BAD_INTERFACE_ID: + return "BAD_INTERFACE_ID"; + case Result::BAD_BITRATE: + return "BAD_BITRATE"; + case Result::BAD_BUS_NAME: + return "BAD_BUS_NAME"; + case Result::INTERFACE_DOWN: + return "INTERFACE_DOWN"; + default: + return "Invalid Result!"; + } +} + +} // namespace android::hardware::automotive::can::config diff --git a/automotive/can/aidl/default/tools/configurator/canprototools.h b/automotive/can/aidl/default/tools/configurator/canprototools.h new file mode 100644 index 0000000000..377ee7f92a --- /dev/null +++ b/automotive/can/aidl/default/tools/configurator/canprototools.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "canbus_config.pb.h" + +#include +#include +#include + +namespace android::hardware::automotive::can::config { + +/** + * This reads the protobuf config file into a protobuf object. Both text based protobuf files as + * well as binary format protobuf files are supported. + * + * \param filepath string containing the name of the config file to read. + * \return a CanBusConfig protobuf object constructed from the config file. + */ +std::optional parseConfigFile(const std::string& filepath); + +/** + * Converts protobuf format single-bus config object to a HAL bus config object. + * + * \param pb_bus is the protobuf object representing a the configuration of one CAN bus. + * \return a converted HAL bus config object. + */ +std::optional<::aidl::android::hardware::automotive::can::BusConfig> fromPbBus(const Bus& pb_bus); + +/** + * Get the CAN HAL interface type specified by a given protobuf config object. + * + * \param pb_bus is the protobuf object representing a the configuration of one CAN bus. + * \return the CAN HAL interface type. + */ +std::optional<::aidl::android::hardware::automotive::can::InterfaceType> getHalIftype( + const Bus& pb_bus); + +std::string resultStringFromStatus(const ndk::ScopedAStatus& status); + +} // namespace android::hardware::automotive::can::config diff --git a/automotive/can/aidl/default/tools/configurator/proto/Android.bp b/automotive/can/aidl/default/tools/configurator/proto/Android.bp new file mode 100644 index 0000000000..da1b37c664 --- /dev/null +++ b/automotive/can/aidl/default/tools/configurator/proto/Android.bp @@ -0,0 +1,37 @@ +// +// Copyright (C) 2022 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "hardware_interfaces_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["hardware_interfaces_license"], +} + +cc_library_static { + name: "android.hardware.automotive.can-aidl-config-format", + defaults: ["android.hardware.automotive.can@defaults"], + proto: { + export_proto_headers: true, + type: "full", + }, + strip: { + keep_symbols: true, + }, + srcs: ["canbus_config.proto"], +} diff --git a/automotive/can/aidl/default/tools/configurator/proto/canbus_config.proto b/automotive/can/aidl/default/tools/configurator/proto/canbus_config.proto new file mode 100644 index 0000000000..b03b03567f --- /dev/null +++ b/automotive/can/aidl/default/tools/configurator/proto/canbus_config.proto @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto3"; + +package android.hardware.automotive.can.config; + +message IfaceNative { + string ifname = 1; + repeated string serialno = 2; +}; + +message IfaceSlcan { + string ttyname = 1; + repeated string serialno = 2; +}; + +message IfaceVirtual { + string ifname = 1; +}; + +message IfaceIndexed { + uint32 index = 1; +}; + +message Bus { + string name = 1; // this is the name presented in the HAL + oneof iface_type { + IfaceNative native = 2; + IfaceSlcan slcan = 3; + IfaceVirtual virtual = 4; + IfaceIndexed indexed = 5; + } + uint32 bitrate = 6; +}; + +message CanBusConfig { + repeated Bus buses = 1; +}; diff --git a/automotive/can/aidl/vts/functional/Android.bp b/automotive/can/aidl/vts/functional/Android.bp new file mode 100644 index 0000000000..b816a4952e --- /dev/null +++ b/automotive/can/aidl/vts/functional/Android.bp @@ -0,0 +1,51 @@ +// +// Copyright (C) 2022 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "hardware_interfaces_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["hardware_interfaces_license"], +} + +cc_test { + name: "VtsHalCanControllerV1_0Test", + defaults: [ + "VtsHalTargetTestDefaults", + "android.hardware.automotive.can@defaults", + "use_libaidlvintf_gtest_helper_static", + ], + cpp_std: "experimental", + srcs: [ + "CanControllerAidlTest.cpp", + ], + shared_libs: [ + "libbase", + "libbinder_ndk", + ], + static_libs: [ + "android.hardware.automotive.can-V1-ndk", + "android.hardware.automotive.can@libnetdevice", + "libnl++", + "libgmock", + ], + test_suites: [ + "general-tests", + "vts", + ], +} diff --git a/automotive/can/aidl/vts/functional/CanControllerAidlTest.cpp b/automotive/can/aidl/vts/functional/CanControllerAidlTest.cpp new file mode 100644 index 0000000000..c2b28795b2 --- /dev/null +++ b/automotive/can/aidl/vts/functional/CanControllerAidlTest.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using aidl::android::hardware::automotive::can::BusConfig; +using aidl::android::hardware::automotive::can::ICanController; +using aidl::android::hardware::automotive::can::VirtualInterface; +using namespace std::chrono_literals; +using namespace std::string_literals; + +class CanControllerAidlTest : public ::testing::TestWithParam { + public: + virtual void SetUp() override { + android::base::SetDefaultTag("CAN_HAL_VTS"); + android::base::SetMinimumLogSeverity(android::base::VERBOSE); + const auto instance = ICanController::descriptor + "/default"s; + mCanControllerService = ICanController::fromBinder( + ndk::SpAIBinder(AServiceManager_waitForService(instance.c_str()))); + + ASSERT_NE(mCanControllerService, nullptr); + } + virtual void TearDown() override {} + + static bool mTestCaseInitialized; + std::shared_ptr mCanControllerService; +}; + +// we can't test a real bus, since we can't make any assumptions about hardware +// this checks upBus, getInterfaceName, and downBus +TEST_P(CanControllerAidlTest, ToggleBus) { + const std::string_view canIface = "vcan50"; + const std::string busName = "VTS_CAN"; + + std::string upBusReturn; // should be vcan50 + BusConfig config = {}; + VirtualInterface iface = {}; + iface.ifname = canIface; + config.interfaceId.set(iface); + config.name = busName; + auto aidlStatus = mCanControllerService->upBus(config, &upBusReturn); + ASSERT_TRUE(aidlStatus.isOk()); + EXPECT_EQ(upBusReturn, canIface); + + std::string ifaceName; + aidlStatus = mCanControllerService->getInterfaceName(busName, &ifaceName); + ASSERT_TRUE(aidlStatus.isOk()); + EXPECT_EQ(ifaceName, canIface); + + aidlStatus = mCanControllerService->downBus(busName); + ASSERT_TRUE(aidlStatus.isOk()); +} + +TEST_P(CanControllerAidlTest, GetSupported) { + LOG(VERBOSE) << "Get the supported iface types"; + std::vector<::aidl::android::hardware::automotive::can::InterfaceType> supportedTypes; + auto aidlStatus = mCanControllerService->getSupportedInterfaceTypes(&supportedTypes); + ASSERT_TRUE(aidlStatus.isOk()); + EXPECT_FALSE(supportedTypes.empty()); +} + +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CanControllerAidlTest); +INSTANTIATE_TEST_SUITE_P( + PerInstance, CanControllerAidlTest, + testing::ValuesIn(android::getAidlHalInstanceNames(ICanController::descriptor)), + android::PrintInstanceNameToString); diff --git a/automotive/can/aidl/vts/functional/OWNERS b/automotive/can/aidl/vts/functional/OWNERS new file mode 100644 index 0000000000..85257a3537 --- /dev/null +++ b/automotive/can/aidl/vts/functional/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 533426 +twasilczyk@google.com +chrisweir@google.com diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml index dc87c63789..ebc3dffda8 100644 --- a/compatibility_matrices/compatibility_matrix.current.xml +++ b/compatibility_matrices/compatibility_matrix.current.xml @@ -68,6 +68,14 @@ default + + android.hardware.automotive.can + 1 + + ICanController + default + + android.hardware.automotive.can 1.0