#include <u.h>
#include <libc.h>
#include <thread.h>
#include <fcall.h>
#include <9p.h>
#include "usb.h"
#include "serial.h"
enum {
ReadVersion = 0x5F,
ReadReg = 0x95,
WriteReg = 0x9A,
SerialInit = 0xA1,
ModemCtrl = 0xA4,
LcrEnableRx = 0x80,
LcrEnableTx = 0x40,
LcrMarkSpace = 0x20,
LcrParEven = 0x10,
LcrEnablePar = 0x08,
LcrStopBits2 = 0x04,
LcrCS8 = 0x03,
LcrCS7 = 0x02,
LcrCS6 = 0x01,
LcrCS5 = 0x00,
};
static Cinfo chinfo[] = {
{ 0x4348, 0x5523, },
{ 0x1a86, 0x7523, },
{ 0x1a86, 0x5523, },
{ 0, 0, },
};
static Serialops chops;
int
chprobe(Serial *ser)
{
Usbdev *ud = ser->dev->usb;
if(matchid(chinfo, ud->vid, ud->did) == nil)
return -1;
ser->Serialops = chops;
return 0;
}
static int
chin(Serialport *p, uint req, uint val, uint index, void *buf, int len)
{
Serial *ser;
ser = p->s;
return usbcmd(ser->dev, Rd2h | Rvendor | Rdev, req, val, index, buf, len);
}
static int
chout(Serialport *p, uint req, uint val, uint index)
{
Serial *ser;
ser = p->s;
return usbcmd(ser->dev, Rh2d | Rvendor | Rdev, req, val, index, nil, 0);
}
static int
chinit(Serialport *p)
{
Serial *ser;
uchar ver[2];
ser = p->s;
dsprint(2, "chinit\n");
if(chin(p, ReadVersion, 0, 0, ver, sizeof(ver)) < 0)
return -1;
dsprint(2, "ch340: chip version: %ux.%ux\n", ver[0], ver[1]);
if(chout(p, SerialInit, 0, 0) < 0)
return -1;
p->baud = 115200;
p->stop = 1;
p->bits = 8;
chops.setparam(p);
/* p gets freed by closedev, the process has a reference */
incref(ser->dev);
return 0;
}
static int
chsetbaud(Serialport *p)
{
uint factor, divisor, a;
factor = 1532620800 / p->baud;
divisor = 3;
while((factor > 0xfff0) && divisor) {
factor /= 8;
divisor--;
}
if(factor > 0xfff0)
return -1;
factor = 0x10000 - factor;
a = (factor & 0xFF00) | divisor;
a |= 1<<7;
return chout(p, WriteReg, 0x1312, a);
}
static int
chsetparam(Serialport *p)
{
uint lcr;
if(p->baud > 0)
chsetbaud(p);
lcr = LcrEnableRx | LcrEnableTx;
switch(p->bits){
case 5:
lcr |= LcrCS5;
break;
case 6:
lcr |= LcrCS6;
break;
case 7:
lcr |= LcrCS7;
break;
case 8:
default:
lcr |= LcrCS8;
break;
}
switch(p->parity){
case 0:
default:
break;
case 1:
lcr |= LcrEnablePar;
break;
case 2:
lcr |= LcrEnablePar|LcrParEven;
break;
case 3:
lcr |= LcrEnablePar|LcrMarkSpace;
break;
case 4:
lcr |= LcrEnablePar|LcrParEven|LcrMarkSpace;
break;
};
if(p->stop == 2)
lcr |= LcrStopBits2;
return chout(p, WriteReg, 0x2518, lcr);
}
static Serialops chops = {
.init = chinit,
.setparam = chsetparam,
};