1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <ddk/binding.h>
6#include <ddk/debug.h>
7#include <ddk/device.h>
8#include <ddk/driver.h>
9#include <ddk/protocol/serial-impl.h>
10#include <ddk/protocol/usb.h>
11#include <ddk/usb/usb.h>
12#include <zircon/listnode.h>
13#include <zircon/hw/usb.h>
14
15#include <inttypes.h>
16#include <fcntl.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <threads.h>
21#include <unistd.h>
22
23#include "ftdi.h"
24
25#define FTDI_STATUS_SIZE 2
26#define FTDI_RX_HEADER_SIZE 4
27
28#define READ_REQ_COUNT 8
29#define WRITE_REQ_COUNT 4
30#define INTR_REQ_COUNT 4
31#define USB_BUF_SIZE 2048
32#define INTR_REQ_SIZE 4
33
34#define FIFOSIZE 256
35#define FIFOMASK (FIFOSIZE - 1)
36
37typedef struct {
38    zx_device_t* usb_device;
39    zx_device_t* zxdev;
40    usb_protocol_t usb;
41
42    uint16_t ftditype;
43    uint32_t baudrate;
44
45    serial_port_info_t serial_port_info;
46    serial_impl_protocol_t serial;
47
48    serial_notify_cb notify_cb;
49    void* notify_cb_cookie;
50    bool enabled;
51    uint32_t state;
52    // pool of free USB requests
53    list_node_t free_read_reqs;
54    list_node_t free_write_reqs;
55    // list of received packets not yet read by upper layer
56    list_node_t completed_reads;
57    size_t read_offset;
58    mtx_t mutex;
59} ftdi_t;
60
61
62static uint32_t ftdi_check_state(ftdi_t* ftdi) {
63    uint32_t state = 0;
64
65    state |= list_is_empty(&ftdi->free_write_reqs) ? 0 : SERIAL_STATE_WRITABLE;
66
67    state |= list_is_empty(&ftdi->completed_reads) ? 0 : SERIAL_STATE_READABLE;
68
69    if (state != ftdi->state) {
70        ftdi->state = state;
71        if (ftdi->notify_cb) {
72            ftdi->notify_cb(state, ftdi->notify_cb_cookie);
73        }
74    }
75    return state;
76}
77
78static void ftdi_read_complete(usb_request_t* request, void* cookie) {
79    ftdi_t* ftdi = (ftdi_t*)cookie;
80
81    if (request->response.status == ZX_ERR_IO_NOT_PRESENT) {
82        zxlogf(INFO,"FTDI: remote closed\n");
83        usb_req_release(&ftdi->usb, request);
84        return;
85    }
86
87    mtx_lock(&ftdi->mutex);
88    if ((request->response.status == ZX_OK) && (request->response.actual > 2)) {
89        list_add_tail(&ftdi->completed_reads, &request->node);
90        ftdi_check_state(ftdi);
91    } else {
92        usb_request_queue(&ftdi->usb, request);
93    }
94    mtx_unlock(&ftdi->mutex);
95}
96
97static void ftdi_write_complete(usb_request_t* request, void* cookie) {
98    ftdi_t* ftdi = (ftdi_t*)cookie;
99
100    if (request->response.status == ZX_ERR_IO_NOT_PRESENT) {
101        usb_req_release(&ftdi->usb, request);
102        return;
103    }
104    mtx_lock(&ftdi->mutex);
105    list_add_tail(&ftdi->free_write_reqs, &request->node);
106    ftdi_check_state(ftdi);
107    mtx_unlock(&ftdi->mutex);
108}
109
110static zx_status_t ftdi_calc_dividers(uint32_t* baudrate, uint32_t clock,       uint32_t divisor,
111                                                          uint16_t* integer_div, uint16_t* fraction_div) {
112
113    static const uint8_t frac_lookup[8] = {0, 3, 2, 4, 1, 5, 6, 7};
114
115    uint32_t base_clock = clock/divisor;
116
117    // integer dividers of 1 and 0 are special cases.  0=base_clock and 1 = 2/3 of base clock
118    if (*baudrate >=  base_clock) {  // return with max baud rate achievable
119        *fraction_div = 0;
120        *integer_div = 0;
121        *baudrate = base_clock;
122    }
123    else if (*baudrate >=  (base_clock* 2 )/3) {
124        *integer_div = 1;
125        *fraction_div = 0;
126        *baudrate = (base_clock * 2)/3;
127    } else {
128        // create a 28.4 fractional integer
129        uint32_t ratio = (base_clock * 16) / *baudrate;
130        ratio++;    //round up if needed
131        ratio = ratio & 0xfffffffe;
132
133        *baudrate = (base_clock << 4) / ratio;
134        *integer_div = ratio >> 4;
135        *fraction_div = frac_lookup[ (ratio >> 1) & 0x07 ];
136    }
137    return ZX_OK;
138}
139
140static zx_status_t ftdi_write(void *ctx, const void* buf, size_t length, size_t* actual) {
141    ftdi_t* ftdi = ctx;
142    usb_request_t* req = NULL;
143    zx_status_t status = ZX_OK;
144
145    mtx_lock(&ftdi->mutex);
146
147    req = list_remove_head_type(&ftdi->free_write_reqs, usb_request_t, node);
148    if (!req) {
149        status = ZX_ERR_SHOULD_WAIT;
150        *actual = 0;
151        goto out;
152    }
153
154    *actual = usb_req_copy_to(&ftdi->usb, req, buf, length, 0);
155    req->header.length = length;
156
157    usb_request_queue(&ftdi->usb,req);
158    ftdi_check_state(ftdi);
159
160out:
161    mtx_unlock(&ftdi->mutex);
162    return status;
163}
164
165
166static zx_status_t ftdi_read(void* ctx, void* data, size_t len, size_t* actual) {
167    ftdi_t* ftdi = ctx;
168    size_t bytes_copied = 0;
169    size_t offset = ftdi->read_offset;
170    uint8_t* buffer = (uint8_t*)data;
171
172    mtx_lock(&ftdi->mutex);
173
174    usb_request_t* req = list_peek_head_type(&ftdi->completed_reads,
175                                              usb_request_t, node);
176    while ((req) && (bytes_copied < len)) {
177
178        size_t to_copy = req->response.actual - offset - FTDI_STATUS_SIZE;
179
180        if ( (to_copy + bytes_copied) > len) {
181            to_copy = len - bytes_copied;
182        }
183
184        usb_req_copy_from(&ftdi->usb, req, &buffer[bytes_copied],
185                          to_copy, offset + FTDI_STATUS_SIZE);
186
187        bytes_copied = bytes_copied + to_copy;
188
189        if ((to_copy + offset + FTDI_STATUS_SIZE) < req->response.actual) {
190            offset = offset + to_copy;
191            goto out;
192        } else {
193            list_remove_head(&ftdi->completed_reads);
194            // requeue the read request
195            usb_request_queue(&ftdi->usb, req);
196            offset = 0;
197        }
198
199        req = list_peek_head_type(&ftdi->completed_reads, usb_request_t, node);
200    }
201    ftdi_check_state(ftdi);
202
203out:
204    ftdi->read_offset = offset;
205    mtx_unlock(&ftdi->mutex);
206    *actual = bytes_copied;
207    return *actual? ZX_OK : ZX_ERR_SHOULD_WAIT;
208}
209
210
211static zx_status_t ftdi_set_baudrate(ftdi_t* ftdi, uint32_t baudrate){
212    uint16_t whole,fraction,value,index;
213    zx_status_t status;
214
215    if (ftdi == NULL) {
216        return ZX_ERR_INVALID_ARGS;
217    }
218
219    switch(ftdi->ftditype) {
220        case FTDI_TYPE_R:
221        case FTDI_TYPE_2232C:
222        case FTDI_TYPE_BM:
223            ftdi_calc_dividers(&baudrate,FTDI_C_CLK,16,&whole,&fraction);
224            ftdi->baudrate = baudrate;
225            break;
226        default:
227            return ZX_ERR_INVALID_ARGS;
228    }
229    value = (whole & 0x3fff) | (fraction << 14);
230    index = fraction >> 2;
231    status = usb_control(&ftdi->usb, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
232                         FTDI_SIO_SET_BAUDRATE, value, index, NULL, 0,
233                         ZX_TIME_INFINITE,NULL);
234    if (status == ZX_OK) {
235        ftdi->baudrate = baudrate;
236    }
237    return status;
238}
239
240static zx_status_t ftdi_reset(ftdi_t* ftdi) {
241
242    if (ftdi == NULL || ftdi->usb_device == NULL) {
243        return ZX_ERR_INVALID_ARGS;
244    }
245
246    return usb_control(
247            &ftdi->usb,
248            USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
249            FTDI_SIO_RESET_REQUEST,
250            FTDI_SIO_RESET, //value
251            0, //index
252            NULL, 0, //data,length
253            ZX_TIME_INFINITE,
254            NULL);
255}
256
257
258static zx_status_t ftdi_serial_config(void* ctx, uint32_t baud_rate, uint32_t flags) {
259    ftdi_t* ftdi = ctx;
260
261    if (baud_rate != ftdi->baudrate) {
262        return ftdi_set_baudrate(ftdi, baud_rate);
263    }
264
265    return ZX_OK;
266}
267
268
269static zx_status_t ftdi_serial_get_info(void* ctx, serial_port_info_t* info) {
270    ftdi_t* ftdi = ctx;
271    memcpy(info, &ftdi->serial_port_info, sizeof(*info));
272    return ZX_OK;
273}
274
275static zx_status_t ftdi_serial_enable(void* ctx, bool enable) {
276    ftdi_t* ftdi = ctx;
277    ftdi->enabled = enable;
278    return ZX_OK;
279}
280
281static zx_status_t ftdi_set_notify_callback(void* ctx, serial_notify_cb cb, void* cookie) {
282    ftdi_t* ftdi = ctx;
283
284    if (ftdi->enabled) {
285        return ZX_ERR_BAD_STATE;
286    }
287
288    ftdi->notify_cb = cb;
289    ftdi->notify_cb_cookie = cookie;
290
291    mtx_lock(&ftdi->mutex);
292    ftdi_check_state(ftdi);
293    mtx_unlock(&ftdi->mutex);
294
295    return ZX_OK;
296}
297
298static serial_impl_ops_t ftdi_serial_ops = {
299    .get_info = ftdi_serial_get_info,
300    .config = ftdi_serial_config,
301    .enable = ftdi_serial_enable,
302
303    .read = ftdi_read,
304    .write = ftdi_write,
305    .set_notify_callback = ftdi_set_notify_callback,
306};
307
308
309static void ftdi_free(ftdi_t* ftdi) {
310    usb_request_t* req;
311    while ((req = list_remove_head_type(&ftdi->free_read_reqs, usb_request_t, node)) != NULL) {
312        usb_req_release(&ftdi->usb, req);
313    }
314    while ((req = list_remove_head_type(&ftdi->free_write_reqs, usb_request_t, node)) != NULL) {
315        usb_req_release(&ftdi->usb, req);
316    }
317    while ((req = list_remove_head_type(&ftdi->completed_reads, usb_request_t, node)) != NULL) {
318        usb_req_release(&ftdi->usb, req);
319    }
320
321    free(ftdi);
322}
323
324static void ftdi_uart_release(void* ctx) {
325    zxlogf(INFO,"releasing ftdi uart driver\n");
326    ftdi_t* ftdi = ctx;
327    ftdi_free(ftdi);
328}
329
330static void ftdi_unbind(void* ctx) {
331    ftdi_t* ftdi = ctx;
332    device_remove(ftdi->usb_device);
333}
334
335static zx_protocol_device_t ftdi_device_proto = {
336    .version = DEVICE_OPS_VERSION,
337    .unbind = ftdi_unbind,
338    .release = ftdi_uart_release,
339};
340
341static zx_status_t ftdi_bind(void* ctx, zx_device_t* device) {
342
343    usb_protocol_t usb;
344    zx_status_t status = device_get_protocol(device, ZX_PROTOCOL_USB, &usb);
345    if (status != ZX_OK) {
346        return status;
347    }
348
349    // find our endpoints
350    usb_desc_iter_t iter;
351    status = usb_desc_iter_init(&usb, &iter);
352
353    if (status != ZX_OK) {
354        return status;
355    }
356
357    usb_desc_iter_next_interface(&iter, true);
358
359    uint8_t bulk_in_addr = 0;
360    uint8_t bulk_out_addr = 0;
361
362    usb_endpoint_descriptor_t* endp = usb_desc_iter_next_endpoint(&iter);
363    //int idx = 0;
364    while (endp) {
365        if (usb_ep_direction(endp) == USB_ENDPOINT_OUT) {
366            if (usb_ep_type(endp) == USB_ENDPOINT_BULK) {
367                bulk_out_addr = endp->bEndpointAddress;
368            }
369        } else {
370            if (usb_ep_type(endp) == USB_ENDPOINT_BULK) {
371                bulk_in_addr = endp->bEndpointAddress;
372            }
373        }
374        endp = usb_desc_iter_next_endpoint(&iter);
375    }
376
377    usb_desc_iter_release(&iter);
378
379    if (!bulk_in_addr || !bulk_out_addr ) {
380        zxlogf(ERROR,"FTDI: could not find all endpoints\n");
381        return ZX_ERR_NOT_SUPPORTED;
382    }
383
384    ftdi_t* ftdi = calloc(1, sizeof(ftdi_t));
385    if (!ftdi) {
386        zxlogf(ERROR,"FTDI: Not enough memory\n");
387        return ZX_ERR_NO_MEMORY;
388    }
389
390    ftdi->ftditype = FTDI_TYPE_R;
391
392    list_initialize(&ftdi->free_read_reqs);
393    list_initialize(&ftdi->free_write_reqs);
394    list_initialize(&ftdi->completed_reads);
395
396    ftdi->usb_device = device;
397
398    memcpy(&ftdi->usb, &usb, sizeof(ftdi->usb));
399
400    mtx_init(&ftdi->mutex, mtx_plain);
401
402    for (int i = 0; i < READ_REQ_COUNT; i++) {
403        usb_request_t* req;
404        status = usb_req_alloc(&ftdi->usb, &req, USB_BUF_SIZE, bulk_in_addr);
405        if (status != ZX_OK) {
406            goto fail;
407        }
408        req->complete_cb = ftdi_read_complete;
409        req->cookie = ftdi;
410        list_add_head(&ftdi->free_read_reqs, &req->node);
411    }
412    for (int i = 0; i < WRITE_REQ_COUNT; i++) {
413        usb_request_t* req;
414        status = usb_req_alloc(&ftdi->usb, &req, USB_BUF_SIZE, bulk_out_addr);
415        if (status != ZX_OK) {
416            goto fail;
417        }
418        req->complete_cb = ftdi_write_complete;
419        req->cookie = ftdi;
420        list_add_head(&ftdi->free_write_reqs, &req->node);
421    }
422
423    if (ftdi_reset(ftdi) < 0) {
424        zxlogf(ERROR,"FTDI reset failed\n");
425        goto fail;
426    }
427
428    status = ftdi_set_baudrate(ftdi, 115200);
429    if (status != ZX_OK) {
430        zxlogf(ERROR,"FTDI: set baudrate failed\n");
431        goto fail;
432    }
433
434    ftdi->serial_port_info.serial_class = SERIAL_CLASS_GENERIC;
435
436    device_add_args_t args = {
437        .version = DEVICE_ADD_ARGS_VERSION,
438        .name = "ftdi-uart",
439        .ctx = ftdi,
440        .ops = &ftdi_device_proto,
441        .proto_id = ZX_PROTOCOL_SERIAL_IMPL,
442        .proto_ops = &ftdi_serial_ops,
443    };
444
445    status = device_add(ftdi->usb_device, &args, &ftdi->zxdev);
446    if (status != ZX_OK) {
447        zxlogf(ERROR, "ftdi_uart: device_add failed\n");
448        goto fail;
449    }
450
451    //Queue the read requests
452    usb_request_t* req;
453    usb_request_t* prev;
454    list_for_every_entry_safe (&ftdi->free_read_reqs, req, prev, usb_request_t, node) {
455        list_delete(&req->node);
456        usb_request_queue(&ftdi->usb, req);
457    }
458
459    zxlogf(INFO,"ftdi bind successful\n");
460    return status;
461
462fail:
463    zxlogf(ERROR,"ftdi_bind failed: %d\n", status);
464    ftdi_free(ftdi);
465    return status;
466}
467
468static zx_driver_ops_t _ftdi_driver_ops = {
469    .version = DRIVER_OPS_VERSION,
470    .bind = ftdi_bind,
471};
472
473ZIRCON_DRIVER_BEGIN(ftdi, _ftdi_driver_ops, "zircon", "0.1", 3)
474    BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_USB),
475    BI_MATCH_IF(EQ, BIND_USB_VID, FTDI_VID),
476    BI_MATCH_IF(EQ, BIND_USB_PID, FTDI_232R_PID),
477ZIRCON_DRIVER_END(ftdi)
478