xref: /drivers/peripheral/usb/net/src/cdc_ether.c (revision 094332d3)
1/*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include <unistd.h>
17
18#include "cdc_ether.h"
19#include "hdf_base.h"
20#include "hdf_log.h"
21#include "hdf_usb_pnp_manage.h"
22#include "osal_mem.h"
23#include "osal_time.h"
24#include "securec.h"
25#include "usb_ddk_interface.h"
26#ifndef USBD_WRAPPER_H
27#define USBD_WRAPPER_H
28// define Domain ID
29#ifdef LOG_DOMAIN
30#undef LOG_DOMAIN
31#endif
32#define LOG_DOMAIN 0xD002518
33#endif // USBD_WRAPPER_H
34
35#define HDF_LOG_TAG USB_HOST_ECM
36
37static bool g_ecmReleaseFlag = false;
38
39static void EcmWriteBulk(struct UsbRequest *req);
40static void EcmAllocWriteReq(struct EcmDevice * const ecm);
41static void EcmFreeWriteReq(struct EcmDevice * const ecm);
42static int32_t EcmAllocIntReq(struct EcmDevice *ecm);
43static void EcmAllocReadReq(struct EcmDevice *ecm);
44static void EcmFreeReadReq(struct EcmDevice *ecm);
45static int32_t EcmInit(struct EcmDevice *ecm);
46static void EcmRelease(struct EcmDevice *ecm);
47static struct UsbInterface *EcmGetUsbInterfaceById(struct EcmDevice *ecm, uint8_t interfaceIndex);
48
49static int32_t EcmWbAlloc(struct EcmDevice *ecm)
50{
51    int32_t i, wbn;
52    struct EcmWb *wb = NULL;
53    wbn = 0;
54    i = 0;
55    OsalMutexLock(&ecm->writeLock);
56    for (;;) {
57        wb = &ecm->wb[wbn];
58        if (!wb->use) {
59            wb->use = 1;
60            wb->len = 0;
61            OsalMutexUnlock(&ecm->writeLock);
62            return wbn;
63        }
64        wbn = (wbn + 1) % ECM_NW;
65        if (++i >= ECM_NW) {
66            OsalMutexUnlock(&ecm->writeLock);
67            return 0;
68        }
69    }
70    OsalMutexUnlock(&ecm->writeLock);
71}
72
73static UsbInterfaceHandle *InterfaceIdToHandle(const struct EcmDevice *ecm, uint8_t id)
74{
75    UsbInterfaceHandle *devHandle = NULL;
76
77    if (id == 0xFF) {
78        devHandle = ecm->ctrDevHandle;
79    } else {
80        for (int32_t i = 0; i < ecm->interfaceCnt; i++) {
81            if (ecm->iface[i]->info.interfaceIndex == id) {
82                devHandle = ecm->devHandle[i];
83                break;
84            }
85        }
86    }
87    return devHandle;
88}
89
90static int32_t EcmAllocFifo(struct DataFifo *fifo, uint32_t size)
91{
92    if (!DataFifoIsInitialized(fifo)) {
93        void *data = OsalMemAlloc(size);
94        if (data == NULL) {
95            HDF_LOGE("%{public}s: allocate fifo data buffer failed", __func__);
96            return HDF_ERR_MALLOC_FAIL;
97        }
98        DataFifoInit(fifo, size, data);
99    }
100    return HDF_SUCCESS;
101}
102
103static void EcmFreeFifo(struct DataFifo *fifo)
104{
105    void *buf = fifo->data;
106    OsalMemFree(buf);
107    DataFifoInit(fifo, 0, NULL);
108}
109
110static int32_t EcmWbIsAvail(struct EcmDevice *ecm)
111{
112    int32_t i, n;
113    n = ECM_NW;
114    OsalMutexLock(&ecm->writeLock);
115    for (i = 0; i < ECM_NW; i++) {
116        n -= ecm->wb[i].use;
117    }
118    OsalMutexUnlock(&ecm->writeLock);
119    return n;
120}
121
122static int32_t EcmStartWb(struct EcmDevice *ecm, struct EcmWb *wb)
123{
124    int32_t rc;
125    struct UsbRequestParams parmas = {};
126    ecm->transmitting++;
127    parmas.interfaceId = ecm->dataOutPipe->interfaceId;
128    parmas.pipeAddress = ecm->dataOutPipe->pipeAddress;
129    parmas.pipeId = ecm->dataOutPipe->pipeId;
130    parmas.callback = EcmWriteBulk;
131    parmas.requestType = USB_REQUEST_PARAMS_DATA_TYPE;
132    parmas.timeout = USB_CTRL_SET_TIMEOUT;
133    parmas.dataReq.numIsoPackets = 0;
134    parmas.userData = (void *)wb;
135    parmas.dataReq.length = (uint32_t)wb->len;
136    parmas.dataReq.buffer = wb->buf;
137    rc = UsbFillRequest(wb->request, InterfaceIdToHandle(ecm, ecm->dataOutPipe->interfaceId), &parmas);
138    if (rc != HDF_SUCCESS) {
139        HDF_LOGE("%{public}s: UsbFillRequest failed, ret=%{public}d", __func__, rc);
140        return rc;
141    }
142    ecm->writeReq = wb->request;
143    rc = UsbSubmitRequestAsync(wb->request);
144    if (rc < 0) {
145        HDF_LOGE("UsbRequestSubmitSync failed, ret=%{public}d", rc);
146        OsalMutexLock(&ecm->writeLock);
147        wb->use = 0;
148        OsalMutexUnlock(&ecm->writeLock);
149        ecm->transmitting--;
150    } else {
151        ecm->writeReqNum++;
152    }
153    return rc;
154}
155
156static int32_t EcmWriteBufAlloc(struct EcmDevice *ecm)
157{
158    int32_t i;
159    struct EcmWb *wb;
160    for (wb = &ecm->wb[0], i = 0; i < ECM_NW; i++, wb++) {
161        wb->buf = OsalMemCalloc(ecm->writeSize);
162        if (!wb->buf) {
163            while (i != 0) {
164                --i;
165                --wb;
166                OsalMemFree(wb->buf);
167                wb->buf = NULL;
168            }
169            return -HDF_ERR_MALLOC_FAIL;
170        }
171    }
172    return 0;
173}
174
175static int32_t EcmWriteBufFree(struct EcmDevice *ecm)
176{
177    int32_t i;
178    struct EcmWb *wb;
179    for (wb = &ecm->wb[0], i = 0; i < ECM_NW; i++, wb++) {
180        if (wb->buf) {
181            OsalMemFree(wb->buf);
182            wb->buf = NULL;
183        }
184    }
185    return 0;
186}
187
188static void EcmWriteBulk(struct UsbRequest *req)
189{
190    int32_t status = req->compInfo.status;
191    struct EcmWb *wb = (struct EcmWb *)req->compInfo.userData;
192    struct EcmDevice *ecm = wb->ecm;
193    ecm->writeReqNum--;
194
195    switch (status) {
196        case USB_REQUEST_COMPLETED:
197            OsalMutexLock(&ecm->writeLock);
198            wb->use = 0;
199            OsalMutexUnlock(&ecm->writeLock);
200            break;
201        case -ECONNRESET:
202        case -ENOENT:
203        case -ESHUTDOWN:
204            return;
205        default:
206            goto EXIT;
207    }
208EXIT:
209    return;
210}
211
212static struct UsbControlRequest EcmUsbControlMsg(const struct EcmControlParams * const controlParams)
213{
214    struct UsbControlRequest dr;
215    dr.target = controlParams->requestType & TARGET_MASK;
216    dr.reqType = (controlParams->requestType >> USB_TYPE_OFFSET) & REQUEST_TYPE_MASK;
217    dr.directon = (controlParams->requestType >> USB_DIR_OFFSET) & DIRECTION_MASK;
218    dr.request = controlParams->request;
219    dr.value = CPU_TO_LE16(controlParams->value);
220    dr.index = CPU_TO_LE16(controlParams->index);
221    dr.buffer = controlParams->data;
222    dr.length = CPU_TO_LE16(controlParams->size);
223    return dr;
224}
225
226static int32_t EcmCtrlMsg(struct EcmDevice *ecm, uint8_t request, uint16_t value, void *buf, uint16_t len)
227{
228    int32_t ret;
229    const uint16_t index = 0;
230    struct UsbRequest *usbRequest = NULL;
231    struct EcmControlParams controlParams;
232    struct UsbRequestParams parmas = {};
233    if (ecm == NULL) {
234        HDF_LOGE("%{public}s:invalid param", __func__);
235        return HDF_ERR_IO;
236    }
237    usbRequest = UsbAllocRequest(ecm->ctrDevHandle, 0, len);
238    if (usbRequest == NULL) {
239        HDF_LOGE("%{public}s: UsbAllocRequest failed", __func__);
240        return HDF_ERR_IO;
241    }
242    ecm->ctrlReq = usbRequest;
243
244    controlParams.request = request;
245    controlParams.requestType = USB_DDK_TYPE_CLASS | USB_DDK_RECIP_INTERFACE;
246    controlParams.value = value;
247    controlParams.index = index;
248    controlParams.data = buf;
249    controlParams.size = len;
250
251    parmas.interfaceId = USB_CTRL_INTERFACE_ID;
252    parmas.pipeAddress = ecm->ctrPipe->pipeAddress;
253    parmas.pipeId = ecm->ctrPipe->pipeId;
254    parmas.requestType = USB_REQUEST_PARAMS_CTRL_TYPE;
255    parmas.timeout = USB_CTRL_SET_TIMEOUT;
256    parmas.ctrlReq = EcmUsbControlMsg(&controlParams);
257
258    ret = UsbFillRequest(ecm->ctrlReq, ecm->ctrDevHandle, &parmas);
259    if (ret != HDF_SUCCESS) {
260        HDF_LOGE("%{public}s: failed, ret=%{public}d ", __func__, ret);
261        return ret;
262    }
263    ret = UsbSubmitRequestAsync(ecm->ctrlReq);
264    if (ret != HDF_SUCCESS) {
265        HDF_LOGE("UsbRequestSubmitSync failed, ret=%{public}d ", ret);
266        return ret;
267    }
268    if (!ecm->ctrlReq->compInfo.status) {
269        HDF_LOGE("%{public}s  status=%{public}d ", __func__, ecm->ctrlReq->compInfo.status);
270    }
271    return HDF_SUCCESS;
272}
273
274static int32_t EcmRead(struct EcmDevice *ecm, struct HdfSBuf *reply)
275{
276    uint32_t len;
277    int32_t ret = HDF_SUCCESS;
278    uint8_t *buf = NULL;
279    if (ecm == NULL) {
280        HDF_LOGE("%{public}d: invalid parma", __LINE__);
281        return HDF_ERR_INVALID_PARAM;
282    }
283    if (!(ecm->openFlag)) {
284        return HDF_ERR_BAD_FD;
285    }
286
287    for (int32_t i = 0; i < ECM_NR; i++) {
288        if (ecm->readReq[i]->compInfo.status != USB_REQUEST_COMPLETED) {
289            HDF_LOGE("%{public}s:%{public}d i=%{public}d status=%{public}d!",
290                __func__, __LINE__, i, ecm->readReq[i]->compInfo.status);
291            return HDF_FAILURE;
292        }
293    }
294    OsalMutexLock(&ecm->readLock);
295    if (DataFifoIsEmpty(&ecm->readFifo)) {
296        OsalMutexUnlock(&ecm->readLock);
297        return 0;
298    }
299    OsalMutexUnlock(&ecm->readLock);
300    buf = (uint8_t *)OsalMemCalloc(DataFifoLen(&ecm->readFifo) + sizeof(uint32_t));
301    if (buf == NULL) {
302        HDF_LOGE("%{public}s: OsalMemCalloc error", __func__);
303        return HDF_ERR_MALLOC_FAIL;
304    }
305    OsalMutexLock(&ecm->readLock);
306    len = DataFifoRead(&ecm->readFifo, buf, DataFifoLen(&ecm->readFifo));
307    if (len == 0) {
308        HDF_LOGE("%{public}s: no data", __func__);
309        ret = 0;
310        OsalMutexUnlock(&ecm->readLock);
311        goto OUT;
312    }
313    OsalMutexUnlock(&ecm->readLock);
314    bool bufok = HdfSbufWriteBuffer(reply, (const void *)buf, len);
315    if (!bufok) {
316        HDF_LOGE("EcmRead HdfSbufWriteBuffer err");
317        ret = HDF_ERR_IO;
318    }
319OUT:
320    OsalMemFree(buf);
321    return ret;
322}
323
324static int32_t EcmOpen(struct EcmDevice *ecm, struct HdfSBuf *data)
325{
326    int32_t ret;
327    int32_t cmdType = HOST_ECM_ADD_INTERFACE;
328
329    if ((ecm == NULL) || (data == NULL)) {
330        HDF_LOGE("%{public}s: invalid parma", __func__);
331        return HDF_ERR_INVALID_PARAM;
332    }
333
334    if (!HdfSbufReadInt32(data, &cmdType)) {
335        HDF_LOGE("%{public}s:%{public}d sbuf read cmdType failed", __func__, __LINE__);
336        return HDF_ERR_INVALID_PARAM;
337    }
338
339    ret = EcmInit(ecm);
340    if (ret != HDF_SUCCESS) {
341        HDF_LOGE("%{public}s:%{public}d EcmInit failed", __func__, __LINE__);
342        return HDF_FAILURE;
343    }
344
345    if ((cmdType == HOST_ECM_ADD_INTERFACE) || (cmdType == HOST_ECM_REMOVE_INTERFACE)) {
346        HDF_LOGD("%{public}s:%{public}d add or remove interface success", __func__, __LINE__);
347        return HDF_SUCCESS;
348    }
349
350    ret = EcmAllocFifo(&ecm->readFifo, READ_BUF_SIZE);
351    if (ret != HDF_SUCCESS) {
352        HDF_LOGE("%{public}s: UsbSerialAllocFifo failed", __func__);
353        return HDF_ERR_INVALID_PARAM;
354    }
355    ecm->openFlag = true;
356    ecm->readReqNum = 0;
357    ecm->writeReqNum = 0;
358    EcmAllocWriteReq(ecm);
359    EcmAllocReadReq(ecm);
360    for (int32_t i = 0; i < ECM_NR; i++) {
361        ret = UsbSubmitRequestAsync(ecm->readReq[i]);
362        if (ret != HDF_SUCCESS) {
363            HDF_LOGE("UsbSubmitRequestAsync failed, ret=%{public}d ", ret);
364            goto ERR;
365        } else {
366            ecm->readReqNum++;
367        }
368    }
369    return HDF_SUCCESS;
370ERR:
371    EcmFreeFifo(&ecm->readFifo);
372    return ret;
373}
374
375static void EcmClostRelease(struct EcmDevice *ecm)
376{
377    int32_t ret;
378    int32_t cnt = 0;
379    const int32_t temp = 20;
380
381    if (!(ecm->openFlag)) {
382        HDF_LOGE("%{public}s:%{public}d: openFlag is false", __func__, __LINE__);
383        return;
384    }
385
386    for (int32_t i = 0; i < ECM_NR; i++) {
387        ret = UsbCancelRequest(ecm->readReq[i]);
388        if (ret != HDF_SUCCESS) {
389            HDF_LOGE("UsbCancelRequest rd failed, ret=%{public}d ", ret);
390        }
391    }
392    for (int32_t i = 0; i < ECM_NW; i++) {
393        struct EcmWb *snd = &(ecm->wb[i]);
394        ret = UsbCancelRequest(snd->request);
395        if (ret != HDF_SUCCESS) {
396            HDF_LOGE("UsbCancelRequest wr failed, ret=%{public}d ", ret);
397        }
398    }
399
400    while ((cnt < temp) && ((ecm->readReqNum != 0) || (ecm->writeReqNum != 0))) {
401        cnt++;
402    }
403
404    EcmFreeWriteReq(ecm);
405    EcmFreeReadReq(ecm);
406    EcmFreeFifo(&ecm->readFifo);
407    ecm->openFlag = false;
408}
409
410static int32_t EcmClose(struct EcmDevice *ecm, struct HdfSBuf *data)
411{
412    int32_t cmdType = HOST_ECM_REMOVE_INTERFACE;
413
414    if ((ecm == NULL) || (data == NULL)) {
415        HDF_LOGE("%{public}s: invalid parma", __func__);
416        return HDF_ERR_INVALID_PARAM;
417    }
418
419    if (!HdfSbufReadInt32(data, &cmdType)) {
420        HDF_LOGE("%{public}s:%{public}d sbuf read cmdType failed", __func__, __LINE__);
421        return HDF_ERR_INVALID_PARAM;
422    }
423
424    if ((cmdType == HOST_ECM_ADD_INTERFACE) || (cmdType == HOST_ECM_REMOVE_INTERFACE)) {
425        HDF_LOGD("%{public}s:%{public}d cmdType=%{public}d success", __func__, __LINE__, cmdType);
426        return HDF_SUCCESS;
427    }
428
429    EcmClostRelease(ecm);
430    EcmRelease(ecm);
431    return HDF_SUCCESS;
432}
433
434static int32_t EcmWrite(struct EcmDevice *ecm, struct HdfSBuf *data)
435{
436    uint32_t size;
437    uint32_t totalSize = 0;
438    int32_t ret;
439    uint8_t *tmp = NULL;
440    int32_t wbn;
441    uint32_t len;
442    struct EcmWb *wb = NULL;
443
444    if (ecm == NULL || ecm->openFlag == false) {
445        return HDF_ERR_BAD_FD;
446    }
447    if (!HdfSbufReadBuffer(data, (const void **)&tmp, &totalSize)) {
448        return HDF_ERR_IO;
449    }
450    size = totalSize;
451    while (size != 0) {
452        if (EcmWbIsAvail(ecm)) {
453            wbn = EcmWbAlloc(ecm);
454        } else {
455            return (int32_t)size;
456        }
457        if (wbn < ECM_NW && wbn >= 0) {
458            wb = &ecm->wb[wbn];
459        }
460        if (wb == NULL) {
461            return HDF_ERR_INVALID_PARAM;
462        }
463        if (size > ecm->writeSize) {
464            len = ecm->writeSize;
465            size -= ecm->writeSize;
466        } else {
467            len = size;
468            size = 0;
469        }
470        if (wb->buf) {
471            ret = memcpy_s(wb->buf, ecm->writeSize, tmp, len);
472            if (ret != EOK) {
473                return (int32_t)size;
474            }
475            tmp += len;
476            wb->len = (int)len;
477            wb->ecm = ecm;
478            ret = EcmStartWb(ecm, wb);
479            if (ret != HDF_SUCCESS) {
480                return HDF_FAILURE;
481            }
482        }
483    }
484    return totalSize;
485}
486
487static int32_t EcmGetMac(struct EcmDevice *ecm, struct HdfSBuf *reply)
488{
489    (void)ecm;
490    (void)reply;
491    return HDF_SUCCESS;
492}
493
494static int32_t EcmAddOrRemoveInterface(int32_t cmd, struct EcmDevice *ecm, struct HdfSBuf *data)
495{
496    UsbInterfaceStatus status = USB_INTERFACE_STATUS_NORMAL;
497    uint32_t index = 0;
498    if (ecm == NULL) {
499        HDF_LOGE("%{public}d: invalid param", __LINE__);
500        return HDF_ERR_INVALID_PARAM;
501    }
502
503    if (!HdfSbufReadUint32(data, &index)) {
504        HDF_LOGE("%{public}s:%{public}d sbuf read interfaceNum failed", __func__, __LINE__);
505        return HDF_ERR_INVALID_PARAM;
506    }
507
508    if (cmd == CMD_ECM_ADD_INTERFACE) {
509        status = USB_INTERFACE_STATUS_ADD;
510    } else if (cmd == CMD_ECM_REMOVE_INTERFACE) {
511        status = USB_INTERFACE_STATUS_REMOVE;
512    } else {
513        HDF_LOGE("%{public}s:%{public}d cmd=%{public}d is not define", __func__, __LINE__, cmd);
514        return HDF_ERR_INVALID_PARAM;
515    }
516
517    return UsbAddOrRemoveInterface(ecm->session, ecm->busNum, ecm->devAddr, index, status);
518}
519
520static int32_t EcmDeviceDispatch(
521    struct HdfDeviceIoClient *client, int32_t cmd, struct HdfSBuf *data, struct HdfSBuf *reply)
522{
523    struct EcmDevice *ecm = NULL;
524    if (client == NULL) {
525        HDF_LOGE("%{public}s: client is null", __func__);
526        return HDF_ERR_INVALID_OBJECT;
527    }
528
529    if (client->device == NULL) {
530        HDF_LOGE("%{public}s: client->device is null", __func__);
531        return HDF_ERR_INVALID_OBJECT;
532    }
533
534    if (client->device->service == NULL) {
535        HDF_LOGE("%{public}s: client->device->service is null", __func__);
536        return HDF_ERR_INVALID_OBJECT;
537    }
538
539    if (g_ecmReleaseFlag) {
540        HDF_LOGE("%{public}s:%{public}d g_ecmReleaseFlag is true", __func__, __LINE__);
541        return HDF_ERR_DEVICE_BUSY;
542    }
543
544    ecm = (struct EcmDevice *)client->device->service;
545
546    switch (cmd) {
547        case CMD_ECM_OPEN:
548            return EcmOpen(ecm, data);
549        case CMD_ECM_CLOSE:
550            return EcmClose(ecm, data);
551        case CMD_ECM_READ:
552            return EcmRead(ecm, reply);
553        case CMD_ECM_WRITE:
554            return EcmWrite(ecm, data);
555        case CMD_ECM_GET_MAC:
556            return EcmGetMac(ecm, reply);
557        case CMD_ECM_ADD_INTERFACE:
558        case CMD_ECM_REMOVE_INTERFACE:
559            return EcmAddOrRemoveInterface(cmd, ecm, data);
560        default:
561            return HDF_ERR_NOT_SUPPORT;
562    }
563
564    return HDF_SUCCESS;
565}
566
567static struct UsbInterface *EcmGetUsbInterfaceById(struct EcmDevice *ecm, uint8_t interfaceIndex)
568{
569    return UsbClaimInterface(ecm->session, ecm->busNum, ecm->devAddr, interfaceIndex);
570}
571
572static void EcmFreePipes(struct EcmDevice *ecm)
573{
574    if (ecm == NULL) {
575        return;
576    }
577    if (ecm->ctrPipe) {
578        OsalMemFree(ecm->ctrPipe);
579        ecm->ctrPipe = NULL;
580    }
581    if (ecm->intPipe) {
582        OsalMemFree(ecm->intPipe);
583        ecm->intPipe = NULL;
584    }
585    if (ecm->dataInPipe) {
586        OsalMemFree(ecm->dataInPipe);
587        ecm->dataInPipe = NULL;
588    }
589    if (ecm->dataOutPipe) {
590        OsalMemFree(ecm->dataOutPipe);
591        ecm->dataOutPipe = NULL;
592    }
593}
594
595static struct UsbPipeInfo *EcmEnumePipe(
596    struct EcmDevice *ecm, uint8_t interfaceIndex, UsbPipeType pipeType, UsbPipeDirection pipeDirection)
597{
598    struct UsbInterfaceInfo *info = NULL;
599    UsbInterfaceHandle *interfaceHandle = NULL;
600    if (pipeType == USB_PIPE_TYPE_CONTROL) {
601        info = &ecm->ctrIface->info;
602        interfaceHandle = ecm->ctrDevHandle;
603    } else {
604        info = &ecm->iface[interfaceIndex]->info;
605        interfaceHandle = ecm->devHandle[interfaceIndex];
606    }
607
608    for (uint8_t i = 0; i <= info->pipeNum; i++) {
609        struct UsbPipeInfo p;
610        int32_t ret = UsbGetPipeInfo(interfaceHandle, info->curAltSetting, i, &p);
611        if (ret < HDF_SUCCESS) {
612            continue;
613        }
614        if ((p.pipeDirection == pipeDirection) && (p.pipeType == pipeType)) {
615            struct UsbPipeInfo *pi = OsalMemCalloc(sizeof(*pi));
616            if (pi == NULL) {
617                HDF_LOGE("%{public}s: Alloc pipe failed", __func__);
618                return NULL;
619            }
620            p.interfaceId = info->interfaceIndex;
621            *pi = p;
622            return pi;
623        }
624    }
625    return NULL;
626}
627
628static struct UsbPipeInfo *EcmGetPipe(struct EcmDevice *ecm, UsbPipeType pipeType, UsbPipeDirection pipeDirection)
629{
630    uint8_t i;
631    if (ecm == NULL) {
632        HDF_LOGE("%{public}s: invalid parmas", __func__);
633        return NULL;
634    }
635    for (i = 0; i < ecm->interfaceCnt; i++) {
636        struct UsbPipeInfo *p = NULL;
637        if (ecm->iface[i] == NULL) {
638            continue;
639        }
640        p = EcmEnumePipe(ecm, i, pipeType, pipeDirection);
641        if (p == NULL) {
642            continue;
643        }
644        return p;
645    }
646    return NULL;
647}
648
649static int32_t EcmGetPipes(struct EcmDevice *ecm)
650{
651    ecm->dataInPipe = EcmGetPipe(ecm, USB_PIPE_TYPE_BULK, USB_PIPE_DIRECTION_IN);
652    if (ecm->dataInPipe == NULL) {
653        HDF_LOGE("dataInPipe is NULL");
654        goto ERROR;
655    }
656    ecm->dataOutPipe = EcmGetPipe(ecm, USB_PIPE_TYPE_BULK, USB_PIPE_DIRECTION_OUT);
657    if (ecm->dataOutPipe == NULL) {
658        HDF_LOGE("dataOutPipe is NULL");
659        goto ERROR;
660    }
661    ecm->ctrPipe = EcmEnumePipe(ecm, ecm->ctrIface->info.interfaceIndex, USB_PIPE_TYPE_CONTROL, USB_PIPE_DIRECTION_OUT);
662    if (ecm->ctrPipe == NULL) {
663        HDF_LOGE("ctrPipe is NULL");
664        goto ERROR;
665    }
666    ecm->intPipe = EcmGetPipe(ecm, USB_PIPE_TYPE_INTERRUPT, USB_PIPE_DIRECTION_IN);
667    if (ecm->intPipe == NULL) {
668        HDF_LOGE("intPipe is NULL");
669        goto ERROR;
670    }
671
672    ecm->readSize = ecm->dataInPipe->maxPacketSize;
673    ecm->writeSize = ecm->dataOutPipe->maxPacketSize;
674    ecm->ctrlSize = ecm->ctrPipe->maxPacketSize;
675    ecm->intSize = ecm->intPipe->maxPacketSize;
676
677    return HDF_SUCCESS;
678
679ERROR:
680    EcmFreePipes(ecm);
681    return HDF_FAILURE;
682}
683
684static int32_t EcmDriverBind(struct HdfDeviceObject *device)
685{
686    struct UsbPnpNotifyServiceInfo *info = NULL;
687    errno_t err;
688    struct EcmDevice *ecm = NULL;
689    if (device == NULL) {
690        HDF_LOGE("%{public}s: device is null", __func__);
691        return HDF_ERR_INVALID_OBJECT;
692    }
693    ecm = (struct EcmDevice *)OsalMemCalloc(sizeof(*ecm));
694    if (ecm == NULL) {
695        HDF_LOGE("%{public}s: Alloc usb serial device failed", __func__);
696        return HDF_FAILURE;
697    }
698
699    info = (struct UsbPnpNotifyServiceInfo *)device->priv;
700    if (info != NULL) {
701        HDF_LOGD("bus:%{public}d+dev:%{public}d", info->busNum, info->devNum);
702        HDF_LOGD("interfaceLength:%{public}d", info->interfaceLength);
703        ecm->busNum = (uint8_t)info->busNum;
704        ecm->devAddr = (uint8_t)info->devNum;
705        ecm->interfaceCnt = info->interfaceLength;
706        err = memcpy_s((void *)(ecm->interfaceIndex), USB_MAX_INTERFACES, (const void *)info->interfaceNumber,
707            info->interfaceLength);
708        if (err != EOK) {
709            HDF_LOGE("%{public}s:%{public}d memcpy_s failed err=%{public}d", __func__, __LINE__, err);
710            goto ERROR;
711        }
712    } else {
713        HDF_LOGE("%{public}s:%{public}d info is NULL!", __func__, __LINE__);
714        goto ERROR;
715    }
716
717    ecm->device = device;
718    device->service = &(ecm->service);
719    ecm->device->service->Dispatch = EcmDeviceDispatch;
720    HDF_LOGD("EcmDriverBind=========================OK");
721    return HDF_SUCCESS;
722
723ERROR:
724    OsalMemFree(ecm);
725    ecm = NULL;
726    return HDF_FAILURE;
727}
728
729static void EcmProcessNotification(struct EcmDevice *ecm, unsigned char *buf)
730{
731    (void)ecm;
732    struct UsbCdcNotification *dr = (struct UsbCdcNotification *)buf;
733    switch (dr->bNotificationType) {
734        case USB_DDK_CDC_NOTIFY_NETWORK_CONNECTION:
735            HDF_LOGE("%{public}s - network connection: %{public}s", __func__, (dr->wValue ? "on" : "off"));
736            break;
737        case USB_DDK_CDC_NOTIFY_SPEED_CHANGE:
738            HDF_LOGE("%{public}s - speed change wLength: %{public}d", __func__, dr->wLength);
739            break;
740        default:
741            HDF_LOGE("%{public}s-%{public}d received: index %{public}d len %{public}d",
742                __func__, dr->bNotificationType, dr->wIndex, dr->wLength);
743    }
744    return;
745}
746
747static void EcmNotificationAndRequest(struct UsbRequest *req, struct EcmDevice *ecm, struct UsbCdcNotification *dr,
748    unsigned int currentSize, uint32_t expectedSize)
749{
750    if (currentSize >= expectedSize) {
751        EcmProcessNotification(ecm, (unsigned char *)dr);
752        ecm->nbIndex = 0;
753    }
754
755    if ((UsbSubmitRequestAsync(req) != HDF_SUCCESS) && (UsbSubmitRequestAsync(req) != -EPERM)) {
756        HDF_LOGE("%{public}s: usb_submit_urb failed", __func__);
757    }
758}
759
760static void EcmCtrlIrq(struct UsbRequest *req)
761{
762    struct EcmDevice *ecm = (struct EcmDevice *)req->compInfo.userData;
763    int32_t status = (int32_t)req->compInfo.status;
764    struct UsbCdcNotification *dr = (struct UsbCdcNotification *)req->compInfo.buffer;
765    unsigned int currentSize = req->compInfo.actualLength;
766    switch (status) {
767        case 0:
768            break;
769        case -ECONNRESET:
770        case -ENOENT:
771        case -ESHUTDOWN:
772            return;
773        default:
774            goto EXIT;
775    }
776    if (ecm->nbIndex) {
777        dr = (struct UsbCdcNotification *)ecm->notificationBuffer;
778    }
779    uint32_t expectedSize = sizeof(struct UsbCdcNotification) + LE16_TO_CPU(dr->wLength);
780    if (currentSize < expectedSize) {
781        if (ecm->nbSize < expectedSize) {
782            if (ecm->nbSize) {
783                OsalMemFree(ecm->notificationBuffer);
784                ecm->nbSize = 0;
785            }
786            uint32_t allocSize = expectedSize;
787            ecm->notificationBuffer = OsalMemCalloc(allocSize);
788            if (!ecm->notificationBuffer) {
789                goto EXIT;
790            }
791            ecm->nbSize = allocSize;
792        }
793        uint32_t copySize = MIN(currentSize, expectedSize - ecm->nbIndex);
794        int32_t ret = memcpy_s(
795            &ecm->notificationBuffer[ecm->nbIndex], ecm->nbSize - ecm->nbIndex, req->compInfo.buffer, copySize);
796        if (ret != EOK) {
797            HDF_LOGE("%{public}s: memcpy_s failed", __func__);
798            OsalMemFree(ecm->notificationBuffer);
799            ecm->notificationBuffer = NULL;
800            return;
801        }
802        ecm->nbIndex += copySize;
803        currentSize = ecm->nbIndex;
804    }
805    EcmNotificationAndRequest(req, ecm, dr, currentSize, expectedSize);
806EXIT:
807    HDF_LOGE("%{public}s: exit", __func__);
808}
809
810static void EcmReadBulk(struct UsbRequest *req)
811{
812    int32_t status = req->compInfo.status;
813    size_t size = req->compInfo.actualLength;
814    struct EcmDevice *ecm = (struct EcmDevice *)req->compInfo.userData;
815    ecm->readReqNum--;
816    switch (status) {
817        case USB_REQUEST_COMPLETED:
818            OsalMutexLock(&ecm->readLock);
819            if (size) {
820                uint8_t *data = req->compInfo.buffer;
821                if (DataFifoIsFull(&ecm->readFifo)) {
822                    HDF_LOGD("%{public}s:%{public}d fifo is full", __func__, __LINE__);
823                    DataFifoSkip(&ecm->readFifo, size);
824                }
825                uint32_t count = DataFifoWrite(&ecm->readFifo, data, size);
826                if (count != size) {
827                    HDF_LOGW("%{public}s: write %{public}u less than expected %{public}zu", __func__, count, size);
828                }
829            }
830            OsalMutexUnlock(&ecm->readLock);
831            break;
832        default:
833            HDF_LOGE("%{public}s:%{public}d status=%{public}d", __func__, __LINE__, status);
834            return;
835    }
836
837    if (ecm->openFlag) {
838        int32_t retval = UsbSubmitRequestAsync(req);
839        if (retval && retval != -EPERM) {
840            HDF_LOGE("%{public}s - usb_submit_urb failed: %{public}d", __func__, retval);
841        } else {
842            ecm->readReqNum++;
843        }
844    }
845}
846
847static void EcmAllocWriteReq(struct EcmDevice * const ecm)
848{
849    if (EcmWriteBufAlloc(ecm) < 0) {
850        HDF_LOGE("EcmAllocWriteReq buf alloc failed");
851        return;
852    }
853
854    for (int32_t i = 0; i < ECM_NW; i++) {
855        struct EcmWb *snd = &(ecm->wb[i]);
856        snd->request = UsbAllocRequest(InterfaceIdToHandle(ecm, ecm->dataOutPipe->interfaceId), 0, ecm->writeSize);
857        snd->instance = ecm;
858        snd->use = 0;
859        if (snd->request == NULL) {
860            HDF_LOGE("snd request fail");
861            goto ERR;
862        }
863    }
864    return;
865ERR:
866    EcmWriteBufFree(ecm);
867    return;
868}
869
870static void EcmFreeWriteReq(struct EcmDevice * const ecm)
871{
872    OsalMutexLock(&ecm->writeLock);
873    for (int32_t i = 0; i < ECM_NW; i++) {
874        struct EcmWb *snd = &(ecm->wb[i]);
875        snd->use = 0;
876        UsbFreeRequest(snd->request);
877    }
878    OsalMutexUnlock(&ecm->writeLock);
879    EcmWriteBufFree(ecm);
880}
881
882static int32_t EcmAllocIntReq(struct EcmDevice *ecm)
883{
884    int32_t ret;
885    struct UsbRequestParams intParmas = {};
886    if (ecm == NULL || ecm->intPipe == NULL) {
887        return HDF_FAILURE;
888    }
889    ecm->notifyReq = UsbAllocRequest(InterfaceIdToHandle(ecm, ecm->intPipe->interfaceId), 0, ecm->intSize);
890    if (!ecm->notifyReq) {
891        HDF_LOGE("notifyReq request fail");
892        return HDF_ERR_MALLOC_FAIL;
893    }
894    intParmas.userData = (void *)ecm;
895    intParmas.pipeAddress = ecm->intPipe->pipeAddress;
896    intParmas.pipeId = ecm->intPipe->pipeId;
897    intParmas.interfaceId = ecm->intPipe->interfaceId;
898    intParmas.callback = EcmCtrlIrq;
899    intParmas.requestType = USB_REQUEST_PARAMS_DATA_TYPE;
900    intParmas.timeout = USB_CTRL_SET_TIMEOUT;
901    intParmas.dataReq.numIsoPackets = 0;
902    intParmas.dataReq.directon = (((uint32_t)(ecm->intPipe->pipeDirection)) >> USB_DIR_OFFSET) & 0x1;
903    intParmas.dataReq.length = ecm->intSize;
904    ret = UsbFillRequest(ecm->notifyReq, InterfaceIdToHandle(ecm, ecm->intPipe->interfaceId), &intParmas);
905    if (ret != HDF_SUCCESS) {
906        HDF_LOGE("%{public}s: UsbFillRequest failed, ret=%{public}d ", __func__, ret);
907        return ret;
908    }
909    return HDF_SUCCESS;
910}
911
912static void EcmAllocReadReq(struct EcmDevice *ecm)
913{
914    struct UsbRequestParams readParmas = {};
915    for (int32_t i = 0; i < ECM_NR; i++) {
916        ecm->readReq[i] = UsbAllocRequest(InterfaceIdToHandle(ecm, ecm->dataInPipe->interfaceId), 0, ecm->readSize);
917        if (!ecm->readReq[i]) {
918            HDF_LOGE("readReq request failed");
919            return;
920        }
921        readParmas.userData = (void *)ecm;
922        readParmas.pipeAddress = ecm->dataInPipe->pipeAddress;
923        readParmas.pipeId = ecm->dataInPipe->pipeId;
924        readParmas.interfaceId = ecm->dataInPipe->interfaceId;
925        readParmas.callback = EcmReadBulk;
926        readParmas.requestType = USB_REQUEST_PARAMS_DATA_TYPE;
927        readParmas.timeout = USB_CTRL_SET_TIMEOUT;
928        readParmas.dataReq.numIsoPackets = 0;
929        readParmas.dataReq.directon = (((uint32_t)(ecm->dataInPipe->pipeDirection)) >> USB_DIR_OFFSET) & 0x1;
930        readParmas.dataReq.length = ecm->readSize;
931        int32_t ret =
932            UsbFillRequest(ecm->readReq[i], InterfaceIdToHandle(ecm, ecm->dataInPipe->interfaceId), &readParmas);
933        if (ret != HDF_SUCCESS) {
934            HDF_LOGE("%{public}s: UsbFillRequest failed, ret=%{public}d ", __func__, ret);
935            return;
936        }
937    }
938}
939
940static void EcmFreeReadReq(struct EcmDevice *ecm)
941{
942    int32_t ret;
943    for (int32_t i = 0; i < ECM_NR; i++) {
944        ret = UsbFreeRequest(ecm->readReq[i]);
945        if (ret) {
946            goto ERR;
947        }
948    }
949ERR:
950    return;
951}
952
953static void UsbFreeNotifyReqeust(struct EcmDevice *ecm)
954{
955    int32_t ret;
956
957    if ((ecm == NULL) || (ecm->notifyReq == NULL)) {
958        HDF_LOGE("%{public}s: ecm or notifyReq is NULL", __func__);
959        return;
960    }
961    ret = UsbCancelRequest(ecm->notifyReq);
962    if (ret != HDF_SUCCESS) {
963        HDF_LOGE("UsbCancelRequest rd failed, ret=%{public}d ", ret);
964    }
965    ret = UsbFreeRequest(ecm->notifyReq);
966    if (ret == HDF_SUCCESS) {
967        ecm->notifyReq = NULL;
968    } else {
969        HDF_LOGE("%{public}s: UsbFreeNotifyReqeust failed, ret=%{public}d", __func__, ret);
970    }
971}
972
973static void EcmReleaseInterfaces(struct EcmDevice *ecm)
974{
975    for (uint8_t i = 0; i < ecm->interfaceCnt; i++) {
976        if (ecm->iface[i]) {
977            UsbReleaseInterface(ecm->iface[i]);
978            ecm->iface[i] = NULL;
979        }
980    }
981    if (ecm->ctrIface) {
982        UsbReleaseInterface(ecm->ctrIface);
983        ecm->ctrIface = NULL;
984    }
985}
986
987static int32_t EcmClaimInterfaces(struct EcmDevice *ecm)
988{
989    for (uint8_t i = 0; i < ecm->interfaceCnt; i++) {
990        ecm->iface[i] = EcmGetUsbInterfaceById(ecm, ecm->interfaceIndex[i]);
991        if (ecm->iface[i] == NULL) {
992            HDF_LOGE("interface%{public}d is null", ecm->interfaceIndex[i]);
993            goto ERROR;
994        }
995    }
996
997    ecm->ctrIface = EcmGetUsbInterfaceById(ecm, USB_CTRL_INTERFACE_ID);
998    if (ecm->ctrIface == NULL) {
999        HDF_LOGE("%{public}d: UsbClaimInterface null", __LINE__);
1000        goto ERROR;
1001    }
1002
1003    return HDF_SUCCESS;
1004
1005ERROR:
1006    EcmReleaseInterfaces(ecm);
1007    return HDF_FAILURE;
1008}
1009
1010static void EcmCloseInterfaces(struct EcmDevice *ecm)
1011{
1012    for (uint8_t i = 0; i < ecm->interfaceCnt; i++) {
1013        if (ecm->devHandle[i]) {
1014            UsbCloseInterface(ecm->devHandle[i], false);
1015            ecm->devHandle[i] = NULL;
1016        }
1017    }
1018
1019    if (ecm->ctrDevHandle) {
1020        UsbCloseInterface(ecm->ctrDevHandle, false);
1021        ecm->ctrDevHandle = NULL;
1022    }
1023}
1024
1025static int32_t EcmOpenInterfaces(struct EcmDevice *ecm)
1026{
1027    for (uint8_t i = 0; i < ecm->interfaceCnt; i++) {
1028        if (ecm->iface[i]) {
1029            ecm->devHandle[i] = UsbOpenInterface(ecm->iface[i]);
1030            if (ecm->devHandle[i] == NULL) {
1031                HDF_LOGE("%{public}s: UsbOpenInterface null", __func__);
1032                goto ERROR;
1033            }
1034        }
1035    }
1036
1037    ecm->ctrDevHandle = UsbOpenInterface(ecm->ctrIface);
1038    if (ecm->ctrDevHandle == NULL) {
1039        HDF_LOGE("%{public}s: ctrDevHandle UsbOpenInterface null", __func__);
1040        goto ERROR;
1041    }
1042
1043    return HDF_SUCCESS;
1044
1045ERROR:
1046    EcmCloseInterfaces(ecm);
1047    return HDF_FAILURE;
1048}
1049
1050static int32_t EcmGetPipesAndRequest(struct EcmDevice *ecm, int32_t ret)
1051{
1052    ret = EcmGetPipes(ecm);
1053    if (ret != HDF_SUCCESS) {
1054        HDF_LOGE("%{public}s: EcmGetPipes failed", __func__);
1055        goto ERROR_GET_PIPES;
1056    }
1057
1058    ret = EcmAllocIntReq(ecm);
1059    if (ret != HDF_SUCCESS) {
1060        HDF_LOGE("%{public}s: EcmAllocIntReq failed", __func__);
1061        goto ERROR_ALLOC_REQ;
1062    }
1063    if (false) {
1064        EcmCtrlMsg(ecm, USB_DDK_CDC_SET_ETHERNET_PACKET_FILTER,
1065            USB_DDK_CDC_PACKET_TYPE_DIRECTED | USB_DDK_CDC_PACKET_TYPE_BROADCAST, NULL, 0);
1066        ret = UsbSubmitRequestAsync(ecm->notifyReq);
1067        if (ret != HDF_SUCCESS) {
1068            return ret;
1069        }
1070    }
1071    ecm->initFlag = true;
1072    return HDF_SUCCESS;
1073ERROR_ALLOC_REQ:
1074    EcmFreePipes(ecm);
1075ERROR_GET_PIPES:
1076    UsbFreeNotifyReqeust(ecm);
1077    return ret;
1078}
1079
1080static int32_t EcmInit(struct EcmDevice *ecm)
1081{
1082    int32_t ret;
1083    const uint8_t altsetting = 1;
1084
1085    if (ecm->initFlag == true) {
1086        HDF_LOGE("%{public}s: initFlag is true", __func__);
1087        return HDF_SUCCESS;
1088    }
1089
1090    if (UsbInitHostSdk(NULL) != HDF_SUCCESS) {
1091        HDF_LOGE("%{public}s: UsbInitHostSdk failed", __func__);
1092        return HDF_ERR_IO;
1093    }
1094    ecm->session = NULL;
1095
1096    ret = EcmClaimInterfaces(ecm);
1097    if (ret != HDF_SUCCESS) {
1098        HDF_LOGE("%{public}s: EcmClaimInterfaces failed", __func__);
1099        goto ERR_CLAIM_INTERFACES;
1100    }
1101
1102    ret = EcmOpenInterfaces(ecm);
1103    if (ret != HDF_SUCCESS) {
1104        HDF_LOGE("%{public}s: EcmOpenInterfaces failed", __func__);
1105        goto ERROR_OPEN_INTERFACES;
1106    }
1107
1108    if (ecm->interfaceCnt > USB_MAX_INTERFACES) {
1109        HDF_LOGE("interfaceCnt invalid : %{public}u", ecm->interfaceCnt);
1110        goto ERROR_OPEN_INTERFACES;
1111    }
1112
1113    ret = UsbSelectInterfaceSetting(
1114        ecm->devHandle[ecm->interfaceCnt - 1], altsetting, &ecm->iface[ecm->interfaceCnt - 1]); // set altsetting
1115    if (ret != HDF_SUCCESS) {
1116        HDF_LOGE("UsbSelectInterfaceSetting fail");
1117        goto ERROR_SELECT_SETTING;
1118    }
1119    EcmGetPipesAndRequest(ecm, ret);
1120ERROR_SELECT_SETTING:
1121    EcmCloseInterfaces(ecm);
1122ERROR_OPEN_INTERFACES:
1123    EcmReleaseInterfaces(ecm);
1124ERR_CLAIM_INTERFACES:
1125    UsbExitHostSdk(ecm->session);
1126    ecm->session = NULL;
1127    return ret;
1128}
1129
1130static void EcmRelease(struct EcmDevice *ecm)
1131{
1132    if (!(ecm->initFlag)) {
1133        HDF_LOGE("%{public}s:%{public}d: initFlag is false", __func__, __LINE__);
1134        return;
1135    }
1136
1137    EcmCloseInterfaces(ecm);
1138    EcmReleaseInterfaces(ecm);
1139    UsbFreeNotifyReqeust(ecm);
1140    EcmFreePipes(ecm);
1141    UsbExitHostSdk(ecm->session);
1142
1143    ecm->initFlag = false;
1144}
1145
1146static int32_t EcmDriverInit(struct HdfDeviceObject *device)
1147{
1148    struct EcmDevice *ecm = NULL;
1149
1150    if (device == NULL) {
1151        HDF_LOGE("%{public}s: device is null", __func__);
1152        return HDF_ERR_INVALID_OBJECT;
1153    }
1154    ecm = (struct EcmDevice *)device->service;
1155    if (ecm == NULL) {
1156        HDF_LOGE("%{public}s: ecm is null", __func__);
1157        return HDF_FAILURE;
1158    }
1159
1160    OsalMutexInit(&ecm->readLock);
1161    OsalMutexInit(&ecm->writeLock);
1162    ecm->openFlag = false;
1163    ecm->initFlag = false;
1164    g_ecmReleaseFlag = false;
1165
1166    HDF_LOGE("%{public}s:%{public}d EcmDriverInit OK", __func__, __LINE__);
1167    return HDF_SUCCESS;
1168}
1169
1170static void EcmDriverRelease(struct HdfDeviceObject *device)
1171{
1172    struct EcmDevice *ecm = NULL;
1173    if (device == NULL) {
1174        HDF_LOGE("%{public}s: device is NULL", __func__);
1175        return;
1176    }
1177    ecm = (struct EcmDevice *)device->service;
1178    if (ecm == NULL) {
1179        HDF_LOGE("%{public}s: ecm is null", __func__);
1180        return;
1181    }
1182
1183    g_ecmReleaseFlag = true;
1184
1185    if (ecm->openFlag) {
1186        HDF_LOGD("%{public}s:%{public}d EcmClostRelease", __func__, __LINE__);
1187        EcmClostRelease(ecm);
1188    }
1189    if (ecm->initFlag) {
1190        HDF_LOGD("%{public}s:%{public}d EcmRelease", __func__, __LINE__);
1191        EcmRelease(ecm);
1192    }
1193    OsalMutexDestroy(&ecm->writeLock);
1194    OsalMutexDestroy(&ecm->readLock);
1195    OsalMemFree(ecm);
1196    ecm = NULL;
1197    HDF_LOGD("%{public}s:%{public}d exit", __func__, __LINE__);
1198}
1199
1200struct HdfDriverEntry g_ecmUsbDriverEntry = {
1201    .moduleVersion = 1,
1202    .moduleName = "usbhost_ecm",
1203    .Bind = EcmDriverBind,
1204    .Init = EcmDriverInit,
1205    .Release = EcmDriverRelease,
1206};
1207
1208HDF_INIT(g_ecmUsbDriverEntry);
1209