nusb/serial: add support for CH340 serial converter

This commit is contained in:
cinap_lenrek 2018-10-21 17:24:33 +02:00
parent 21001d4294
commit 8dfff00e50
3 changed files with 176 additions and 3 deletions

View file

@ -0,0 +1,171 @@
#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,
};

View file

@ -1,7 +1,7 @@
</$objtype/mkfile
TARG=serial
OFILES=ftdi.$O serial.$O prolific.$O ucons.$O silabs.$O
OFILES=ftdi.$O serial.$O prolific.$O ucons.$O silabs.$O ch340.$O
HFILES=\
../lib/usb.h\
serial.h\

View file

@ -712,6 +712,7 @@ static Srv serialfs = {
extern int ftprobe(Serial *ser);
extern int plprobe(Serial *ser);
extern int slprobe(Serial *ser);
extern int chprobe(Serial *ser);
extern int uconsprobe(Serial *ser);
void
@ -748,7 +749,8 @@ threadmain(int argc, char* argv[])
if(uconsprobe(ser)
&& ftprobe(ser)
&& slprobe(ser)
&& plprobe(ser))
&& plprobe(ser)
&& chprobe(ser))
sysfatal("no serial devices found");
for(i = 0; i < ser->nifcs; i++){
@ -769,7 +771,7 @@ threadmain(int argc, char* argv[])
for(i = 0; i < ser->nifcs; i++){
p = &ser->p[i];
if(serinit(p) < 0)
sysfatal("wserinit: %r");
sysfatal("serinit: %r");
if(ser->nifcs == 1)
snprint(p->name, sizeof p->name, "%s%s", p->isjtag ? "jtag" : "eiaU", dev->hname);
else