robocar-led/vendor/periph.io/x/host/v3/ftdi/driver.go

214 lines
4.9 KiB
Go
Raw Normal View History

2021-09-01 19:41:28 +00:00
// Copyright 2018 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
package ftdi
import (
"strconv"
"sync"
"periph.io/x/conn/v3/driver/driverreg"
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/gpio/gpioreg"
"periph.io/x/conn/v3/i2c"
"periph.io/x/conn/v3/i2c/i2creg"
"periph.io/x/conn/v3/pin"
"periph.io/x/conn/v3/pin/pinreg"
"periph.io/x/conn/v3/spi/spireg"
"periph.io/x/d2xx"
)
// All enumerates all the connected FTDI devices.
func All() []Dev {
drv.mu.Lock()
defer drv.mu.Unlock()
out := make([]Dev, len(drv.all))
copy(out, drv.all)
return out
}
//
// open opens a FTDI device.
//
// Must be called with mu held.
func open(opener func(i int) (d2xx.Handle, d2xx.Err), i int) (Dev, error) {
h, err := openHandle(opener, i)
if err != nil {
return nil, err
}
if err := h.Init(); err != nil {
// setupCommon() takes the device in its previous state. It could be in an
// unexpected state, so try resetting it first.
if err := h.Reset(); err != nil {
_ = h.Close()
return nil, err
}
if err := h.Init(); err != nil {
_ = h.Close()
return nil, err
}
// The second attempt worked.
}
// Makes a copy of the handle.
g := generic{index: i, h: h, name: h.t.String()}
if i > 0 {
// When more than one device is present, add "(index)" suffix.
// TODO(maruel): Using the serial number would be nicer than a number.
g.name += "(" + strconv.Itoa(i) + ")"
}
// Makes a copy of the generic instance.
switch g.h.t {
case DevTypeFT232H:
f, err := newFT232H(g)
if err != nil {
_ = h.Close()
return nil, err
}
return f, nil
case DevTypeFT2232H:
f, err := newFT232H(g)
if err != nil {
_ = h.Close()
return nil, err
}
return f, nil
case DevTypeFT232R:
f, err := newFT232R(g)
if err != nil {
_ = h.Close()
return nil, err
}
return f, nil
default:
return &g, nil
}
}
// registerDev registers the header and supported buses and ports in the
// relevant registries.
func registerDev(d Dev, multi bool) error {
name := d.String()
hdr := d.Header()
// Register the GPIOs.
for _, p := range hdr {
if err := gpioreg.Register(p); err != nil {
return err
}
}
if !multi {
// Register shorthands.
// The "." used here vs the "_" used in pinreg is unfortunate. Investigate
// a better way.
prefix := len(name) + 1
for _, p := range hdr {
n := p.Name()
if err := gpioreg.RegisterAlias(n[prefix:], n); err != nil {
return err
}
}
}
// Register the header.
raw := make([][]pin.Pin, len(hdr))
for i := range hdr {
raw[i] = []pin.Pin{hdr[i]}
}
if err := pinreg.Register(name, raw); err != nil {
return err
}
switch t := d.(type) {
case *FT232H:
// Register I²C without pull up.
if err := i2creg.Register(name, nil, -1, func() (i2c.BusCloser, error) { return t.I2C(gpio.Float) }); err != nil {
return err
}
if err := spireg.Register(name, nil, -1, t.SPI); err != nil {
return err
}
// TODO(maruel): UART
case *FT232R:
// TODO(maruel): SPI, UART
}
return nil
}
// driver implements driver.Impl.
type driver struct {
mu sync.Mutex
all []Dev
d2xxOpen func(i int) (d2xx.Handle, d2xx.Err)
numDevices func() (int, error)
}
func (d *driver) String() string {
return "ftdi"
}
func (d *driver) Prerequisites() []string {
return nil
}
func (d *driver) After() []string {
return nil
}
func (d *driver) Init() (bool, error) {
num, err := d.numDevices()
if err != nil {
return true, err
}
multi := num > 1
for i := 0; i < num; i++ {
// TODO(maruel): Close the device one day. :)
if dev, err1 := open(d.d2xxOpen, i); err1 == nil {
d.all = append(d.all, dev)
if err = registerDev(dev, multi); err != nil {
return true, err
}
} else {
// Create a shallow broken handle, so the user can learn how to fix the
// problem.
//
// TODO(maruel): On macOS with a FT232R, calling two processes in a row
// often results in a broken device on the second process. Figure out why
// and make it more resilient.
err = err1
// The serial number is not available so what can be listed is limited.
// TODO(maruel): Add VID/PID?
name := "broken#" + strconv.Itoa(i) + ": " + err.Error()
d.all = append(d.all, &broken{index: i, err: err, name: name})
}
}
return true, err
}
func (d *driver) reset() {
d.mu.Lock()
defer d.mu.Unlock()
d.all = nil
// open is mocked in tests.
d.d2xxOpen = d2xx.Open
// numDevices is mocked in tests.
d.numDevices = numDevices
// The d2xx can hang for up to the timeout under certain circumstances and the
// Go profiler is not very useful to find the source, so use manual logging
// to see where time it spent.
//d.d2xxOpen = func(i int) (d2xx.Handle, int) {
// h, e := d2xxOpen(i)
// return &d2xxLoggingHandle{h}, e
//}
}
func init() {
if d2xx.Available {
drv.reset()
driverreg.MustRegister(&drv)
}
}
var drv driver