提交 e9f81422 编写于 作者: W wusongqing

updated docs

Signed-off-by: Nwusongqing <wusongqing@huawei.com>
上级 be5bdb7e
# 32- and 64-Bit Portability Coding Guide
## About This Document
### Purpose
OpenHarmony aims to build an open, distributed OS framework for smart IoT devices in the full-scenario, full-connectivity, and full-intelligence era. It has the following technical features: hardware collaboration for resource sharing, one-time development for multi-device deployment, and a unified OS for flexible deployment.
OpenHarmony supports the following system types:
1. Mini system: a device oriented to devices that have MCU processors such as ARM Cortex-M and 32-bit RISC-V. These devices have limited hardware resources. The minimum device memory is 128 KiB.
2. Small system: a device oriented to devices that have application processors such as 64-bit ARM Cortex-A. The minimum device memory is 1 MiB.
3. Standard system: a device oriented to devices that have application processors such as 64-bit ARM Cortex-A. The minimum device memory is 128 MiB.
OpenHarmony code can run on 32- and 64-bit devices. For easier code portability, clear coding specifications are necessary. This document stipulates the specifications on code porting and 64-bit coding, helping you improve code standardization and portability.
### Application Scope
C and C++ code for the user space and kernel space, without distinguishing the programming language standards.
### Differences Between 32-Bit OS and 64-Bit OS
#### Data Type Differences
Most 32-bit OSs use ILP32, which defines int, long, and pointer as 32 bits. Most 64-bit OSs use LP64, which defines long, long long, and pointer as 64 bits. The Windows OS uses LLP64, which defines long long and pointer as 64 bits. The table below lists the length of each primitive data type.
| **Type**| **ILP32** | **LP64** | **LLP64** | **LP32** | **ILP64** |
| --------- | --------- | -------- | --------- | -------- | --------- |
| char | 1 | 1 | 1 | 1 | 1 |
| short | 2 | 2 | 2 | 2 | 2 |
| int | 4 | 4 | 4 | 2 | 8 |
| long | **4** | **8** | **4** | 4 | 8 |
| long long | 8 | 8 | 8 | 8 | 8 |
| float | 4 | 4 | 4 | 4 | 4 |
| double | 8 | 8 | 8 | 8 | 8 |
| size_t | **4** | **8** | **8** | 4 | 8 |
| pointer | **4** | **8** | **8** | 4 | 8 |
The table below lists the length of `sizeof` and `print` in ILP32 and LP64 to show the differences between constants and types.
| Type | ILP32 sizeof | ILP32 print | LP64 sizeof | LP64 print | Remarks|
| ------------------ | ------------ | ----------- | ----------- | ---------- | ------ |
| bool | 1 | %u | 1 | %u | C++ |
| char | 1 | %d or %c| 1 | %d or %c| |
| unsigned char | 1 | %u | 1 | %u | |
| short | 2 | %d | 2 | %d | |
| unsigned short | 2 | %u | 2 | %u | |
| int | 4 | %d | 4 | %d | |
| unsigned int | 4 | %u | 4 | %u | |
| long | 4 | %ld | **8** | %ld | Differences exist.|
| unsigned long | 4 | %lu | **8** | %lu | Differences exist.|
| long int | 4 | %ld | **8** | %ld | Differences exist.|
| unsigned long int | 4 | %lu | **8** | %lu | Differences exist.|
| long long | 8 | %lld | 8 | %lld | |
| unsigned long long | 8 | %llu | 8 | %llu | |
| type * | 4 | %p | **8** | %p | Differences exist.|
| pid_t | 4 | %d | 4 | %d | |
| socklen_t | 4 | %u | 4 | %u | |
| off_t | 4 | %zd | **8** | %zd | Differences exist.|
| time_t | 4 | %zd | 8 | %zd | Differences exist.|
| pthread_t | 4 | %zu | **8** | %zu | Differences exist.|
| size_t | 4 | %zu | 8 | %zu | Differences exist.|
| ssize_t | 4 | %zd | **8** | %zd | Differences exist.|
#### Differences in Data Structure Alignment
##### Alignment of the data structure that contains a pointer
```c
typedef struct tagFoo {
void *p;
uint32_t i;
} Foo;
```
On a 32-bit OS, the pointer length is 4, **Foo** is 4-byte aligned, and **sizeof(Foo)** is 8. On a 64-bit OS, the pointer length is 8, **Foo** is 8-byte aligned, and **sizeof(Foo)** is 16.
##### Alignment of the data structure that contains a 64-bit integer
```c
typedef struct tagFoo {
uint64_t p;
uint32_t i;
} Foo;
```
Although uint64\_t has a fixed length, **sizeof(Foo)** is different due to alignment. On a 32-bit OS, **Foo** is 4-byte aligned, and **sizeof(Foo)** is 12. On a 64-bit OS, **Foo** is 8-byte aligned, and **sizeof(Foo)** is 16. The preceding statement is also true when uint64\_t is replaced by double.
### Conventions
**Rule**: Conventions that must be complied with during programming.
**Rec**: Conventions that should be complied with during programming.
## Coding Guide
### General Principles
#### [Rule] Follow this coding guide to write code that is applicable to both 32- and 64-bit OSs.
[Description] OpenHarmony will run in both 32- and 64-bit environments for a long time. To ensure code consistency, you must consider code portability during coding.
### Data Type Definition and Formatting
#### [Rule] Use the defined data types to define variables, and avoid custom basic data types without special meanings or requirements.
[Description] Data types with variable lengths may cause incompatibility issues on 32- and 64-bit OSs. Unified and clear data types are required. OpenHarmony defines the following primitive data types:
| Type| ILP32 | LP64 | Print| Use Case|
| ---------------- | ----- | ----- | ------- | ------------------------------------------------------------ |
| void | - | - | - | Used only for placeholder and general pointer definition.|
| char | 1 | 1 | %c | Used for strings and arrays.|
| int8_t | 1 | 1 | %d | Used for 1-byte integers.|
| uint8_t | 1 | 1 | %u | Used for 1-byte integers.|
| int16_t | 2 | 2 | %d | Used to replace short.|
| uint16_t | 2 | 2 | %u | Used to replace unsigned short.|
| int32_t | 4 | 4 | %d | Used to replace int.|
| uint32_t | 4 | 4 | %u | Used to replace unsigned int.|
| int64_t | 8 | 8 | %PRId64 | Used to replace long long and macros.|
| uint64_t | 8 | 8 | %PRIu64 | Used to replace unsigned long long and macros.|
| float | 4 | 4 | %f | Used for single-precision floating point numbers.|
| double | 8 | 8 | %lf | Used for double-precision floating point numbers.|
| bool | 1 | 1 | %d | Used for Boolean.|
| uintptr_t | **4** | **8** | %zu | Used for pointer storage. Different lengths are defined for 32- and 64-bit OSs.|
| type * | **4** | **8** | %p | Variable-length type. It is equivalent to uintptr_t, which is recommended for type conversion.|
| nullptr_t | **4** | **8** | %p | Used for pointer initialization.|
| pid_t | 4 | 4 | %d | Built-in for the Linux kernel. It has a fixed length.|
| socklen_t | 4 | 4 | %u | Built-in for the Linux kernel. It has a fixed length.|
| off_t/time_t | **4** | **8** | %zd | Signed, variable-length type.|
| size_t/pthread_t | **4** | **8** | %zu | Unsigned, variable-length type. It is used only for compatibility of calling library functions (for example, size_t is used in an underlying API).|
The preceding types are defined in the **stddef.h** (C) and **cstdint** (C++) standard libraries. When `#define` is used to redefine related types, the source must be one of these types.
Do not use non-standard types unless otherwise specified. Do not define common basic types unless they have special meanings. For primitive data types used in third-party interfaces and API calling, refer to their respective rules.
[Example] Do not customize primitive data types unless necessary.
```c
// Do not use the following code to redefine a data type.
typedef unsigned int UINT32;// This definition is forbidden.
// The preceding definition can be replaced by uint32_t. However, you can define a type that has a specific meaning.
typedef uint32_t DB_TABLE_ID; // This definition can be retained.
```
[Example] Do not use the following two types, because their lengths are different from the common sense.
| Type| ILP32 | LP64 | PRINT | Use Case|
| -------- | ------ | ----- | ----- | ---------------------- |
| float_t | **12** | **4** | - | Improper length. Do not use it.|
| double_t | **12** | **8** | - | Improper length. Do not use it.|
#### [Rec] Do not use custom variable-length variables. A clear description is required if they are used to adapt to platforms or third-party interfaces.
[Description] The native types such as long, int, short, and size_t have different lengths in the 32- and 64-bit environments. This may cause code risks. If these types are used for external storage or communication, system incompatibility may occur. Therefore, do not use them unless otherwise specified.
[Exception] Due to platform, third-party interface, or library function reasons, they can be used with a clear description.
[Example]
```c
long var;
// This data type is 4-byte long on a 32-bit OS and 8-byte long on a 64-bit OS. You are advised to use uint32_t or uint64_t instead.
```
#### [Rule] Do not use uchar to define variables.
[Description] It is not standard to use uchar or unsigned char to define strings. Instead, use char for strings or uint8_t for unsigned integers.
For non-ANSI character sequences involving 8-bit encoding, char is recommended. In C++, wchar can be used.
#### [Rule] Define an integer variable that is used to store a pointer as uintptr_t.
[Description] The uintptr_t type is used to store data of the pointer length. The length can be automatically adjusted in the 32- and 64-bits environments.
[Example]
```c
uintptr_t sessionPtr;
// When the pointer is stored as a variable, both the forced type conversion and left value should be defined as uintptr_t to adapt to the length changes in different scenarios.
sessionPtr = (uintptr_t) GetMemAddress();
```
#### [Rec] When the type defined by the input parameter or return value of a function does not match the variable type, exercise caution to ensure that the result is correct after conversion following the value assignment and type conversion rules.
[Description] If type inconsistency is inevitable, convert the type with caution to ensure that the final result meets the application requirements.
[Example]
```c
long function (long l);
int main () {
int i = -2;
unsigned int k = 1U;
long n = function(i + k);
}
```
Note: The preceding code will fail to be executed on a 64-bit OS because the expression (i + k) is an unsigned 32-bit expression. When it is converted to the long type, the symbols are not extended. The result of the input parameter is incorrect. The solution is to forcibly convert one of the operands to a 64-bit type.
#### [Rule] Use 64-bit compatible macros, such as %PRId64, %PRIu64, and %PRIx64, to print 64-bit integers. Incompatible macros, such as %d, %ld, %zd, %x, and %lx, are not allowed.
[Description] If the data to be formatted is of the 64-bit type (defined as uint64_t), format the output as follows:
```c
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main()
{
uint64_t a = 0x1234567fffffff;
printf("a = %"PRIx64"\n", a);
return 0;
}
```
The preceding code can output 64-bit numbers in both 32- and 64-bit environments. If other formatting methods are used, compatibility issues may occur, as listed in the following table.
| Formatting Method| ILP32 Build| ILP32 Result| LP64 Build| LP64 Result| Conclusion|
| ---------- | -------------- | --------- | -------------- | -------- | ---------- |
| %lx | Type mismatch alarm| Incorrect| No alarm| Correct| Incompatible|
| %zx | Type mismatch alarm| Incorrect| No alarm| Correct| Incompatible|
| %llx | No alarm| Correct| Type mismatch alarm| Correct| Incompatible|
| %p | Type mismatch alarm| Incorrect| Type mismatch alarm| Correct| Incompatible|
| %PRIx64 | No alarm| Correct| No alarm| Correct| Compatible|
[Example]The following is an example of the type mismatch alarm information:
```bash
# Build on a 32-bit OS
format '%lx' expects argument of type 'long unsigned int', but argument 2 has type 'uint64_t {aka long long unsigned int}'
format '%zx' expects argument of type 'size_t', but argument 2 has type 'uint64_t {aka long long unsigned int}'
format '%p' expects argument of type 'void *', but argument 2 has type 'uint64_t {aka long long unsigned int}'
# Build on a 64-bit OS
format '%llx' expects argument of type 'long long unsigned int', but argument 2 has type 'uint64_t {aka long unsigned int}'
format '%p' expects argument of type 'void *', but argument 2 has type 'uint64_t {aka long unsigned int}'
```
#### [Rule] When printing type-variable data, consider the data length and reserve sufficient space during alignment.
[Description] On a 32-bit OS, the maximum length of the pointer and size_t is 8 bits (hexadecimal) or 10 bits (decimal). On a 64-bit OS, their maximum length is 20 bits. Therefore, you need to consider the length range when printing the data.
#### [Rule] Do not use L/UL as the suffix of constants. You can add the suffix U to indicate the unsigned int type and the suffix LL/ULL to indicate the 64-bit length.
[Description] By default, constants without suffixes are of the int type. When L/UL is used as the suffix, the length may change on 32- and 64-bit OSs. Therefore, use LL/ULL as the suffix to ensure that the length is fixed at 64 bits in both the 32- and 64-bit environments.
| Constant| ILP32 | LP64 | Scenario|
| -------------- | --------- | ----- | ------------------------------------------------------------ |
| 1 | 4 | 4 | The default type is int32_t, and the length is fixed.|
| 1U | 4 | 4 | The default type is uint32_t, and the length is fixed.|
| 1L | **4** | **8** | The suffix is L. The length is not fixed. Do not use this constant.|
| 1UL | **4** | **8** | The suffix is UL. The length is not fixed. Do not use this constant.|
| 1LL | 8 | 8 | The default type is int64_t. The value is a long integer, and the length is fixed at 64 bits.|
| 1ULL | 8 | 8 | The type is uint64_t, and the value is an unsigned long integer.|
| 0x7FFFFFFF | 4 | 4 | The value is an unsigned number. It can be used within the value range of int32\_t. The default type is int32\_t. |
| 0x7FFFFFFFL | **4** | **8** | The length is not fixed. Do not use this constant.|
| 0x7FFFFFFFLL | 8 | 8 | The length is fixed.|
| 0x80000000 | 4 | 4 | It can be used within the value range of uint32\_t. The type is uint32\_t.|
| 0x80000000L | **4** | **8** | The suffix is L. It can be used within the value range of uint32\_t. This constant is meaningless and should be avoided.|
| 0x80000000LL | 8 | 8 | The suffix is LL. It can be used within the value range of uint32\_t. The length is fixed.|
| 0x8000000000 | **NA or 8**| **8** | No prefix. It can be used when the value range of uint32\_t is exceeded. The compiler uses LL by default or considers the value invalid. On a 64-bit OS, the type is fixed at uint64\_t.|
| 0x8000000000L | **NA or 8**| **8** | The suffix is L. It can be used when the value range of uint32_t is exceeded. This constant is meaningless and should be avoided.|
| 0x8000000000LL | 8 | 8 | The suffix is LL. The type is uint64_t.|
As shown in the preceding table, a constant with the L or UL suffix has different lengths in the 32- and 64-bit environments. This hinders code portability. Therefore, do not use the L or UL suffix.
[Example]
```c
// UL in the following definition is meaningless. It causes an error on a 32-bit OS and is unnecessary on a 64-bit OS.
#define YYY_START_ADDRESS(XXX_START_ADDR + 0x80000000UL)
```
#### [Rule] Use macro constants defined in the standard header file.
[Description] The standard header files **stdint.h** (C) and **cstdint.h** (C++) contain macro constants with the maximum and minimum values. Reference these macro constants, rather than customizing new ones.
[Example]
```c
#include <cstdio>
#include <cinttypes>
int main()
{
std::printf("%zu\n", sizeof(std::int64_t));
std::printf("%s\n", PRId64);
std::printf("%+" PRId64 "\n", INT64_MIN);
std::printf("%+" PRId64 "\n", INT64_MAX);
std::int64_t n = 7;
std::printf("%+" PRId64 "\n", n);
}
```
#### [Rule] Use unified definitions instead of all Fs to indicate invalid values of constants.
[Description] The same numeric constant value may be understood differently on 32- and 64-bit OSs, since the 32-bit OS has the signed and unsigned types. Especially, if the most significant bit is 1, this bit is considered as a negative number on a 32-bit OS based on the signed type and a positive number on a 64-bit OS. Use the definitions in **typedef.h** to indicate invalid values.
```c
// Use the definitions in typedef.h.
#define INVALID_INT8((int8_t)(-1))
#define INVALID_UINT8((uint8_t)(-1))
#define INVALID_INT16((int16_t)(-1))
#define INVALID_UINT16((uint16_t)(-1))
#define INVALID_INT32((int32_t)(-1))
#define INVALID_UINT32((uint32_t)(-1))
#define INVALID_INT64((int64_t)(-1))
#define INVALID_UINT64((uint64_t)(-1))
```
[Example]
```c
// On a 32-bit OS, n is a negative number.
long n = 0xFFFFFFFF;
// On a 64-bit OS, n is a positive number. The actual value is 0x00000000FFFFFFFF.
long n = 0xFFFFFFFF;
```
#### [Rec] Use uint32_t to define temporary variables whose size does not exceed 32 bits.
[Description] You do not need to pay special attention to the type of a variable. Use the default uint32_t type to minimize forced type conversions caused by type inconsistency.
### Structure Alignment and Padding
#### [Rule] Do not hardcode a constant to specify the variable length and structure length. Use built-in types such as **sizeof** to obtain the variable length and structure length.
[Description] **sizeof** automatically calculates the variable length and structure length, which might be incorrect if hardcoded. In addition, the running performance will not be adversely affected since the variable length is calculated during build.
On a 64-bit OS, 8-byte alignment is used by default. Using **sizeof()** to obtain the structure length can avoid length calculation errors caused by structure alignment.
Pay attention to the length differences on 32- and 64-bit OSs to avoid length calculation errors. Calculate and apply for space based on the maximum length.
[Example] **sizeof** is not used to calculate the structure length, causing insufficient memory space.
```c
int32_t *p;
p = (int32_t *)malloc(4 * ELEMENTS_NUMBER);
// This code assumes that the pointer length is 4 bytes. However, in LP64, the pointer length is 8 bytes.
// The correct method is to use sizeof().
int32_t *p;
p = (int32_t *)malloc(sizeof(p) * ELEMENTS_NUMBER);
```
#### [Rec] In special cases, force the compiler to use a specific alignment mode on the 64-bit OS.
[Description] If necessary, you can use the specified alignment mode to ensure code compatibility. If the pseudo-instruction **#pragma pack (n)** is used, the compiler aligns data by n bytes. If the pseudo-instruction **#pragma pack ()** is used, the compiler cancels the custom byte alignment mode.
[Example]
```c
#pragma pack(push) # Save the current alignment mode.
#pragma pack(1) # Set the alignment mode to 1-byte alignment.
struct test
{
......
};
#pragma pack(pop) # Restore the previous alignment mode.
```
#### [Rule] Uniform the message structures related to multi-device communication. For compatibility purposes, 1-byte alignment is preferred. Do not use 8-byte alignment or 64-bit data types to avoid errors during communication with a 32-bit OS.
Note: Inter-board communication messages involve inter-board operations. The communication fails unless all software is upgraded synchronously. For the sake of compatibility, use 1-byte alignment for the existing protocols and structures and convert them into the network byte order. For new communication protocols, use 4-byte alignment for higher communication efficiency and processing performance.
#### [Rule] Avoid structure padding for external APIs and use at least 4-byte alignment.
[Description] If structure definitions are included in an API header file provided externally, do not pad the structures. It is recommended that natural alignment be used to avoid data holes. Use at least 4-byte alignment.
#### [Rec] Use member names to access structure members. Do not use the offset mode.
[Description] The offsets of data structure members are different in the 32- and 64-bit environments. Therefore, the size of each member cannot be used as the offset. A 32-bit OS does not have structures automatically padded. However, on a 64-bit OS, automatic padding may occur.
[Example]
```c
Struct A
{
uint32_t a;
uint32_t *p;
uint32_t b;
};
```
The offset of member b is equal to sizeof(a) + sizeof(p) on a 32-bit OS. This is not true on a 64-bit OS. The correct method is to locate the index based on the variable name.
```c
xxx.b = 123;
```
If the structure definition is special, for example, if the structure is only the message header and there are other fields, then you can redefine the structure so that it does not include padding fields.
[Example]
```c
typedef struct {
uint32_t self; /* Structure used for alignment. */
uint32_t brother; /* Structure used for alignment. */
uint8_t processedFlag; /* Flag indicating whether the current node has been processed. */
uint8_t reserve[3]; /* Reserved for 4-byte alignment. */
} TreeNodeInfo;
typedef struct {
TreeNodeInfo nodeInfo; /* Tree information data structure of each node */
void *userInfo; /* User information data structure of each node */
} TreeNode;
```
The **TreeNode** structure has two member structures. In the following code, the address of the first member is the initial address of the second member minus the value of sizeof (the first member). (**inUserInfo** points to the **userInfo** field in the structure.)
```c
inTreeNodeInfo = (TreeNodeInfo *)((void *)(((char *)inUserInfo) - sizeof(TreeNodeInfo)));
```
The structure adopts natural alignment. Note that the length of the substructure **TreeNodeInfo** is 12 bytes on both 32- and 64-bit OSs. On a 32-bit OS, there is no padding between member structures of the **TreeNode** structure, and the structure length is 16 bytes. On a 64-bit OS, **sizeof(TreeNodeInfo)** is 12 and **sizeof(TreeNode)** is 24, which means that there is a 4-byte padding field after the substructure **TreeNodeInfo**. Therefore, when the preceding method is used to obtain the address of the previous field on a 64-bit OS, the 4-byte padding field is not calculated, causing a member access error.
#### [Rec] When defining a structure, implement 8-byte natural alignment to save storage space and avoid padding on the premise that readability is ensured.
[Description] If a structure can be naturally aligned, no padding is required, which effectively reduces the invalid space of the structure. You are advised to define 64-bit data types such as size_t and pointer at both ends of the structure without affecting readability.
[Example]
```c
// The length of the following structure is 24 bytes in natural alignment mode.
struct Foo
{
int32_t a;
uint64_t l;
int32_t x;
}
// After proper adjustment, the size can be reduced to 16 bytes.
struct Foo
{
uint64_t l;
int32_t a;
int32_t x;
}
```
### Data Type Conversion and Calculation
#### [Rule] Avoid implicit type conversion between different data types. If necessary, use explicit type conversion to avoid result inconsistency between the 32- and 64-bit environments.
[Description] Exercise caution when performing operations between operands with different lengths and precisions. Pay attention to the following about implicit type conversion:
1. When the value of a 64-bit variable is assigned to a 32-bit variable, the values of the least significant 32 bits are directly assigned, and the values of the most significant 32 bits are ignored. Then the least significant 32 bits are treated as signed or unsigned based on the left value variable type.
2. When the value of a 32-bit variable is assigned to a 64-bit variable, sign extension is performed based on whether the 32-bit variable is signed or unsigned, and then the 64-bit variable is treated as signed or unsigned based on the variable type.
3. For operations of signed and unsigned numbers, if the result type is not specified, the result is treated as unsigned by default.
The preceding conversion process may cause unexpected results. Therefore, exercise caution to avoid implicit type conversion as much as possible.
[Example] Example of the correct conversion result
```c
int32_t t1 = -2;
int64_t t2 = 1;
int32_t t3 = t1 + t2;
printf("t3 = %d\n", t3);
// Printed: t3 = -1
```
t1 is a 32-bit number, and t2 is a 64-bit number. Before the calculation, t1 is extended to 64 bits. After the calculation, the result is a 64-bit int64_t type. The hexadecimal value stored in the memory is 0xffffffffffffffff. During the value assignment, the value is truncated into 0xffffffff. Then, the value is treated as signed, which is -1. Although the result is correct, data truncation occurs. This may not be the author's intention.
[Example] Conversion from signed to unsigned
```c
int64_t t1 = -1;
uint32_t t2 = t1;
printf("t2=%u", t2);
// Printed: t2=4294967295
```
t1 is a 64-bit int64_t type, which is 0xffffffffffffffff in binary mode. When this value is assigned to a 32-bit int type, the most significant 32 bits are ignored and the least significant 32 bits are displayed. The binary value is 0xffffffff, which is 4294967295 in unsigned mode.
[Example] Conversion from 32-bit to unsigned 64-bit
```c
int32_t t1 = -1;
uint64_t t2 = t1;
printf("t2 = %lu\n", t2);
// Printed: t2 = 18446744073709551615
```
t1 is a signed negative 32-bit number, which must be extended with signs. The most significant bits of the negative number are all fs, and the value after extension is 0xffffffffffffffff. t2 is an unsigned 64-bit number, the value of which is a large positive number.
#### [Rule] When a pointer is used as the base address and the offset is calculated by byte, the pointer must be forcibly converted to a single-byte pointer such as uintptr_t or uint8_t *.
[Description] If the pointer is converted to an integer of the uint32_t type, the pointer may be truncated. This will not occur if the pointer is converted to uintptr_t. The pointer can also be converted to a single-byte pointer such as uint8_t * and char *. In this case, the offset is considered as bytes. A one-byte offset will be carried out for the void * type. To clarify the type, you are advised to use the type that is more specific.
[Example]
```c
// Incorrect
void *pPkt = (void *)((uint32_t)MSG_GET_DATA_ADDR(msgAddr) + OFFSET);
// Correct
void *pPkt = (void *)((uintptr_t)MSG_GET_DATA_ADDR(msgAddr) + OFFSET);// C
void *pPkt = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(MSG_GET_DATA_ADDR(msgAddr)) + OFFSET); // C++
```
#### [Rule] Mutual assignment is forbidden between pointers and uint32_t, including function parameter passing.
[Description] If the variable to be defined is a length-variable pointer, use void *. If the variable to be defined is a pointer or an integer, use uintptr_t.
[Example] Conversion between pointers and integers
```c
int32_t i, *p, *q;
p = &i;
q = (int32_t *) (int32_t)&i;
// In ARM Cortex-A32, p = q; in A64-LP64, p != q
```
To avoid this type conflict, use uintptr_t to indicate the pointer type.
#### [Rule] Mutual assignment is forbidden between size_t and int32_t/uint32_t, including function parameter passing.
[Description] The variable-length type cannot be forcibly converted to the 32-bit type.
[Example]
```c
int32_t length = (int32_t)strlen(str); // Incorrect
```
strlen returns size_t (unsigned long in LP64). When the value is assigned to int32_t, truncation is inevitable. Generally, truncation occurs only when the length of **str** is greater than 2 GB. This is rare in programs.
#### [Rule] When a large array or large `for` loop index is used on a 64-bit OS, the index type must be consistent with the subscript boundary.
[Description] If a large array or large loop is used on a 64-bit OS, the index range may exceed 32 bits. In this case, the variable-length type or 64-bit type must be used to define the array subscript or loop variable to prevent full traversal failures caused by an incorrect variable range.
[Example]
```c
int64_t count = BIG_NUMBER;
for (unsigned int index = 0; index != count; index++)
...
```
On a 64-bit OS, int64_t is a 64-bit type, and count is a large number. unsigned int is a 32-bit type. As a result, the loop never ends. Therefore, the index type should be changed to int64_t.
### Bit Field and Byte Order
#### [Rule] When defining variables based on bit fields, fully consider the basic types and alignment of bit fields to avoid calculation errors in different bit widths.
[Description] Consider both the width of a bit field and alignment. The structure length is different in different environments. Use the name rather than direct calculation result for the value of a bit field.
#### [Rule] On a 64-bit OS, consider the length difference caused by the shift operation and the problem that the value generated after shift is implicitly extended to 64 bits.
[Description] During the shift operation, check whether overflow and rewind occur. Change the data type to 32-bit to avoid result inconsistency.
[Example]
```c
int64_t t = 1 << a; // Consider the size of a.
```
The maximum value of a is 32 on a 32-bit OS and 64 on a 64-bit OS. If a is a 32-bit variable, the extension to 64 bits must also be considered.
### Third-Party Libraries and Differentiated Features
#### [Rule] The third-party class libraries used by a 64-bit OS must support 64-bit.
[Description] Some library functions and third-party open-source code may not fully support 64-bit. You must evaluate all involved code to ensure that it can run in the 64-bit environment. Some library functions are implemented using different APIs in the 32- and 64-bit environments. Ensure that correct APIs are used.
[Example] In the 32-bit environment, **mmap** can be used. If the size of the memory to map exceeds 2 GB, **mmap64** is required. In the 64-bit environment, **mmap** is used.
#### [Rule] Functionalities involving bottom-layer assembly must be debugged separately on the 32- and 64-bit OSs.
[Description] The number and bit width of registers are different in the 32- and 64-bit environments. The debugging functionalities related to assembly must be adjusted. You must also pay attention to the code validity in the 32-bit and 64-bit environments.
[Example] For the push-to-stack functionality of the call stack, you must write and debug the assembly code in the 32- and 64-bit environment separately.
#### [Rule] The patch functionality must support both 32- and 64-bit instructions.
[Description] The patch mechanism and functionalities must be adopted to the command length changes.
#### [Rule] All tools used on a 64-bit OS must support 64-bit.
[Description] If type inconsistency is inevitable, convert the type with caution to ensure that the final result meets the application requirements.
# OpenHarmony Java Secure Coding Guide # Java Secure Coding Guide
This document provides secure coding suggestions for Java-based development. This document provides secure coding suggestions for Java-based development.
......
# HDF Driver Coding Guide
## About This Document
### Purpose
OpenHarmony aims to build an open, distributed OS framework for smart IoT devices in the full-scenario, full-connectivity, and full-intelligence era. It has the following technical features: hardware collaboration for resource sharing, one-time development for multi-device deployment, and a unified OS for flexible deployment.
The Hardware Driver Foundation (HDF) provides the following driver framework capabilities: driver loading, driver service management, and driver message mechanism. This unified driver architecture system is designed to provide a more precise and efficient development environment, where you can perform one-time driver development for multi-system deployment.
As such, certain coding specifications are required for the OpenHarmony driver implemented based on the HDF. This document stipulates the specifications on the driver code, helping you improve code standardization and portability.
## Coding Guide
### General Principles
#### [Rule] Use the capabilities provided by the HDF to implement drivers.
[Description] The HDF supports driver loading, driver service management, and driver message mechanism. It provides the Operating System Abstraction Layer (OSAL) and Platform Abstraction Layer (PAL) to support cross-system and cross-platform driver deployment. It also provides capabilities such as driver model abstraction, common tools, and peripheral component framework. You should develop drivers based on these capabilities to ensure that the drivers can be deployed on various devices powered by OpenHarmony.
#### [Rule] Follow this coding guide to develop drivers that can run in both the kernel space and user space.
[Description] Kernel-mode drivers are different from user-mode drivers in essence. They apply to different use cases. You must follow this guide during service design and development and use the HDF OSAL and PAL to shield the differences, so as to ensure that the drivers can run in both the kernel space and user space.
#### [Rec] Include the drivers/framework/include directory instead of a subdirectory in the build script.
[Description] The **drivers/framework/include** directory is the root directory of the header file exposed by the HDF externally. This directory contains multiple subdirectories to represent different modules such as the core framework, OSAL, and PAL. When using a header file, you are advised to include the **drivers/framework/include** directory in the build script. This avoids repeated inclusion when the script is referenced in the code.
[Example]
```gn
config("xxxx_private_config") {
include_dirs = [
"//drivers/framework/include",
"//drivers/framework/include/core", # Not recommended.
]
}
```
```c
#include <core/hdf_device_desc.h>
#include <hdf_device_desc.h> // Not recommended.
```
### HDF Core Framework
#### [Rule] Implement the Bind, Init, and Release methods based on the responsibility definitions in the HdfDriverEntry object.
[Description] The **HdfDriverEntry** object is the entry of an HDF driver. The **Bind**, **Init**, and **Release** methods have their own responsibilities. You must implement the corresponding functions based on the responsibilities.
```c
struct HdfDriverEntry g_sampleDriverEntry = {
.moduleVersion = 1,
.moduleName = "sample_driver",
.Bind = SampleDriverBind, // Responsibility: Bind the service interface provided by the driver to the HDF.
.Init = SampleDriverInit, // Responsibility: Initialize the driver service.
.Release = SampleDriverRelease, // Responsibility: Release driver resources. It is invoked when an exception occurs.
};
HDF_INIT(g_sampleDriverEntry);
```
#### [Rule] The first member in the driver service structure must be of the IDeviceIoService type.
[Description] The first member of the service interface defined by the driver must be of the **IDeviceIoService** type.
[Example]
```c
struct ISampleDriverService {
struct IDeviceIoService ioService; // The first member must be of the IDeviceIoService type.
int32_t (*FunctionA)(void); // The first service interface of the driver.
int32_t (*FunctionB)(uint32_t inputCode); // The second service interface of the driver. More service interfaces can be added here.
};
```
[Example]
```c
struct ISampleDriverService {
struct IDeviceIoService ioService; // The first member must be of the IDeviceIoService type.
void *instance; // A service instance can be encapsulated here to provide service interfaces.
};
```
#### [Rule] All driver service interfaces must be bound using the Bind method of the HdfDriverEntry object. All service interfaces must be defined. They cannot be defined as null.
[Description] The service interfaces defined by the driver are exposed externally. If a service interface is not defined or is defined as null, exceptions may occur during external invocation.
[Example]
```c
int32_t SampleDriverBind(struct HdfDeviceObject *deviceObject)
{
static struct ISampleDriverService sampleDriver = {
.FunctionA = SampleDriverServiceA,
.FunctionB = NULL, // The service interface cannot be defined as null.
};
// Bind ioService to the device object created by the HDF.
deviceObject->service = &sampleDriver.ioService;
return HDF_SUCCESS;
}
```
#### [Rec] Call the HdfDeviceSetClass interface in the Init method of the HdfDriverEntry object to define the driver type.
[Description] Based on the driver type, you can classify the drivers of the current device and query the driver capabilities of the current device. For better driver management, you are advised to call **HdfDeviceSetClass** to set the driver type.
[Example]
```c
int32_t SampleDriverInit(struct HdfDeviceObject *deviceObject)
{
// Set the driver type to DISPLAY.
if (!HdfDeviceSetClass(deviceObject, DEVICE_CLASS_DISPLAY)) {
HDF_LOGE("HdfDeviceSetClass failed");
return HDF_FAILURE;
}
return HDF_SUCCESS;
}
```
### HCS
HDF Configuration Source (HCS) describes the configuration source code of the HDF in the form of key-value pairs. It decouples configuration code from driver code, making it easy for you to manage the driver configuration.
The driver configuration consists of the driver device description defined by the HDF and the private configuration of a driver.
**Driver Device Description**
The driver loading information required by the HDF comes from the driver device description. Therefore, you must add the driver device description to the **device_info.hcs** file defined by the HDF.
#### [Rule] Before configuring a driver, determine the hardware to which the driver belongs and the deployment mode, and plan the directories and files to be configured.
[Description] In the **vendor** directory of the OpenHarmony source code, plan the directories based on the chip vendor, development board, and configuration. The HDF driver configuration is stored in the **hdf\_config** directory. According to the hardware specifications, the **hdf\_config** directory stores kernel-mode configuration information or both kernel- and user-mode configuration information. You should determine the directory where the driver is to be configured based on the driver hardware and deployment mode.
[Example]
```bash
$openharmony_src_root/vendor/hisilicon/hispark_taurus/hdf_config # Directory for storing the kernel-mode configuration file. There are no user-mode configuration files.
$openharmony_src_root/vendor/hisilicon/Hi3516DV300/hdf_config/khdf # Directory for storing the kernel-mode configuration file.
$openharmony_src_root/vendor/hisilicon/Hi3516DV300/hdf_config/uhdf # Directory for storing the user-mode configuration file.
$openharmony_src_root/vendor/hisilicon/Hi3516DV300/hdf_config/khdf/device_info/device_info.hcs # Device description file of the kernel-mode driver.
$openharmony_src_root/vendor/hisilicon/Hi3516DV300/hdf_config/khdf/lcd/lcd_config.hcs # Private configuration file of the kernel-mode driver.
```
#### [Rule] Use existing configuration information and inherit existing configuration templates during driver configuration.
[Description] The **host**, **device**, and **deviceNode** templates have been configured in the **device_info.hcs** file. When configuring a driver, make full use of the existing configuration information and inherit HCS features to minimize your configuration workload.
[Example]
```
root {
device_info {
match_attr = "hdf_manager";
template host { // host template
hostName = "";
priority = 100; // Host startup priority. The value ranges from 0 to 200. A larger value indicates a lower priority. The default value 100 is recommended. If the priorities are the same, the host loading sequence cannot be ensured.
template device { // device template
template deviceNode { // deviceNode template
policy = 0; // Policy for publishing drive services.
priority = 100; // Driver startup priority. The value ranges from 0 to 200. A larger value indicates a lower priority. The default value 100 is recommended. If the priorities are the same, the device loading sequence cannot be ensured.
preload = 0; // The driver is loaded as required.
permission = 0664; // Permission for the driver to create a device node.
moduleName = "";
serviceName = "";
deviceMatchAttr = "";
}
}
}
// To use the default values in the template, the node fields can be not included.
sample_host :: host { // sample_host inherits the host template.
hostName = "host0"; // Host name. The host node is a container used to store a type of drivers.
device_sample :: device { // device_sample inherits the device template.
device0 :: deviceNode { // device0 inherits the deviceNode template.
policy = 1; // Overwrite the policy in the template.
moduleName = "sample_driver"; // Driver name. The value of this field must be the same as that of moduleName in the HdfDriverEntry structure.
serviceName = "sample_service"; // Service name of the driver, which must be unique.
deviceMatchAttr = "sample_config"; // Keyword for matching the private data of the driver. The value must be the same as that of match_attr in the private data configuration table of the driver.
}
}
}
}
}
```
#### [Rule] Use the defined types during driver model design and classification. Do not configure hosts and devices repeatedly.
[Description] The HDF places the same type of devices in the same host. You can develop and deploy the driver functionalities of the host by layer, so that one driver has multiple nodes. The following figure shows the HDF driver model.
![HDFDriverModel.png]( ../device-dev/driver/figures/HDFDriverModel.png)
Place devices of the same type in the same host. When adding a device, check whether the host of the same type already exists. If such a host already exists, configure the device in the host. Do not configure the same host again. A device belongs to only one driver. Therefore, do not configure the same device in different hosts.
#### [Rule] Set publication policies for driver services based on service rules.
[Description] The driver service is the object of capabilities provided by the driver to external systems and is managed by the HDF in a unified manner. The HDF uses the **policy** field in the configuration file to define policies for drivers to publish services externally. The values and meanings of this field are as follows:
```c
typedef enum {
/* The driver does not provide services.*/
SERVICE_POLICY_NONE = 0,
/* The driver provides services for kernel-space applications. */
SERVICE_POLICY_PUBLIC = 1,
/* The driver provides services for both kernel- and user-space applications. */
SERVICE_POLICY_CAPACITY = 2,
/** Driver services are not published externally but can be subscribed to. */
SERVICE_POLICY_FRIENDLY = 3,
/* Driver services are not published externally and cannot be subscribed to. */
SERVICE_POLICY_PRIVATE = 4,
/** Invalid policy. */
SERVICE_POLICY_INVALID
} ServicePolicy;
```
You must set the policies based on service rules. Do not set unnecessary policies, for example, setting user-mode publication policies for kernel-mode drivers.
[Example]
```
root {
device_info {
sample_host {
sample_device {
device0 {
policy = 1; // The driver provides services for kernel-space applications.
...
}
}
}
}
}
```
#### [Rule] The permission to create device nodes for a driver must match the publication policy of the driver.
[Description] In the **device_info.hcs** file, the **permission** field specifies the permission used by the driver to create a device node. This field is a 4-digit octal number and uses the Unix file permissions, for example, 0644. This field takes effect only when the driver provides services for user-space applications (policy = 2).
You must ensure that the publication policy of the driver service matches the device node permission. Otherwise, access to the driver service may fail or the permission of the device node may be inappropriate.
[Example]
```
root {
device_info {
sample_host {
sample_device {
device0 {
policy = 2; // The driver provides services for both kernel- and user-space applications.
permission = 0640; // Recommended value
...
}
}
}
}
}
```
[Counterexample]
```
root {
device_info {
sample_host {
sample_device {
device0 {
policy = 2; // The driver provides services for both kernel- and user-space applications.
permission = 0777; // Excessive permission.
...
}
}
}
}
}
```
[Counterexample]
```
root {
device_info {
sample_host {
sample_device {
device0 {
policy = 1; // The driver provides services for kernel-space applications but does not create a device node.
permission = 0640; // Redundancy configuration
...
}
}
}
}
}
```
#### [Rule] Configure whether to load a driver as required based on service requirements.
[Description] In the **device_info.hcs**, **preload** specifies the driver loading mode. The values and meanings of this field are as follows:
```c
typedef enum {
/* The driver is loaded by default during the system boot process. */
DEVICE_PRELOAD_ENABLE = 0,
/* The driver is loaded after a quick start is complete if the system supports quick start. If the system does not support quick start, this value has the same meaning as DEVICE\_PRELOAD\_ENABLE. */
DEVICE_PRELOAD_ENABLE_STEP2,
/* The driver is not loaded during the system boot process. When a user-mode process requests the driver service, the HDF attempts to dynamically load the driver if the driver service does not exist. */
DEVICE_PRELOAD_DISABLE,
/** Invalid value. */
DEVICE_PRELOAD_INVALID
} DevicePreload;
```
Set the **preload** field based on the service requirements.
[Example]
```
root {
device_info {
sample_host {
sample_device {
device0 {
preload = 2; // The driver is loaded as required.
...
}
}
}
}
}
```
#### [Rec] When the preload field is set to 0, configure the loading priority based on the service requirements.
[Description] In the **device_info.hcs** file, the **priority** field indicates the host and driver loading priority. The value of this field is an integer ranging from 0 to 200. For drivers in different hosts, a smaller priority value of the host indicates a higher driver loading priority. For drivers in the same host, a smaller priority value of the driver indicates a higher driver loading priority. The default value of the **priority** field is 100. If this field is not set or set to the same value for different drivers, the driver loading sequence cannot be ensured. You should configure the **priority** field based on the service requirements to ensure the driver loading sequence.
[Example]
```
root {
device_info {
sample_host0 {
priority = 100;
sample_device {
device0 {
preload = 0; // The driver is loaded by default.
priority = 100; // The HDF ensures that the driver is loaded before device1.
...
}
device1 {
preload = 0; // The driver is loaded by default.
priority = 200; // The HDF ensures that the driver is loaded after device0.
...
}
}
}
sample_host1 {
priority = 100; // The HDF does not ensure the loading sequence because this host has the same priority as sample_host0.
...
}
}
}
```
**Private Configuration Information of the Driver**
If a driver has private configurations, you can add a driver configuration file to fill in default configurations of the driver. When loading the driver, the HDF obtains the configuration information, saves it in the **property** field of **HdfDeviceObject**, and passes it to the driver through **Bind** and **Init**.
#### [Rule] Store the private configuration files of the drivers in different directories according to the component type or module.
[Description] You must properly plan the directory for storing private configuration files of drivers. Do not store them in the root directory.
[Example]
```bash
$openharmony_src_root/vendor/hisilicon/Hi3516DV300/hdf_config/khdf/sample/sample_config.hcs # Correct. The private configuration file is stored in the sample directory.
$openharmony_src_root/vendor/hisilicon/Hi3516DV300/hdf_config/khdf/sample_config.hcs # Incorrect. The private configuration file is placed in the root directory.
```
#### [Rule] Add the private configuration file of a driver to the hdf.hcs file in the hdf_config directory.
[Description] The **hdf.hcs** file summarizes the configuration information. The HDF parses the file content and loads the private configuration information of the driver to the device node during the build and runtime. Include the private configuration file of the driver in the **hdf.hcs** file to trigger driver initialization.
[Example]
```c
#include "device_info/device_info.hcs"
#include "sample/sample_config.hcs" // The file contains the private configuration file of a driver.
root {
module = "hisilicon,hi35xx_chip";
}
```
#### [Rule] The value of the matchAttr field in the private configuration file of the driver must be the same as that of the deviceMatchAttr field in device_info.hcs.
[Description] The HDF associates with the driver device through the **match_attr** field. A mismatch causes a failure to obtain the private configuration information.
[Example]
```
root {
sample_config {
...
match_attr = "sample_config"; // The value of this field must be the same as that of deviceMatchAttr in device_info.hcs.
}
}
```
#### [Rule] Use underscores (_) in field names in the private configuration file.
[Description] According to the naming rules in the C/C++ coding guide, use underscores (_) in field names in the private configuration file of a driver. In this way, the naming rule is satisfied during the definition of the private configuration data structure in the implementation code. It also makes unified management of the code and configuration files easier.
[Example]
```
root {
sample_config {
sample_version = 1; // Use an underscore (_) in the field name.
sample_bus = "I2C_0";
match_attr = "sample_config";
}
}
```
### HCS Macros
The private configuration information of a driver is loaded to **property** of **HdfDeviceObject**. This occupies memory space, which should be avoided for mini- and small-system devices. To minimize the memory usage, the HDF provides HCS macros to parse the private configuration information.
#### [Rule] Use HCS macros to parse the private configuration information in memory-sensitive or cross-system scenario.
[Description] You should specify the use cases of drivers. In memory-sensitive scenarios or if a driver needs to be used in mini-, small-, and standard-system devices, use HCS macros to parse the private configuration information for higher performance and portability.
[Example]
```c
#include <utils/hcs_macro.h>
#define SAMPLE_CONFIG_NODE HCS_NODE(HCS_ROOT, sample_config)
ASSERT_EQ(HCS_PROP(SAMPLE_CONFIG_NODE, sampleVersion), 1);
ASSERT_EQ(HCS_PROP(SAMPLE_CONFIG_NODE, sample_bus), "I2C_0");
ASSERT_EQ(HCS_PROP(SAMPLE_CONFIG_NODE, match_attr), "sample_config");
```
### HDF Tools
#### [Rule] Determine the communication scenario and HdfSbuf type.
[Description] **HdfSbuf** is a data structure used for data transfer. This structure is classified into different types based on the communication scenario. These types are defined in the **hdf_sbuf.h** header file.
```c
enum HdfSbufType {
SBUF_RAW = 0, /* SBUF used for communication between the user space and the kernel space. */
SBUF_IPC, /* SBUF used for inter-process communication (IPC). */
SBUF_IPC_HW, /* Reserved for extension. */
SBUF_TYPE_MAX, /* Maximum value of the SBUF type. */
};
```
Determine whether the data transfer is IPC in the user space or between the user space and kernel space, and then create the corresponding **HdfSbuf** structure.
[Example]
```c
void SampleDispatchBetweenUserAndKernel()
{
int32_t ret;
/* Communication between the user space and kernel space. */
struct HdfSBuf *data = HdfSBufTypedObtain(SBUF_RAW);
...
ret = sample->dispatcher->Dispatch(&sample->object, CMD_SAMPLE_DISPATCH, data, NULL);
...
HdfSBufRecycle(data);
}
```
[Example]
```c++
void SampleDispatchIpc()
{
/* IPC */
struct HdfSBuf *data = HdfSBufTypedObtain(SBUF_IPC);
...
int ret = sample->dispatcher->Dispatch(sample, CMD_SAMPLE_DISPATCH, data, nullptr);
...
HdfSBufRecycle(data);
}
```
#### [Rule] Define the HDF_LOG_TAG macro when using HDF logging.
[Description] The HDF provides the **hdf_log.h** tool ,using which you can output driver run logs. The **HDF_LOG_TAG** macro specifies the log tag. You must define this macro before printing logs.
[Example]
```c
#include <hdf_log.h>
#define HDF_LOG_TAG sample_driver // Define the log tag.
int32_t SampleDriverInit(struct HdfDeviceObject *deviceObject)
{
HDF_LOGI("sample driver is initialized"); // Use the tool to print logs.
return HDF_SUCCESS;
}
```
#### [Rule] Verify the return values of the HDF methods and use the error codes provided by the HDF.
[Description] The HDF methods have specific return values. You should verify them rather than ignoring them. The return values correspond to error codes in the **hdf_base.h** header file. Use the error codes provided by the HDF when using the HDF or implementing custom methods.
[Example]
```c
int32_t SampleDriverInit(struct HdfDeviceObject *deviceObject)
{
int32_t ret;
// Check whether the device type is successfully set.
if (!HdfDeviceSetClass(deviceObject, DEVICE_CLASS_DISPLAY)) {
HDF_LOGE("HdfDeviceSetClass failed");
return HDF_FAILURE;
}
ret = InitDiver();
// A custom method uses an error code provided by the HDF.
if (ret != HDF_SUCCESS) {
HDF_LOGE("init drvier is failed");
return ret;
}
return HDF_SUCCESS;
}
```
### OSAL
The HDF OSAL shields the interface differences between OpenHarmony subsystems and provides unified OS interfaces, including memory management, threads, mutexes, spin locks, semaphores, timers, files, IRQ, time, atoms, firmware, and I/O operation modules.
#### [Rule] Use OS interfaces through the OSAL for drivers used across mini-, small-, and standard-system devices.
[Description] The OSAL shields the differences between OS interfaces. You should operate these OS interfaces based on the OSAL to ensure that drivers can run on different types of systems.
[Example]
```c
#include <osal/osal_mem.h>
#include <util/hdf_log.h>
struct DevHandle *SampleInit(void)
{
struct DevHandle *handle = (struct DevHandle *)OsalMemCalloc(sizeof(struct DevHandle));
if (handle == NULL) {
HDF_LOGE("OsalMemCalloc handle failed");
return NULL;
}
return handle;
}
```
[Example]
```c
#include <osal/osal_time.h>
void SampleSleep(uint32_t timeMs)
{
OsalMSleep(timeMs);
}
```
### PAL
The HDF PAL abstracts platform drivers and provides unified operation interfaces for modules such as the GPIO, I2C, SPI, UART, RTC, SDIO, eMMC, DSI, PWM, and watchdog.
#### [Rule] Use platform drivers across mini, small, and standard systems through the PAL.
[Description] The PAL masks the differences between platform driver interfaces of different system types. You should operate these interfaces based on PAL to ensure that drivers can run on different types of systems.
[Example]
```c
#include <platform/gpio_if.h>
#include <util/hdf_log.h>
#include <osal/osal_irq.h>
#include <osal/osal_time.h>
static uint32_t g_irqCnt;
/* Sample function of the GPIO IRQ service */
static int32_t SampleGpioIrqHandler(uint16_t gpio, void *data)
{
HDF_LOGE("%s: irq triggered, on gpio:%u, data=%p", __func__, gpio, data);
g_irqCnt++; /* If the IRQ function is triggered, the number of global counters is incremented by 1. */
return GpioDisableIrq(gpio);
}
/* GPIO sample function */
static int32_t SampleGpioIrqEdge(void)
{
int32_t ret;
uint16_t valRead;
uint16_t mode;
uint16_t gpio = 83; // Number of the GPIO pin to test
uint32_t timeout;
/* Set the output direction for the pin. */
ret = GpioSetDir(gpio, GPIO_DIR_OUT);
...
/* Disable the IRP of this pin. */
ret = GpioDisableIrq(gpio);
...
/* Set the IRR function for the pin. The trigger mode is both rising edge and falling edge. */
mode = OSAL_IRQF_TRIGGER_RISING | OSAL_IRQF_TRIGGER_FALLING;
ret = GpioSetIrq(gpio, mode, SampleGpioIrqHandler, NULL);
...
/* Enable the IRQ for this pin. */
ret = GpioEnableIrq(gpio);
...
g_irqCnt = 0; /* Reset the global counter. */
timeout = 0; /* Reset the waiting time. */
/* Wait for the IRQ function of this pin to trigger. The timeout duration is 1000 ms. */
while (g_irqCnt <= 0 && timeout < 1000) {
ret = GpioRead(gpio, &valRead);
...
ret = GpioWrite(gpio, (valRead == GPIO_VAL_LOW) ? GPIO_VAL_HIGH : GPIO_VAL_LOW);
...
OsalMDelay(200); // Wait for an interrupt to be triggered.
timeout += 200;
}
ret = GpioUnSetIrq(gpio);
...
return (g_irqCnt > 0) ? HDF_SUCCESS : HDF_FAILURE;
}
```
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
- [OpenHarmony Security Design Specifications](OpenHarmony-security-design-guide.md) - [OpenHarmony Security Design Specifications](OpenHarmony-security-design-guide.md)
- [OpenHarmony Security Design Specifications](OpenHarmony-security-design-guide.md)
### Code Style ### Code Style
Develop, review, and test code following the OpenHarmony coding standards. Make sure code is written in the same style. Develop, review, and test code following the OpenHarmony coding standards. Make sure code is written in the same style.
...@@ -17,6 +19,8 @@ Develop, review, and test code following the OpenHarmony coding standards. Make ...@@ -17,6 +19,8 @@ Develop, review, and test code following the OpenHarmony coding standards. Make
- [JavaScript Coding Style Guide](OpenHarmony-JavaScript-coding-style-guide.md) - [JavaScript Coding Style Guide](OpenHarmony-JavaScript-coding-style-guide.md)
- [Python Coding Style Guide](https://pep8.org/) - [Python Coding Style Guide](https://pep8.org/)
- [C&C++ Secure Coding Guide](OpenHarmony-c-cpp-secure-coding-guide.md) - [C&C++ Secure Coding Guide](OpenHarmony-c-cpp-secure-coding-guide.md)
- [HDF Driver Coding Guide](OpenHarmony-Java-secure-coding-guide.md)
- [32- and 64-Bit Portability Coding Guide](OpenHarmony-64bits-coding-guide.md)
- [Java Secure Coding Guide](OpenHarmony-Java-secure-coding-guide.md) - [Java Secure Coding Guide](OpenHarmony-Java-secure-coding-guide.md)
- [Logging Guide](OpenHarmony-Log-guide.md) - [Logging Guide](OpenHarmony-Log-guide.md)
...@@ -28,6 +32,6 @@ For details, see [Contribution Process](contribution-process.md). ...@@ -28,6 +32,6 @@ For details, see [Contribution Process](contribution-process.md).
- [Security Issue Handling and Release Processes](https://gitee.com/openharmony/security/blob/master/en/security-process/README.md) - [Security Issue Handling and Release Processes](https://gitee.com/openharmony/security/blob/master/en/security-process/README.md)
- [OpenHarmony Security and Disclosure Statement](https://gitee.com/openharmony/security/blob/master/en/security-process/security-disclosure.md) - [OpenHarmony Security Vulnerability Notice](https://gitee.com/openharmony/security/blob/master/en/security-process/security-disclosure.md)
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
OpenHarmony的目标是面向全场景、全连接、全智能时代,基于开源的方式,搭建一个智能终端设备操作系统的框架和平台,促进万物互联产业的繁荣发展。具有“硬件互助,资源共享”、“一次开发,多端部署”、“统一OS,弹性部署”的技术特性。 OpenHarmony的目标是面向全场景、全连接、全智能时代,基于开源的方式,搭建一个智能终端设备操作系统的框架和平台,促进万物互联产业的繁荣发展。具有“硬件互助,资源共享”、“一次开发,多端部署”、“统一OS,弹性部署”的技术特性。
OpenHarmony支持三种系统类型: OpenHarmony支持三种系统类型:
1. 轻量系统(mini system),面向MCU类处理器(例如Arm Cortex-M、RISC-V 32位)的轻量设备,硬件资源极其有限,支持的设备最小内存为128KiB; 1. 轻量系统(mini system),面向MCU类处理器(例如Arm Cortex-M、RISC-V 32位)的轻量设备,硬件资源极其有限,支持的设备最小内存为128KiB;
2. 小型系统(small system),面向应用处理器(例如Arm Cortex-A 64位)的设备,支持的设备最小内存为1MiB 2. 小型系统(small system),面向应用处理器(例如Arm Cortex-A 64位)的设备,支持的设备最小内存为1MiB
3. 标准系统(standard system),面向应用处理器(例如Arm Cortex-A 64位)的设备,支持的设备最小内存为128MiB 3. 标准系统(standard system),面向应用处理器(例如Arm Cortex-A 64位)的设备,支持的设备最小内存为128MiB
...@@ -35,7 +35,7 @@ OpenHarmony支持三种系统类型: ...@@ -35,7 +35,7 @@ OpenHarmony支持三种系统类型:
| size_t | **4** | **8** | **8** | 4 | 8 | | size_t | **4** | **8** | **8** | 4 | 8 |
| pointer | **4** | **8** | **8** | 4 | 8 | | pointer | **4** | **8** | **8** | 4 | 8 |
上表中只包含了部分基本类型,下表分别将ILP32和LP64的siziof和print`进行对比,展示了更全面的常量和类型对应的差异: 上表中只包含了部分基本类型,下表分别将ILP32和LP64的sizeof和print`进行对比,展示了更全面的常量和类型对应的差异:
| Type | ILP32 sizeof | ILP32 print | LP64 sizeof | LP64 print | 备注 | | Type | ILP32 sizeof | ILP32 print | LP64 sizeof | LP64 print | 备注 |
| ------------------ | ------------ | ----------- | ----------- | ---------- | ------ | | ------------------ | ------------ | ----------- | ----------- | ---------- | ------ |
...@@ -115,8 +115,8 @@ typedef struct tagFoo { ...@@ -115,8 +115,8 @@ typedef struct tagFoo {
| uint16_t | 2 | 2 | %u | 代替unsigned short | | uint16_t | 2 | 2 | %u | 代替unsigned short |
| int32_t | 4 | 4 | %d | 代替int | | int32_t | 4 | 4 | %d | 代替int |
| uint32_t | 4 | 4 | %u | 代替unsigned int | | uint32_t | 4 | 4 | %u | 代替unsigned int |
| int64_t | 8 | 8 | %PRId64 | 代替long long宏实现代码兼容 | | int64_t | 8 | 8 | %PRId64 | 代替long long宏实现代码兼容 |
| uint64_t | 8 | 8 | %PRIu64 | 代替unsigned longlong,宏实现代码兼容 | | uint64_t | 8 | 8 | %PRIu64 | 代替unsigned long long、宏实现代码兼容 |
| float | 4 | 4 | %f | 单精度浮点数 | | float | 4 | 4 | %f | 单精度浮点数 |
| double | 8 | 8 | %lf | 双精度浮点数 | | double | 8 | 8 | %lf | 双精度浮点数 |
| bool | 1 | 1 | %d | 布尔类型 | | bool | 1 | 1 | %d | 布尔类型 |
...@@ -170,7 +170,7 @@ long var; ...@@ -170,7 +170,7 @@ long var;
#### 【规则】当需要采用整型变量来存储指针时,变量应该定义成uintptr_t以适应不同的位宽 #### 【规则】当需要采用整型变量来存储指针时,变量应该定义成uintptr_t以适应不同的位宽
【说明】uintptr_t类型用于用于存储指针长度级别的数据,其长度在32位和64位可自动适应。 【说明】uintptr_t类型用于用于存储指针长度的数据,其长度在32位和64位可自动适应。
【示例】 【示例】
...@@ -262,7 +262,7 @@ format ‘%p’ expects argument of type ‘void *’, but argument 2 has type ...@@ -262,7 +262,7 @@ format ‘%p’ expects argument of type ‘void *’, but argument 2 has type
| 0x80000000LL | 8 | 8 | 增加LL后缀,小于uint32_t范围,长度固定 | | 0x80000000LL | 8 | 8 | 增加LL后缀,小于uint32_t范围,长度固定 |
| 0x8000000000 | **NA或8** | **8** | 无后缀,超过uint32_t的范围,编译器默认为LL或无效,64位下固定为uint64_t类型 | | 0x8000000000 | **NA或8** | **8** | 无后缀,超过uint32_t的范围,编译器默认为LL或无效,64位下固定为uint64_t类型 |
| 0x8000000000L | **NA或8** | **8** | 后缀为L,对超过uint32_t的范围常数,增加该参数没有意义,应当避免使用 | | 0x8000000000L | **NA或8** | **8** | 后缀为L,对超过uint32_t的范围常数,增加该参数没有意义,应当避免使用 |
| 0x8000000000LL | 8 | 8 | 默认为LL,uint64_t类型 | | 0x8000000000LL | 8 | 8 | 后缀为LL,uint64_t类型 |
从上表中可看出,使用L或UL后缀的常量,其长度在32位和64位下发生变化,不利于代码的可移植性,因此禁止使用这个后缀。 从上表中可看出,使用L或UL后缀的常量,其长度在32位和64位下发生变化,不利于代码的可移植性,因此禁止使用这个后缀。
...@@ -533,7 +533,7 @@ q = (int32_t *) (int32_t)&i; ...@@ -533,7 +533,7 @@ q = (int32_t *) (int32_t)&i;
int32_t length = (int32_t)strlen(str); // 错误 int32_t length = (int32_t)strlen(str); // 错误
``` ```
strlen返回size_t(它在LP64中是unsigned long),当赋值给一个int32_t时,截断是必然发生的。而通常,截断只会在str的长度大于2GB时才会发生,这种情况在程序中一般不会出现,容易出现问题 strlen返回size_t(它在LP64中是unsigned long),当赋值给一个int32_t时,截断是必然发生的。而通常,截断只会在str的长度大于2GB时才会发生,这种情况在程序中一般不会出现,容易忽略
#### 【规则】在64位环境下使用大数组或大for循环索引时,索引类型应当与下标边界保持一致 #### 【规则】在64位环境下使用大数组或大for循环索引时,索引类型应当与下标边界保持一致
......
...@@ -16,7 +16,7 @@ HDF(Hardware Driver Foundation)驱动框架,为开发者提供驱动框架 ...@@ -16,7 +16,7 @@ HDF(Hardware Driver Foundation)驱动框架,为开发者提供驱动框架
#### 【规则】OpenHarmony的驱动程序,应当使用HDF框架提供的能力实现 #### 【规则】OpenHarmony的驱动程序,应当使用HDF框架提供的能力实现
【说明】HDF驱动框架提供了驱动加载、驱动服务管理和驱动消息机制,同时还提供了操作系统抽象层(OSAL, Operating System Abstract Layer)和平台抽象层(PAL, Platform Abstract Layer)来保证驱动程序的跨系统跨平台部署的特性。除此之外,HDF提供了驱动模型的抽象、公共工具、外围器件框架等能力。开发者应该基于HDF提供的这些能力开发驱动,从而保证驱动程序可以在各种形态的OpenHarmony上进行部署。 【说明】HDF驱动框架提供了驱动加载、驱动服务管理和驱动消息机制,同时还提供了操作系统抽象层(OSAL, Operating System Abstraction Layer)和平台抽象层(PAL, Platform Abstraction Layer)来保证驱动程序的跨系统跨平台部署的特性。除此之外,HDF提供了驱动模型的抽象、公共工具、外围器件框架等能力。开发者应该基于HDF提供的这些能力开发驱动,从而保证驱动程序可以在各种形态的OpenHarmony上进行部署。
#### 【规则】开发者应当遵循此规范要求,开发能够同时满足内核态和用户态的驱动 #### 【规则】开发者应当遵循此规范要求,开发能够同时满足内核态和用户态的驱动
...@@ -332,7 +332,7 @@ root { ...@@ -332,7 +332,7 @@ root {
#### 【建议】当preload字段配置为默认加载时,应当根据业务要求配置按序加载的优先级 #### 【建议】当preload字段配置为默认加载时,应当根据业务要求配置按序加载的优先级
【说明】在HDF框架定义的device_info.hcs配置文件中,priority字段(取值范围为整数0到200)是用来表示Host和驱动的优先级。不同的Host内的驱动,Host的priority值越小,驱动加载优先级越高;同一个Host内驱动的priority值越小,加载优先级越高。priority字段的默认值为100,当未配置或字段值相同时,HDF框架将不保证驱动的加载顺序。开发者应当根据业务场景的要求,配置preority字段,保证各个驱动的启动顺序。 【说明】在HDF框架定义的device_info.hcs配置文件中,priority字段(取值范围为整数0到200)是用来表示Host和驱动的优先级。不同的Host内的驱动,Host的priority值越小,驱动加载优先级越高;同一个Host内驱动的priority值越小,加载优先级越高。priority字段的默认值为100,当未配置或字段值相同时,HDF框架将不保证驱动的加载顺序。开发者应当根据业务场景的要求,配置priority字段,保证各个驱动的启动顺序。
【样例】 【样例】
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册