shithub: riscv

Download patch

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