ref: dc593372bfc4224c6cf91d2031d0ad0aeadaddfe
dir: /sys/src/cmd/nusb/serial/ch340.c/
#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, };