1 /*
2 * Copyright (c) 2021-2023 Huawei Device Co., Ltd.
3 *
4 * HDF is dual licensed: you can use it either under the terms of
5 * the GPL, or the BSD license, at your option.
6 * See the LICENSE file in the root of this repository for complete details.
7 */
8
9 #include "pcie_dispatch.h"
10 #include "hdf_log.h"
11 #include "osal_mem.h"
12 #include "pcie_core.h"
13 #include "pcie_if.h"
14 #include "platform_listener_common.h"
15
16 #define HDF_LOG_TAG pcie_dispatch_c
17 #define IRQ_CB_NUM 0
18 #define DMA_CB_NUM 1
19 #define USER_LEM_MAX 4096
20 #define DMA_ALIGN_SIZE 256
21
22 enum PcieIoCmd {
23 PCIE_CMD_READ = 0,
24 PCIE_CMD_WRITE,
25 PCIE_CMD_DMA_MAP,
26 PCIE_CMD_DMA_UNMAP,
27 PCIE_CMD_REG_IRQ,
28 PCIE_CMD_UNREG_IRQ,
29 PCIE_CMD_BUTT,
30 };
31
PcieCmdRead(struct PcieCntlr * cntlr,struct HdfSBuf * data,struct HdfSBuf * reply)32 static int32_t PcieCmdRead(struct PcieCntlr *cntlr, struct HdfSBuf *data, struct HdfSBuf *reply)
33 {
34 uint32_t mode;
35 uint32_t len;
36 uint32_t pos;
37 uint8_t *buf = NULL;
38 int32_t ret;
39
40 if (!HdfSbufReadUint32(data, &mode)) {
41 HDF_LOGE("PcieCmdRead: read mode fail");
42 return HDF_ERR_IO;
43 }
44 if (!HdfSbufReadUint32(data, &len)) {
45 HDF_LOGE("PcieCmdRead: read len fail");
46 return HDF_ERR_IO;
47 }
48 if (len == 0 || len > USER_LEM_MAX) {
49 HDF_LOGE("PcieCmdRead: invalid len");
50 return HDF_ERR_INVALID_PARAM;
51 }
52 if (!HdfSbufReadUint32(data, &pos)) {
53 HDF_LOGE("PcieCmdRead: read pos fail");
54 return HDF_ERR_IO;
55 }
56
57 buf = (uint8_t *)OsalMemCalloc(sizeof(*buf) * len);
58 if (buf == NULL) {
59 HDF_LOGE("PcieCmdRead: OsalMemCalloc error");
60 return HDF_ERR_MALLOC_FAIL;
61 }
62
63 ret = PcieCntlrRead(cntlr, mode, pos, buf, len);
64 if (ret != HDF_SUCCESS) {
65 HDF_LOGE("PcieCmdRead: error, ret is %d", ret);
66 OsalMemFree(buf);
67 return ret;
68 }
69 if (!HdfSbufWriteBuffer(reply, buf, len)) {
70 HDF_LOGE("PcieCmdRead: sbuf write buffer fail!");
71 OsalMemFree(buf);
72 return HDF_ERR_IO;
73 }
74
75 OsalMemFree(buf);
76 return HDF_SUCCESS;
77 }
78
PcieCmdWrite(struct PcieCntlr * cntlr,struct HdfSBuf * data)79 static int32_t PcieCmdWrite(struct PcieCntlr *cntlr, struct HdfSBuf *data)
80 {
81 uint32_t pos;
82 uint32_t len;
83 uint32_t size;
84 uint32_t mode;
85 uint8_t *buf = NULL;
86
87 if (!HdfSbufReadUint32(data, &mode)) {
88 HDF_LOGE("PcieCmdWrite: read pos fail");
89 return HDF_ERR_IO;
90 }
91 if (!HdfSbufReadUint32(data, &len)) {
92 HDF_LOGE("PcieCmdWrite: read pos fail");
93 return HDF_ERR_IO;
94 }
95 if (!HdfSbufReadUint32(data, &pos)) {
96 HDF_LOGE("PcieCmdWrite: read pos fail");
97 return HDF_ERR_IO;
98 }
99 if (!HdfSbufReadBuffer(data, (const void **)&buf, &size)) {
100 HDF_LOGE("PcieCmdWrite: sbuf read buffer fail!");
101 return HDF_ERR_IO;
102 }
103 if (len == 0 || len != size) {
104 HDF_LOGE("PcieCmdWrite: read sbuf error");
105 return HDF_ERR_IO;
106 }
107 return PcieCntlrWrite(cntlr, mode, pos, buf, len);
108 }
109
PcieIoDmaCb(DevHandle handle)110 static int32_t PcieIoDmaCb(DevHandle handle)
111 {
112 struct HdfSBuf *data = NULL;
113 struct PcieCntlr *cntlr = (struct PcieCntlr *)handle;
114 int32_t ret;
115
116 if (cntlr == NULL) {
117 HDF_LOGE("PcieIoDmaCb: cntlr is null!");
118 return HDF_ERR_INVALID_PARAM;
119 }
120
121 data = HdfSbufObtainDefaultSize();
122 if (data == NULL) {
123 HDF_LOGE("PcieIoDmaCb: fail to obtain data!");
124 return HDF_ERR_MALLOC_FAIL;
125 }
126 if (!HdfSbufWriteUint32(data, DMA_CB_NUM)) {
127 HDF_LOGE("PcieIoDmaCb: sbuf write num fail!");
128 HdfSbufRecycle(data);
129 return HDF_ERR_IO;
130 }
131 if (cntlr->dir == PCIE_DMA_FROM_DEVICE) {
132 if (!HdfSbufWriteBuffer(data, (const void *)cntlr->dmaData, cntlr->len)) {
133 HDF_LOGE("PcieIoDmaCb: sbuf write buffer fail!");
134 HdfSbufRecycle(data);
135 return HDF_ERR_IO;
136 }
137 }
138 ret = HdfDeviceSendEvent(cntlr->hdfDevObj, PLATFORM_LISTENER_EVENT_PCIE_NOTIFY, data);
139 if (ret != HDF_SUCCESS) {
140 HDF_LOGE("PcieIoDmaCb: send event fail");
141 }
142
143 HdfSbufRecycle(data);
144 return ret;
145 }
146
DmaToDevice(struct PcieCntlr * cntlr,struct HdfSBuf * data,uint32_t len)147 static int32_t DmaToDevice(struct PcieCntlr *cntlr, struct HdfSBuf *data, uint32_t len)
148 {
149 uint8_t *buf = NULL;
150 uint32_t size;
151
152 if (!HdfSbufReadBuffer(data, (const void **)&buf, &size) || size != len) {
153 HDF_LOGE("DmaToDevice: sbuf read buffer fail!");
154 return HDF_ERR_IO;
155 }
156 cntlr->dmaData = (uintptr_t)buf;
157 cntlr->len = len;
158 cntlr->dir = PCIE_DMA_TO_DEVICE;
159
160 return PcieCntlrDmaMap(cntlr, PcieIoDmaCb, (uintptr_t)buf, len, PCIE_DMA_TO_DEVICE);
161 }
162
DeviceToDma(struct PcieCntlr * cntlr,uint32_t len)163 static int32_t DeviceToDma(struct PcieCntlr *cntlr, uint32_t len)
164 {
165 uint8_t *addr = NULL;
166
167 addr = OsalMemAllocAlign(DMA_ALIGN_SIZE, len);
168 if (addr == NULL) {
169 HDF_LOGE("DeviceToDma: malloc fail");
170 return HDF_ERR_MALLOC_FAIL;
171 }
172 cntlr->dmaData = (uintptr_t)addr;
173 cntlr->len = len;
174 cntlr->dir = PCIE_DMA_FROM_DEVICE;
175
176 return PcieCntlrDmaMap(cntlr, PcieIoDmaCb, (uintptr_t)addr, len, PCIE_DMA_FROM_DEVICE);
177 }
178
PcieCmdDmaMap(struct PcieCntlr * cntlr,struct HdfSBuf * data)179 static int32_t PcieCmdDmaMap(struct PcieCntlr *cntlr, struct HdfSBuf *data)
180 {
181 int32_t ret;
182 uint32_t len;
183 uint8_t dir;
184
185 if (!HdfSbufReadUint8(data, &dir)) {
186 HDF_LOGE("PcieCmdDmaMap: read dir fail");
187 return HDF_ERR_IO;
188 }
189 if (!HdfSbufReadUint32(data, &len)) {
190 HDF_LOGE("PcieCmdDmaMap: read len fail");
191 return HDF_ERR_IO;
192 }
193 if (len == 0 || len > USER_LEM_MAX) {
194 HDF_LOGE("PcieCmdDmaMap: invalid len");
195 return HDF_ERR_INVALID_PARAM;
196 }
197 if (cntlr->dmaData != 0 || cntlr->len != 0) {
198 return HDF_ERR_DEVICE_BUSY;
199 }
200 if (dir == PCIE_DMA_FROM_DEVICE) {
201 ret = DeviceToDma(cntlr, len);
202 } else if (dir == PCIE_DMA_TO_DEVICE) {
203 ret = DmaToDevice(cntlr, data, len);
204 } else {
205 HDF_LOGE("PcieCmdDmaMap: invalid dir");
206 ret = HDF_ERR_INVALID_PARAM;
207 }
208
209 return ret;
210 }
211
PcieCmdDmaUnmap(struct PcieCntlr * cntlr,struct HdfSBuf * data)212 static int32_t PcieCmdDmaUnmap(struct PcieCntlr *cntlr, struct HdfSBuf *data)
213 {
214 uint8_t dir;
215
216 if (!HdfSbufReadUint8(data, &dir) || dir != cntlr->dir) {
217 HDF_LOGE("PcieCmdDmaUnmap: read dir fail");
218 return HDF_ERR_IO;
219 }
220
221 PcieCntlrDmaUnmap(cntlr, cntlr->dmaData, cntlr->len, dir);
222 OsalMemFree((void *)cntlr->dmaData);
223 cntlr->dmaData = 0;
224 cntlr->len = 0;
225
226 return HDF_SUCCESS;
227 }
228
PcieIoCallback(DevHandle handle)229 static int32_t PcieIoCallback(DevHandle handle)
230 {
231 struct HdfSBuf *data = NULL;
232 struct PcieCntlr *cntlr = (struct PcieCntlr *)handle;
233 int32_t ret;
234
235 if (cntlr == NULL) {
236 HDF_LOGE("PcieIoCallback: cntlr is null!");
237 return HDF_ERR_INVALID_PARAM;
238 }
239
240 data = HdfSbufObtainDefaultSize();
241 if (data == NULL) {
242 HDF_LOGE("PcieIoCallback: fail to obtain data!");
243 return HDF_ERR_MALLOC_FAIL;
244 }
245 if (!HdfSbufWriteUint32(data, IRQ_CB_NUM)) {
246 HDF_LOGE("PcieIoCallback: sbuf write num fail!");
247 HdfSbufRecycle(data);
248 return HDF_ERR_IO;
249 }
250
251 ret = HdfDeviceSendEvent(cntlr->hdfDevObj, PLATFORM_LISTENER_EVENT_PCIE_NOTIFY, data);
252 HdfSbufRecycle(data);
253 return ret;
254 }
255
PcieCmdRegisterIrq(struct PcieCntlr * cntlr)256 static int32_t PcieCmdRegisterIrq(struct PcieCntlr *cntlr)
257 {
258 return PcieCntlrRegisterIrq(cntlr, PcieIoCallback);
259 }
260
PcieCmdUnregisterIrq(struct PcieCntlr * cntlr)261 static int32_t PcieCmdUnregisterIrq(struct PcieCntlr *cntlr)
262 {
263 PcieCntlrUnregisterIrq(cntlr);
264 return HDF_SUCCESS;
265 }
266
PcieIoDispatch(struct HdfDeviceIoClient * client,int32_t cmd,struct HdfSBuf * data,struct HdfSBuf * reply)267 int32_t PcieIoDispatch(struct HdfDeviceIoClient *client, int32_t cmd, struct HdfSBuf *data, struct HdfSBuf *reply)
268 {
269 struct PcieCntlr *cntlr = NULL;
270
271 if (client == NULL || client->device == NULL) {
272 HDF_LOGE("PcieIoDispatch: client or hdf dev obj is null!");
273 return HDF_ERR_INVALID_OBJECT;
274 }
275
276 cntlr = (struct PcieCntlr *)client->device->service;
277 if (cntlr == NULL) {
278 HDF_LOGE("PcieIoDispatch: service is null!");
279 return HDF_ERR_INVALID_OBJECT;
280 }
281
282 switch (cmd) {
283 case PCIE_CMD_READ:
284 return PcieCmdRead(cntlr, data, reply);
285 case PCIE_CMD_WRITE:
286 return PcieCmdWrite(cntlr, data);
287 case PCIE_CMD_DMA_MAP:
288 return PcieCmdDmaMap(cntlr, data);
289 case PCIE_CMD_DMA_UNMAP:
290 return PcieCmdDmaUnmap(cntlr, data);
291 case PCIE_CMD_REG_IRQ:
292 return PcieCmdRegisterIrq(cntlr);
293 case PCIE_CMD_UNREG_IRQ:
294 return PcieCmdUnregisterIrq(cntlr);
295 default:
296 HDF_LOGE("PcieIoDispatch: cmd %d is not support", cmd);
297 return HDF_ERR_NOT_SUPPORT;
298 }
299
300 return HDF_SUCCESS;
301 }
302