blob: 1836070657994964bb9c7fb0822008d0a825f48c [file] [log] [blame]
/*
* Copyright 2017, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the BSD 2-Clause license. Note that NO WARRANTY is provided.
* See "LICENSE_BSD2.txt" for details.
*
* @TAG(DATA61_BSD)
*/
/**
* @brief Prolific PL2303 USB to Serial adaptor
* @see http://www.prolific.com.tw/US/ShowProduct.aspx?p_id=232&pcid=41
*/
#include <stdio.h>
#include <string.h>
#include <usb/drivers/pl2303.h>
#include "../services.h"
#define PL2303_VENDOR_REQ 0x01
#define PL2303_READ_TYPE (USB_DIR_IN | USB_TYPE_VEN | USB_RCPT_DEVICE)
#define PL2303_WRITE_TYPE (USB_DIR_OUT | USB_TYPE_VEN | USB_RCPT_DEVICE)
#define PL2303_SET_LINE_REQ 0x20
#define PL2303_SET_CTRL_REQ 0x22
#define PL2303_SET_REQ_TYPE 0x21
#define PL2303_GET_LINE_REQ 0x21
#define PL2303_GET_REQ_TYEP 0xA1
#define PL2303_CTRL_DTR 0x01
#define PL2303_CTRL_RTS 0x01
/* PL2303 USB to Serial Converter */
struct pl2303_device {
struct usb_dev *udev; //The handle to the underlying USB device
uint8_t config; //Active configuration
struct endpoint *ep_int; //Interrupt endpoint
struct endpoint *ep_in; //BULK in endpoint
struct endpoint *ep_out; //BULK out endpoint
struct xact int_xact; //Interrupt xact
};
static int
pl2303_config_cb(void *token, int cfg, int iface, struct anon_desc *desc)
{
struct pl2303_device *dev;
struct config_desc *cdesc;
if (!desc) {
return 0;
}
dev = (struct pl2303_device*)token;
switch (desc->bDescriptorType) {
case CONFIGURATION:
cdesc = (struct config_desc*)desc;
dev->config = cdesc->bConfigurationValue;
break;
default:
break;
}
return 0;
}
static int
pl2303_interrupt_cb(void* token, enum usb_xact_status stat, int rbytes)
{
int err;
struct pl2303_device *dev;
struct usb_dev *udev;
udev = (struct usb_dev*)token;
dev = (struct pl2303_device*)udev->dev_data;
/* Queue another request */
err = usbdev_schedule_xact(udev, dev->ep_int, &dev->int_xact, 1,
pl2303_interrupt_cb, udev);
if (err) {
ZF_LOGF("Transaction error\n");
}
return err;
}
static void
pl2303_startup_magic(struct usb_dev *udev)
{
int err;
struct xact xact[2];
struct usbreq *req;
if (!udev) {
ZF_LOGF("Invalid device\n");
}
xact[0].len = sizeof(struct usbreq);
xact[1].len = 1;
err = usb_alloc_xact(udev->dman, xact, 2);
if (err) {
ZF_LOGF("Out of DMA memory\n");
}
/* Fill in the request */
xact[0].type = PID_SETUP;
xact[1].type = PID_IN;
req = xact_get_vaddr(&xact[0]);
/*
* Send Prolific private initial data.
*
* It is found by sniffing the official windows driver.
*/
req->bRequest = PL2303_VENDOR_REQ;
#define magic_request(typ, idx, val) \
req->bmRequestType = typ; \
req->wIndex = idx; \
req->wValue = val; \
req->wLength = (typ == PL2303_READ_TYPE) ? 1 : 0; \
err = usbdev_schedule_xact(udev, udev->ep_ctrl, xact, \
req->wLength + 1, NULL, NULL); \
magic_request(PL2303_READ_TYPE, 0, 0x8484);
magic_request(PL2303_WRITE_TYPE, 0, 0x0404);
magic_request(PL2303_READ_TYPE, 0, 0x8484);
magic_request(PL2303_READ_TYPE, 0, 0x8383);
magic_request(PL2303_READ_TYPE, 0, 0x8484);
magic_request(PL2303_WRITE_TYPE, 1, 0x0404);
magic_request(PL2303_READ_TYPE, 0, 0x8484);
magic_request(PL2303_READ_TYPE, 0, 0x8383);
magic_request(PL2303_WRITE_TYPE, 1, 0);
magic_request(PL2303_WRITE_TYPE, 0, 1);
magic_request(PL2303_WRITE_TYPE, 0x44, 2);
usb_destroy_xact(udev->dman, xact, 2);
}
int usb_pl2303_bind(usb_dev_t *udev)
{
int err;
struct pl2303_device *dev;
struct xact xact;
struct usbreq *req;
if (!udev) {
ZF_LOGF("Invalid device\n");
}
dev = usb_malloc(sizeof(struct pl2303_device));
if (!dev) {
ZF_LOGD("Not enough memory!\n");
return -1;
}
dev->udev = udev;
udev->dev_data = (struct udev_priv*)dev;
/* Parse the descriptors */
err = usbdev_parse_config(udev, pl2303_config_cb, dev);
if (err) {
ZF_LOGF("Invalid descriptors\n");
}
/* Find endpoints */
for (int i = 0; udev->ep[i] != NULL; i++) {
if (udev->ep[i]->type == EP_BULK) {
if (udev->ep[i]->dir == EP_DIR_OUT) {
dev->ep_out = udev->ep[i];
} else {
dev->ep_in = udev->ep[i];
}
} else if (udev->ep[i]->type == EP_INTERRUPT) {
dev->ep_int = udev->ep[i];
} else {
continue;
}
}
if (udev->vend_id != 0x067b || udev->prod_id != 0x2303) {
ZF_LOGD("Not a PL2303 device(%u:%u)\n",
udev->vend_id, udev->prod_id);
return -1;
}
ZF_LOGD("Found PL2303 USB to serial converter!\n");
/* Activate configuration */
xact.len = sizeof(struct usbreq);
err = usb_alloc_xact(udev->dman, &xact, 1);
if (err) {
ZF_LOGF("Out of DMA memory\n");
}
/* Fill in the request */
xact.type = PID_SETUP;
req = xact_get_vaddr(&xact);
*req = __set_configuration_req(dev->config);
/* Send the request to the host */
err = usbdev_schedule_xact(udev, udev->ep_ctrl, &xact, 1, NULL, NULL);
if (err) {
ZF_LOGF("Transaction error\n");
}
usb_destroy_xact(udev->dman, &xact, 1);
pl2303_startup_magic(udev);
/* Allocate interrupt xact */
dev->int_xact.type = PID_IN;
dev->int_xact.len = dev->ep_int->max_pkt;
err = usb_alloc_xact(udev->dman, &dev->int_xact, 1);
if (err) {
ZF_LOGF("Out of DMA memory\n");
}
/* Schedule a interrupt request */
err = usbdev_schedule_xact(udev, dev->ep_int, &dev->int_xact, 1,
pl2303_interrupt_cb, udev);
if (err) {
ZF_LOGF("Transaction error\n");
}
return 0;
}
int usb_pl2303_configure(usb_dev_t *udev, uint32_t bps, uint8_t char_size,
enum serial_parity parity, uint8_t stop)
{
int err;
struct xact xact[2];
struct usbreq *req;
char *buf;
xact[0].len = sizeof(struct usbreq);
xact[1].len = 7;
err = usb_alloc_xact(udev->dman, xact, 2);
if (err) {
ZF_LOGF("Out of DMA memory\n");
}
xact[0].type = PID_SETUP;
xact[1].type = PID_OUT;
req = xact_get_vaddr(&xact[0]);
buf = xact_get_vaddr(&xact[1]);
/* Data bits */
buf[6] = char_size;
/*
* Set parity
* 0 -- none, 1 -- odd, 2-- even, 3 -- mark, 4 -- space
*/
switch (parity) {
case PARITY_NONE:
buf[5] = 0;
break;
case PARITY_ODD:
buf[5] = 1;
break;
case PARITY_EVEN:
buf[5] = 2;
break;
default:
ZF_LOGD("Unsupported parity!\n");
break;
}
/* 0 -- 1 stop bits, 1 -- 1.5 stop bits, 2 -- 2 stop bits */
buf[4] = stop;
/* Set baudrate */
buf[3] = bps & 0xFF;
buf[2] = (bps >> 16) & 0xFF;
buf[1] = (bps >> 8) & 0xFF;
buf[0] = bps & 0xFF000000;
/* Send configuration */
req->bmRequestType = PL2303_SET_REQ_TYPE;
req->bRequest = PL2303_SET_LINE_REQ;
req->wIndex = 0;
req->wLength = 7;
req->wValue = 0;
err = usbdev_schedule_xact(udev, udev->ep_ctrl, xact, 2, NULL, NULL);
if (err) {
ZF_LOGF("Transaction error\n");
}
/* Activate device */
req->bmRequestType = PL2303_SET_REQ_TYPE;
req->bRequest = PL2303_SET_CTRL_REQ;
req->wIndex = 0;
req->wLength = 0;
req->wValue = PL2303_CTRL_DTR | PL2303_CTRL_RTS;
err = usbdev_schedule_xact(udev, udev->ep_ctrl, xact, 1, NULL, NULL);
if (err) {
ZF_LOGF("Transaction error\n");
}
usb_destroy_xact(udev->dman, xact, 2);
return 0;
}
/* TODO: Remove the 20K limitation */
int usb_pl2303_write(usb_dev_t *udev, void *buf, int len)
{
int err;
struct pl2303_device *dev;
struct xact xact;
dev = (struct pl2303_device*)udev->dev_data;
xact.type = PID_OUT;
xact.len = len;
err = usb_alloc_xact(udev->dman, &xact, 1);
if (err) {
ZF_LOGF("Out of DMA memory\n");
}
memcpy(xact_get_vaddr(&xact), buf, len);
err = usbdev_schedule_xact(udev, dev->ep_out, &xact, 1, NULL, NULL);
if (err) {
ZF_LOGF("Transaction error\n");
}
usb_destroy_xact(udev->dman, &xact, 1);
return len - err;
}
/* TODO: Remove the 20K limitation and possible babble */
int usb_pl2303_read(usb_dev_t *udev, void *buf, int len)
{
int err;
struct pl2303_device *dev;
struct xact xact;
dev = (struct pl2303_device*)udev->dev_data;
xact.type = PID_IN;
xact.len = len;
err = usb_alloc_xact(udev->dman, &xact, 1);
if (err) {
ZF_LOGF("Out of DMA memory\n");
}
err = usbdev_schedule_xact(udev, dev->ep_in, &xact, 1, NULL, NULL);
if (err) {
ZF_LOGF("Transaction error\n");
}
memcpy(buf, xact_get_vaddr(&xact), len);
usb_destroy_xact(udev->dman, &xact, 1);
return len - err;
}