1# SPI<a name="EN-US_TOPIC_0000001199690327"></a> 2 3 4## Overview<a name="section84922229152909"></a> 5 6In the Hardware Driver Foundation \(HDF\), the Serial Peripheral Interface \(SPI\) uses the independent service mode for API adaptation. In this mode, each device independently publishes a device service to handle external access requests. After receiving an access request from an API, the device manager extracts the parameters in the request to call the internal method of the target device. In the independent service mode, the service management capabilities of the HDFDeviceManager can be directly used. However, you need to configure a device node for each device, which increases the memory usage. 7 8**Figure 1** Independent service mode<a name="fig666465313303"></a> 9 10 11## Available APIs<a name="section752964871810"></a> 12 13SpiCntlrMethod: 14 15``` 16struct SpiCntlrMethod { 17 int32_t (*GetCfg)(struct SpiCntlr *cntlr, struct SpiCfg *cfg); 18 int32_t (*SetCfg)(struct SpiCntlr *cntlr, struct SpiCfg *cfg); 19 int32_t (*Transfer)(struct SpiCntlr *cntlr, struct SpiMsg *msg, uint32_t count); 20 int32_t (*Open)(struct SpiCntlr *cntlr); 21 int32_t (*Close)(struct SpiCntlr *cntlr); 22}; 23``` 24**Table 1** Callbacks for the members in the SpiCntlrMethod structure 25 26<a name="table7167123615321"></a> 27<table><thead align="left"><tr id="row816783615326"><th class="cellrowborder" valign="top" width="25%" id="mcps1.2.5.1.1"><p id="p8167193643218"><a name="p8167193643218"></a><a name="p8167193643218"></a>Callback</p> 28</th> 29<th class="cellrowborder" valign="top" width="25%" id="mcps1.2.5.1.2"><p id="p31672362325"><a name="p31672362325"></a><a name="p31672362325"></a>Input Parameter</p> 30</th> 31<th class="cellrowborder" valign="top" width="24.98%" id="mcps1.2.5.1.3"><p id="p51673367328"><a name="p51673367328"></a><a name="p51673367328"></a>Return Value</p> 32</th> 33<th class="cellrowborder" valign="top" width="25.019999999999996%" id="mcps1.2.5.1.4"><p id="p1116883619322"><a name="p1116883619322"></a><a name="p1116883619322"></a>Description</p> 34</th> 35</tr> 36</thead> 37<tbody><tr id="row816883693214"><td class="cellrowborder" valign="top" width="25%" headers="mcps1.2.5.1.1 "><p id="p11683369329"><a name="p11683369329"></a><a name="p11683369329"></a>Transfer</p> 38</td> 39<td class="cellrowborder" valign="top" width="25%" headers="mcps1.2.5.1.2 "><p id="p0671913132514"><a name="p0671913132514"></a><a name="p0671913132514"></a><strong id="b19675134258"><a name="b19675134258"></a><a name="b19675134258"></a>cntlr</strong>: structure pointer to the SPI controller at the core layer.</p> 40<p id="p192109186258"><a name="p192109186258"></a><a name="p192109186258"></a><strong id="b1921051810258"><a name="b1921051810258"></a><a name="b1921051810258"></a>msg</strong>: structure pointer to the SPI message.</p> 41<p id="p6168736173213"><a name="p6168736173213"></a><a name="p6168736173213"></a><strong id="b92627310191"><a name="b92627310191"></a><a name="b92627310191"></a>count</strong>: number of messages. The value is of the uint32_t type.</p> 42</td> 43<td class="cellrowborder" valign="top" width="24.98%" headers="mcps1.2.5.1.3 "><p id="p19168736113216"><a name="p19168736113216"></a><a name="p19168736113216"></a>HDF_STATUS</p> 44</td> 45<td class="cellrowborder" valign="top" width="25.019999999999996%" headers="mcps1.2.5.1.4 "><p id="p18168236143218"><a name="p18168236143218"></a><a name="p18168236143218"></a>Transfers messages.</p> 46</td> 47</tr> 48<tr id="row3168113633211"><td class="cellrowborder" valign="top" width="25%" headers="mcps1.2.5.1.1 "><p id="p4168636193212"><a name="p4168636193212"></a><a name="p4168636193212"></a>SetCfg</p> 49</td> 50<td class="cellrowborder" valign="top" width="25%" headers="mcps1.2.5.1.2 "><p id="p242632402513"><a name="p242632402513"></a><a name="p242632402513"></a><strong id="b742642452513"><a name="b742642452513"></a><a name="b742642452513"></a>cntlr</strong>: structure pointer to the SPI controller at the core layer.</p> 51<p id="p11168163643217"><a name="p11168163643217"></a><a name="p11168163643217"></a><strong id="b1283910558207"><a name="b1283910558207"></a><a name="b1283910558207"></a>cfg</strong>: structure pointer to the SPI attributes.</p> 52</td> 53<td class="cellrowborder" valign="top" width="24.98%" headers="mcps1.2.5.1.3 "><p id="p101683367325"><a name="p101683367325"></a><a name="p101683367325"></a>HDF_STATUS</p> 54</td> 55<td class="cellrowborder" valign="top" width="25.019999999999996%" headers="mcps1.2.5.1.4 "><p id="p1216815368329"><a name="p1216815368329"></a><a name="p1216815368329"></a>Sets SPI controller attributes.</p> 56</td> 57</tr> 58<tr id="row13168183683215"><td class="cellrowborder" valign="top" width="25%" headers="mcps1.2.5.1.1 "><p id="p11168436123220"><a name="p11168436123220"></a><a name="p11168436123220"></a>GetCfg</p> 59</td> 60<td class="cellrowborder" valign="top" width="25%" headers="mcps1.2.5.1.2 "><p id="p9698153182520"><a name="p9698153182520"></a><a name="p9698153182520"></a><strong id="b19698131162510"><a name="b19698131162510"></a><a name="b19698131162510"></a>cntlr</strong>: structure pointer to the SPI controller at the core layer.</p> 61<p id="p3168936133218"><a name="p3168936133218"></a><a name="p3168936133218"></a><strong id="b11555148182113"><a name="b11555148182113"></a><a name="b11555148182113"></a>cfg</strong>: structure pointer to the SPI attributes.</p> 62</td> 63<td class="cellrowborder" valign="top" width="24.98%" headers="mcps1.2.5.1.3 "><p id="p18169036193219"><a name="p18169036193219"></a><a name="p18169036193219"></a>HDF_STATUS</p> 64</td> 65<td class="cellrowborder" valign="top" width="25.019999999999996%" headers="mcps1.2.5.1.4 "><p id="p3169136173219"><a name="p3169136173219"></a><a name="p3169136173219"></a>Obtains SPI controller attributes.</p> 66</td> 67</tr> 68<tr id="row9169133643218"><td class="cellrowborder" valign="top" width="25%" headers="mcps1.2.5.1.1 "><p id="p13169163615320"><a name="p13169163615320"></a><a name="p13169163615320"></a>Open</p> 69</td> 70<td class="cellrowborder" valign="top" width="25%" headers="mcps1.2.5.1.2 "><p id="p181697365327"><a name="p181697365327"></a><a name="p181697365327"></a><strong id="b1876822162218"><a name="b1876822162218"></a><a name="b1876822162218"></a>cntlr</strong>: structure pointer to the SPI controller at the core layer. </p> 71</td> 72<td class="cellrowborder" valign="top" width="24.98%" headers="mcps1.2.5.1.3 "><p id="p1169163693216"><a name="p1169163693216"></a><a name="p1169163693216"></a>HDF_STATUS</p> 73</td> 74<td class="cellrowborder" valign="top" width="25.019999999999996%" headers="mcps1.2.5.1.4 "><p id="p1116963693218"><a name="p1116963693218"></a><a name="p1116963693218"></a>Enables the SPI.</p> 75</td> 76</tr> 77<tr id="row11169436153214"><td class="cellrowborder" valign="top" width="25%" headers="mcps1.2.5.1.1 "><p id="p0169736153219"><a name="p0169736153219"></a><a name="p0169736153219"></a>Close</p> 78</td> 79<td class="cellrowborder" valign="top" width="25%" headers="mcps1.2.5.1.2 "><p id="p31691936193210"><a name="p31691936193210"></a><a name="p31691936193210"></a><strong id="b16798152112239"><a name="b16798152112239"></a><a name="b16798152112239"></a>cntlr</strong>: structure pointer to the SPI controller at the core layer. </p> 80</td> 81<td class="cellrowborder" valign="top" width="24.98%" headers="mcps1.2.5.1.3 "><p id="p2169123617322"><a name="p2169123617322"></a><a name="p2169123617322"></a>HDF_STATUS</p> 82</td> 83<td class="cellrowborder" valign="top" width="25.019999999999996%" headers="mcps1.2.5.1.4 "><p id="p1169636103220"><a name="p1169636103220"></a><a name="p1169636103220"></a>Disables the SPI.</p> 84</td> 85</tr> 86</tbody> 87</table> 88 89## How to Develop<a name="section799667984152909"></a> 90 91The SPI module adaptation involves the following steps: 92 931. Instantiate the driver entry. 94 - Instantiate the **HdfDriverEntry** structure. 95 - Call **HDF\_INIT** to register the **HdfDriverEntry** instance with the HDF. 96 972. Configure attribute files. 98 - Add the **deviceNode** information to the **device\_info.hcs** file. 99 - \(Optional\) Add the **spi\_config.hcs** file. 100 1013. Instantiate the SPI controller object. 102 - Initialize **SpiCntlr**. 103 - Instantiate **SpiCntlrMethod** in the **SpiCntlr** object. 104 105 > **NOTE** 106 107 >For details, see [Available APIs](#section752964871810). 108 1094. Debug the driver. 110 - \(Optional\) For new drivers, verify the basic functions, such as the SPI control status and response to interrupts. 111 112 113## Development Example<a name="section956157227152909"></a> 114 115The following uses **spi\_hi35xx.c** as an example to present the contents that need to be provided by the vendor to implement device functions. 116 1171. Instantiate the driver entry. The driver entry must be a global variable of the **HdfDriverEntry** type \(defined in **hdf\_device\_desc.h**\), and the value of **moduleName** must be the same as that in **device\_info.hcs**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. 118 119 Generally, HDF calls the **Bind** function and then the **Init** function to load a driver. If **Init** fails to be called, HDF calls **Release** to release driver resources and exit. 120 121 - SPI driver entry reference 122 123 ``` 124 struct HdfDriverEntry g_hdfSpiDevice = { 125 .moduleVersion = 1, 126 .moduleName = "HDF_PLATFORM_SPI",// (Mandatory) The value must be the same as that of moduleName in the .hcs file. 127 .Bind = HdfSpiDeviceBind, // See the Bind function. 128 .Init = HdfSpiDeviceInit, // See the Init function. 129 .Release = HdfSpiDeviceRelease, //See the Release function. 130 }; 131 // Call HDF_INIT to register the driver entry with the HDF. 132 HDF_INIT(g_hdfSpiDevice); 133 ``` 134 1352. Add the **deviceNode** information to the **device\_info.hcs** file and configure the device attributes in the **spi\_config.hcs** file. The **deviceNode** information is related to registration of the driver entry. The device attribute values are closely related to the default values or value ranges of the **SpiCntlr** members at the core layer. 136 137 In this example, there is only one SPI controller. If there are multiple SPI controllers, you need to add the **deviceNode** information to the **device\_info** file and add the corresponding device attributes to the **spi\_config** file. 138 139 - **device\_info.hcs** configuration reference 140 141 ``` 142 root { 143 device_info { 144 match_attr = "hdf_manager"; 145 platform :: host { 146 hostName = "platform_host"; 147 priority = 50; 148 device_spi :: device {// Configure an HDF device node for each SPI controller. 149 device0 :: deviceNode { 150 policy = 1; 151 priority = 60; 152 permission = 0644; 153 moduleName = "HDF_PLATFORM_SPI"; 154 serviceName = "HDF_PLATFORM_SPI_0"; 155 deviceMatchAttr = "hisilicon_hi35xx_spi_0"; 156 } 157 device1 :: deviceNode { 158 policy = 1; 159 priority = 60; 160 permission = 0644; 161 moduleName = "HDF_PLATFORM_SPI"; // (Mandatory) Driver name, which must be the same as that of moduleName in the driver entry structure. 162 serviceName = "HDF_PLATFORM_SPI_1"; // (Mandatory) Unique name of the service published by the driver 163 deviceMatchAttr = "hisilicon_hi35xx_spi_1";// The value must be the same as that of match_attr in the .hcs file. 164 } 165 ... 166 } 167 } 168 } 169 } 170 ``` 171 172 - **spi\_config.hcs** configuration reference 173 174 ``` 175 root { 176 platform { 177 spi_config {// Configure private data for each SPI controller. 178 template spi_controller {// Template configuration. In the template, you can configure the common parameters shared by service nodes. 179 serviceName = ""; 180 match_attr = ""; 181 transferMode = 0; // Data transfer mode, which can be interrupt transfer (0), flow control transfer (1), or DMA transfer (2). 182 busNum = 0; // Bus number 183 clkRate = 100000000; 184 bitsPerWord = 8; // Bit width of the data transferred 185 mode = 19; // SPI data input/output mode 186 maxSpeedHz = 0; // Maximum clock frequency 187 minSpeedHz = 0; // Minimum clock frequency 188 speed = 2000000; // Current message transfer speed 189 fifoSize = 256; // FIFO size 190 numCs = 1; // Chip select (CS) number 191 regBase = 0x120c0000; // Used for address mapping. 192 irqNum = 100; // Interruption number 193 REG_CRG_SPI = 0x120100e4; // CRG_REG_BASE(0x12010000) + 0x0e4 194 CRG_SPI_CKEN = 0; 195 CRG_SPI_RST = 0; 196 REG_MISC_CTRL_SPI = 0x12030024; // MISC_REG_BASE(0x12030000) + 0x24 197 MISC_CTRL_SPI_CS = 0; 198 MISC_CTRL_SPI_CS_SHIFT = 0; 199 } 200 controller_0x120c0000 :: spi_controller { 201 busNum = 0; // (Mandatory) Bus number 202 CRG_SPI_CKEN = 0x10000; // (0x1 << 16) 0:close clk, 1:open clk 203 CRG_SPI_RST = 0x1; // (0x1 << 0) 0:cancel reset, 1:reset 204 match_attr = "hisilicon_hi35xx_spi_0";// (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs. 205 } 206 controller_0x120c1000 :: spi_controller { 207 busNum = 1; 208 CRG_SPI_CKEN = 0x20000; // (0x1 << 17) 0:close clk, 1:open clk 209 CRG_SPI_RST = 0x2; // (0x1 << 1) 0:cancel reset, 1:reset 210 match_attr = "hisilicon_hi35xx_spi_1"; 211 regBase = 0x120c1000; // (Mandatory) Used for address mapping. 212 irqNum = 101; // (Mandatory) Interrupt number 213 } 214 ... 215 //(Optional) Add nodes to the device_info.hcs file as required. 216 } 217 } 218 } 219 ``` 220 2213. Initialize the **SpiCntlr** object at the core layer, including initializing the vendor custom structure \(transferring parameters and data\), instantiating **SpiCntlrMethod** \(used to call underlying functions of the driver\) in **SpiCntlr**, and implementing the **HdfDriverEntry** member functions \(**Bind**, **Init**, and **Release**\). 222 - Custom structure reference 223 224 To the driver, the custom structure carries parameters and data. The values in the **spi\_config.hcs** file are read by HDF, and the structure members are initialized through **DeviceResourceIface**. Some important values, such as the device number and bus number, are also passed to the object at the core layer. 225 226 ``` 227 struct Pl022 {// Corresponds to parameters in .hcs. 228 struct SpiCntlr *cntlr; 229 struct DListHead deviceList; 230 struct OsalSem sem; 231 volatile unsigned char *phyBase; 232 volatile unsigned char *regBase; 233 uint32_t irqNum; 234 uint32_t busNum; 235 uint32_t numCs; 236 uint32_t curCs; 237 uint32_t speed; 238 uint32_t fifoSize; 239 uint32_t clkRate; 240 uint32_t maxSpeedHz; 241 uint32_t minSpeedHz; 242 uint32_t regCrg; 243 uint32_t clkEnBit; 244 uint32_t clkRstBit; 245 uint32_t regMiscCtrl; 246 uint32_t miscCtrlCsShift; 247 uint32_t miscCtrlCs; 248 uint16_t mode; 249 uint8_t bitsPerWord; 250 uint8_t transferMode; 251 }; 252 253 // SpiCntlr is the core layer controller structure. Its members are assigned with values by using the Init function. 254 struct SpiCntlr { 255 struct IDeviceIoService service; 256 struct HdfDeviceObject *device; 257 uint32_t busNum; 258 uint32_t numCs; 259 uint32_t curCs; 260 struct OsalMutex lock; 261 struct SpiCntlrMethod *method; 262 struct DListHead list; 263 void *priv; 264 }; 265 ``` 266 267 - Instantiate the callback function structure **SpiCntlrMethod** in **SpiCntlr**. Other members are initialized by using the **Init** function. 268 269 ``` 270 // Example in spi_hi35xx.c: instantiate the hook. 271 struct SpiCntlrMethod g_method = { 272 .Transfer = Pl022Transfer, 273 .SetCfg = Pl022SetCfg, 274 .GetCfg = Pl022GetCfg, 275 .Open = Pl022Open, 276 .Close = Pl022Close, 277 }; 278 ``` 279 280 - Bind function 281 282 Input parameters: 283 284 **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs configuration file information. 285 286 Return values: 287 288 HDF\_STATUS 289 290 Function description: 291 292 Associates the **SpiCntlr** object with **HdfDeviceObject**. 293 294 ``` 295 static int32_t HdfSpiDeviceBind(struct HdfDeviceObject *device) 296 { 297 ... 298 return (SpiCntlrCreate(device) == NULL) ? HDF_FAILURE : HDF_SUCCESS; 299 } 300 301 struct SpiCntlr *SpiCntlrCreate(struct HdfDeviceObject *device) 302 { 303 struct SpiCntlr *cntlr = NULL; // Create the SpiCntlr object of the core layer. 304 ... 305 cntlr = (struct SpiCntlr *)OsalMemCalloc(sizeof(*cntlr));// Allocate memory. 306 ... 307 cntlr->device = device; // Enable conversion between HdfDeviceObject and SpiCntlr. 308 device->service = &(cntlr->service); // Enable conversion between HdfDeviceObject and SpiCntlr. 309 (void)OsalMutexInit(&cntlr->lock); // Initialize the lock. 310 DListHeadInit(&cntlr->list); // Add the corresponding node. 311 cntlr->priv = NULL; 312 return cntlr; 313 } 314 ``` 315 316 - Init function 317 318 Input parameters: 319 320 **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs configuration file information. 321 322 Return values: 323 324 HDF\_STATUS \(The following table lists some status. For details about other status, see **HDF\_STATUS** in the **/drivers/framework/include/utils/hdf\_base.h** file.\) 325 326 **Table 2** Input parameters and return values of the init function 327 328 <a name="table4311552173914"></a> 329 <table><thead align="left"><tr id="row8314152103916"><th class="cellrowborder" valign="top" width="50%" id="mcps1.2.3.1.1"><p id="p331413523392"><a name="p331413523392"></a><a name="p331413523392"></a>Status (Value)</p> 330 </th> 331 <th class="cellrowborder" valign="top" width="50%" id="mcps1.2.3.1.2"><p id="p7314145220390"><a name="p7314145220390"></a><a name="p7314145220390"></a>Description</p> 332 </th> 333 </tr> 334 </thead> 335 <tbody><tr id="row93142052183918"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.2.3.1.1 "><p id="p93141252123914"><a name="p93141252123914"></a><a name="p93141252123914"></a>HDF_ERR_INVALID_OBJECT</p> 336 </td> 337 <td class="cellrowborder" valign="top" width="50%" headers="mcps1.2.3.1.2 "><p id="p331485215390"><a name="p331485215390"></a><a name="p331485215390"></a>Invalid controller object</p> 338 </td> 339 </tr> 340 <tr id="row331445211399"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.2.3.1.1 "><p id="p83141352183917"><a name="p83141352183917"></a><a name="p83141352183917"></a>HDF_ERR_MALLOC_FAIL</p> 341 </td> 342 <td class="cellrowborder" valign="top" width="50%" headers="mcps1.2.3.1.2 "><p id="p15314175220397"><a name="p15314175220397"></a><a name="p15314175220397"></a>Failed to allocate memory</p> 343 </td> 344 </tr> 345 <tr id="row20314175213918"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.2.3.1.1 "><p id="p9314352113917"><a name="p9314352113917"></a><a name="p9314352113917"></a>HDF_ERR_INVALID_PARAM</p> 346 </td> 347 <td class="cellrowborder" valign="top" width="50%" headers="mcps1.2.3.1.2 "><p id="p131535273910"><a name="p131535273910"></a><a name="p131535273910"></a>Invalid parameter</p> 348 </td> 349 </tr> 350 <tr id="row1431565233911"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.2.3.1.1 "><p id="p0315052103915"><a name="p0315052103915"></a><a name="p0315052103915"></a>HDF_ERR_IO</p> 351 </td> 352 <td class="cellrowborder" valign="top" width="50%" headers="mcps1.2.3.1.2 "><p id="p1831585215398"><a name="p1831585215398"></a><a name="p1831585215398"></a>I/O error</p> 353 </td> 354 </tr> 355 <tr id="row2315152193914"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.2.3.1.1 "><p id="p7315185263919"><a name="p7315185263919"></a><a name="p7315185263919"></a>HDF_SUCCESS</p> 356 </td> 357 <td class="cellrowborder" valign="top" width="50%" headers="mcps1.2.3.1.2 "><p id="p1731585213910"><a name="p1731585213910"></a><a name="p1731585213910"></a>Initialization successful</p> 358 </td> 359 </tr> 360 <tr id="row5315752163916"><td class="cellrowborder" valign="top" width="50%" headers="mcps1.2.3.1.1 "><p id="p6315105211392"><a name="p6315105211392"></a><a name="p6315105211392"></a>HDF_FAILURE</p> 361 </td> 362 <td class="cellrowborder" valign="top" width="50%" headers="mcps1.2.3.1.2 "><p id="p83157528399"><a name="p83157528399"></a><a name="p83157528399"></a>Initialization failed</p> 363 </td> 364 </tr> 365 </tbody> 366 </table> 367 368 Function description: 369 370 Initializes the custom structure object and **SpiCntlr**. 371 372 ``` 373 static int32_t HdfSpiDeviceInit(struct HdfDeviceObject *device) 374 { 375 int32_t ret; 376 struct SpiCntlr *cntlr = NULL; 377 ... 378 cntlr = SpiCntlrFromDevice(device);// Forcibly convert HdfDeviceObject to SpiCntlr by using service. For details about the value assignment, see the Bind function. 379 //return (device == NULL) ? NULL : (struct SpiCntlr *)device->service; 380 ... 381 ret = Pl022Init(cntlr, device);// (Mandatory) Instantiate the operation object customized by the vendor. The following is an example: 382 ... 383 ret = Pl022Probe(cntlr->priv); 384 ... 385 return ret; 386 } 387 388 static int32_t Pl022Init(struct SpiCntlr *cntlr, const struct HdfDeviceObject *device) 389 { 390 int32_t ret; 391 struct Pl022 *pl022 = NULL; 392 ... 393 pl022 = (struct Pl022 *)OsalMemCalloc(sizeof(*pl022));// Apply for memory. 394 ... 395 ret = SpiGetBaseCfgFromHcs(pl022, device->property); // Initialize busNum, numCs, speed, fifoSize, clkRate, mode, bitsPerWord, and transferMode. 396 ... 397 ret = SpiGetRegCfgFromHcs(pl022, device->property); // Initialize regBase, phyBase, irqNum, regCrg, clkEnBit ,clkRstBit, regMiscCtrl, miscCtrlCs, and miscCtrlCsShift. 398 ... 399 // Calculate the frequencies corresponding to the maximum and minimum speeds. 400 pl022->maxSpeedHz = (pl022->clkRate) / ((SCR_MIN + 1) * CPSDVSR_MIN); 401 pl022->minSpeedHz = (pl022->clkRate) / ((SCR_MAX + 1) * CPSDVSR_MAX); 402 DListHeadInit(&pl022->deviceList);// Initialize the DList linked list. 403 pl022->cntlr = cntlr; // Enable conversion between Pl022 and SpiCntlr. 404 cntlr->priv = pl022; // Enable conversion between Pl022 and SpiCntlr. 405 cntlr->busNum = pl022->busNum; // Assign a value to busNum in SpiCntlr. 406 cntlr->method = &g_method; // Connect to the SpiCntlrMethod instance. 407 ... 408 ret = Pl022CreatAndInitDevice(pl022); 409 if (ret != 0) { 410 Pl022Release(pl022); // Release the Pl022 object if the initialization fails. 411 return ret; 412 } 413 return 0; 414 } 415 ``` 416 417 - Release function 418 419 Input parameters: 420 421 **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs configuration file information. 422 423 Return values: 424 425 – 426 427 Function description: 428 429 Releases the memory and deletes the controller. This function assigns a value to the **Release** API in the driver entry structure. When the HDF fails to call the **Init** function to initialize the driver, the **Release** function can be called to release driver resources. All forced conversion operations for obtaining the corresponding object can be successful only when the **Init** function has the corresponding value assignment operations. 430 431 ``` 432 static void HdfSpiDeviceRelease(struct HdfDeviceObject *device) 433 { 434 struct SpiCntlr *cntlr = NULL; 435 ... 436 cntlr = SpiCntlrFromDevice(device);// Forcibly convert HdfDeviceObject to SpiCntlr by using service. For details about the value assignment, see the Bind function. 437 // return (device==NULL) ?NULL:(struct SpiCntlr *)device->service; 438 ... 439 if (cntlr->priv != NULL) { 440 Pl022Remove((struct Pl022 *)cntlr->priv);// A forced conversion from SpiCntlr to Pl022 is involved. 441 } 442 SpiCntlrDestroy(cntlr); // Release the Pl022 object. 443 } 444 ``` 445 446 447 448 449