ref: 8dfff00e50652e1c0580c307374991b8cd3b864f
parent: 21001d4294c0496a2fc8c23eead89df44bd85825
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun Oct 21 13:24:33 EDT 2018
nusb/serial: add support for CH340 serial converter
--- /dev/null
+++ b/sys/src/cmd/nusb/serial/ch340.c
@@ -1,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,
+};
--- a/sys/src/cmd/nusb/serial/mkfile
+++ b/sys/src/cmd/nusb/serial/mkfile
@@ -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\
--- a/sys/src/cmd/nusb/serial/serial.c
+++ b/sys/src/cmd/nusb/serial/serial.c
@@ -712,6 +712,7 @@
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 @@
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 @@
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