Need help with the MCP2210 on Linux with libusb

Thread Starter

bloguetronica

Joined Apr 27, 2007
1,449
Hi,

I'm considering using the MCP2210 in future projects, as a replacement for the discontinued CP2130. The latter had a great advantage, as it worked directly with libusb, and I was well familiarized with it. However, the MCP2210 seems to be an HID device. I'm aware that there are, at least, two libraries in Linux to control HID devices, one is even based around libusb. However, none of them are standard and I prefer to deal with libusb directly. Is there any way of doing this?

Kind regards, Samuel Lourenço
 

Thread Starter

bloguetronica

Joined Apr 27, 2007
1,449
Well, I wish there was another alternative. The HID interface might facilitate use under Windows (if that is a thing), but it is not desirable under Linux, especially when libusb should take care of everything.

Anyway, the .zip file you pointed to doesn't seem to include the header (or I failed to find it). However, I've seen the implementation code before, and they are using linux/hiddev.h . The code is not open source, thought. Probably I'll have to get my hands on one MCP2210 development board, use Windows inside a VM, and sniff some USB packets with Wireshark. Black box reverse engineering, basically. Nothing illegal.
 

nsaspook

Joined Aug 27, 2009
8,389
Well, I wish there was another alternative. The HID interface might facilitate use under Windows (if that is a thing), but it is not desirable under Linux, especially when libusb should take care of everything.

Anyway, the .zip file you pointed to doesn't seem to include the header (or I failed to find it). However, I've seen the implementation code before, and they are using linux/hiddev.h . The code is not open source, thought. Probably I'll have to get my hands on one MCP2210 development board, use Windows inside a VM, and sniff some USB packets with Wireshark. Black box reverse engineering, basically. Nothing illegal.
There is a better driver that might be ported to newer kernels.

https://archive.fosdem.org/2018/schedule/event/rapid_spi_over_usb/
 

Thread Starter

bloguetronica

Joined Apr 27, 2007
1,449
I'll see this. Thanks!

As an aside note, I don't have any issues in using a driver to access the MCP2210. As long as I don't have to use proprietary code inside my software, and as long as that driver is available in the official repositories. The convoluted solutions I've seen so far are the ones that might create issues for me. However, a driver is always external to my software, so, no issues at all.
 

Thread Starter

bloguetronica

Joined Apr 27, 2007
1,449
Thanks! I've bought one today, since I really need to see what is going on, either under Linux, or under Windows. Basically:
- What driver does it use under Linux? Can I talk to that driver? Is there any API?
- If not, what packets are sent by the Windows VM? Can I use libusb to send such packets? I just need to read control transfers, and see what commands the software sends.

Of course, I prefer the first solution, as the second does imply a lot of reverse engineering, and coding a wrapper library around libusb, specifically for the MCP2210. Funnily, I did the same with the CP2130, without the reverse engineering part, because AN721 had the information I needed.

Edit: I'm curious. What is the ICSP header for? Does the MCP2210 require any programming? That header is normally used to program the MCUs from Microchip.
 

nsaspook

Joined Aug 27, 2009
8,389
It uses the standard MCP2210 HID driver. My hope is to eventually fix an existing driver to use the standard Linux framework so standard Linux protocol drivers can be written for it other than spidev.

I think the header is for the PICkit Serial Analyzer (DV164122)
 

Thread Starter

bloguetronica

Joined Apr 27, 2007
1,449
I think I can live with that. I'm not sure why they chose it to enumerate as an HID, though.

I'll probably need to request a new PID for my application, but I guess that a udev rule could make it so that the driver attaches to the modified device.
 

Thread Starter

bloguetronica

Joined Apr 27, 2007
1,449
My board has arrived today. I've connected it to my computer, and opened USB Viewer, with interesting results:

USBViewer.png

There is only one endpoint, endpoint 1, that is both IN (0x81) and OUT (0x01). This may seem to show two endpoints, but both addresses point to the same endpoint, as per the USB convention on endpoint addresses (four last bits of the address indicate the endpoint number, while the first bit indicates direction). The main point is that there is a driver attached, since the name of the device does not show in red. I'll have to look into this in more detail.
 

nsaspook

Joined Aug 27, 2009
8,389
Command line details: lsusb -v -d 04d8:00de

Bus 001 Device 012: ID 04d8:00de Microchip Technology, Inc. MCP2210 USB to SPI Master
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 0
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 8
idVendor 0x04d8 Microchip Technology, Inc.
idProduct 0x00de
bcdDevice 0.02
iManufacturer 1 Microchip Technology Inc.
iProduct 2 MCP2210 USB to SPI Master
iSerial 3 0000369936
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x0029
bNumInterfaces 1
bConfigurationValue 1
iConfiguration 0
bmAttributes 0x80
(Bus Powered)
MaxPower 100mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 3 Human Interface Device
bInterfaceSubClass 0
bInterfaceProtocol 0
iInterface 0
HID Device Descriptor:
bLength 9
bDescriptorType 33
bcdHID 1.11
bCountryCode 0 Not supported
bNumDescriptors 1
bDescriptorType 34 Report
wDescriptorLength 29
Report Descriptors:
** UNAVAILABLE **
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x01 EP 1 OUT
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 1
Device Status: 0x0000
(Bus Powered)
 

Thread Starter

bloguetronica

Joined Apr 27, 2007
1,449
Thanks! I had lsusb installed, but I was not aware of the -d option. Very handy, when it is needed to retrieve information about one device. I think the information I've got is quite comparable:
Code:
Bus 003 Device 010: ID 04d8:00de Microchip Technology, Inc.
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0         8
  idVendor           0x04d8 Microchip Technology, Inc.
  idProduct          0x00de
  bcdDevice            0.02
  iManufacturer           1 Microchip Technology Inc.
  iProduct                2 MCP2210 USB to SPI Master
  iSerial                 3 0000992504
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength       0x0029
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              100mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0
      bInterfaceProtocol      0
      iInterface              0
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.11
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      29
         Report Descriptors:
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1
can't get device qualifier: Resource temporarily unavailable
can't get debug descriptor: Resource temporarily unavailable
Device Status:     0x0000
  (Bus Powered)
I'm yet to find what driver is attached to the MCP2210. I suspect it is hidraw. Doing "cat /proc/devices" returns this:
Code:
Character devices:
  1 mem
  4 /dev/vc/0
  4 tty
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  5 ttyprintk
  6 lp
  7 vcs
10 misc
13 input
21 sg
29 fb
89 i2c
99 ppdev
108 ppp
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
195 nvidia-frontend
204 ttyMAX
226 drm
236 nvidia-uvm
237 mei
238 hidraw
239 nvidia-nvswitch
240 nvidia-nvlink
241 nvidia-caps
242 aux
243 vfio
244 bsg
245 watchdog
246 ptp
247 pps
248 cec
249 rtc
250 dax
251 dimmctl
252 ndctl
253 tpm
254 gpiochip

Block devices:
  7 loop
  8 sd
  9 md
11 sr
65 sd
66 sd
67 sd
68 sd
69 sd
70 sd
71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
253 device-mapper
254 mdp
259 blkext
 

nsaspook

Joined Aug 27, 2009
8,389
Notice that the kernel also creates a hiddev and hidraw device for the chip.
https://www.kernel.org/doc/html/latest/hid/hidraw.html

On my machine.
hid-generic 0003:04D8:00DE.0007: hiddev0,hidraw5: USB HID v1.11 Device [Microchip Technology Inc. MCP2210 USB to SPI Master] on usb-0000:00:1d.7-5.1/input0

https://github.com/torvalds/linux/blob/master/samples/hidraw/hid-example.c

Change the example device to: char *device = "/dev/hidraw5";

Compile and run the program (lo.c) for this.
Code:
cc     lo.c   -o lo
root@hpdesk:~/mcp2210-linux# ./lo
Report Descriptor Size: 29
Report Descriptor:
6 0 ff 9 1 a1 1 19 1 29 40 15 0 26 ff 0 75 8 95 40 81 0 19 1 29 40 91 0 c0

Raw Name: Microchip Technology Inc. MCP2210 USB to SPI Master
Raw Phys: usb-0000:00:1d.7-5.1/input0
Raw Info:
        bustype: 3 (USB)
        vendor: 0x04d8
        product: 0x00de
HIDIOCSFEATURE: Broken pipe
HIDIOCGFEATURE: Broken pipe
write() wrote 2 bytes
read: Resource temporarily unavailable
There is a HIDAPI library for hidraw.
https://github.com/libusb/hidapi
On Linux, either the hidraw or the libusb back-end can be used. There are tradeoffs, and the functionality supported is slightly different. Both are built by default. It is up to the application linking to hidapi to choose the backend at link time by linking to either libhidapi-libusb or libhidapi-hidraw.

Linux/hidraw (linux/hid.c):

This back-end uses the hidraw interface in the Linux kernel, and supports both USB and Bluetooth HID devices. It requires kernel version at least 2.6.39 to build. In addition, it will only communicate with devices which have hidraw nodes associated with them. Keyboards, mice, and some other devices which are blacklisted from having hidraw nodes will not work. Fortunately, for nearly all the uses of hidraw, this is not a problem.
 
Last edited:

Thread Starter

bloguetronica

Joined Apr 27, 2007
1,449
I've compiled the program, and ran it, getting these results:
Code:
Report Descriptor Size: 29
Report Descriptor:
6 0 ff 9 1 a1 1 19 1 29 40 15 0 26 ff 0 75 8 95 40 81 0 19 1 29 40 91 0 c0

Raw Name: Microchip Technology Inc. MCP2210 USB to SPI Master
Raw Phys: usb-0000:00:14.0-3.4.2/input0
Raw Info:
        bustype: 3 (USB)
        vendor: 0x04d8
        product: 0x00de
HIDIOCSFEATURE: Broken pipe
HIDIOCGFEATURE: Broken pipe
write() wrote 2 bytes
read: Resource temporarily unavailable
Accessing an HID device by its HID number was not what I was having in mind. However, I would like to access it by VID and PID. I guess I could use libusb directly? It is probably a more convoluted approach, but I have experience with libusb, since I've dealt with the CP2130 before. The minus of this approach is that probably I'll be reinventing the wheel.

On the other hand, hidapi seems to allow a device to be opened by PID and VID, but there is no documentation. The licenses might be complicated, because I release my software under a GNU license. I don't know what license is hidapi under. It looks proprietary.
 

Thread Starter

bloguetronica

Joined Apr 27, 2007
1,449
I've sniffed the USB traffic from and to the evaluation board with Wireshark.

Screenshot_20210521_003152.png

Yup, the libusb approach is not practical with the MCP2210. Each SPI write corresponds to dozens of interrupt transfers (counted 89 interrupt transfers in total, each time I've pressed "Transfer SPI Data" in the SPI Terminal application running on the Windows VM). In the case of the CP2130, the transfers were more straightforward, with a small number of control transfers to set the CS pin and one or two bulk transfers.
 
Last edited:

nsaspook

Joined Aug 27, 2009
8,389
https://github.com/libusb/hidapi/blob/master/LICENSE.txt

It's included with Debian so I don't think it's proprietary.

https://packages.debian.org/search?searchon=sourcenames&keywords=hidapi

I've not used these libs but they should provide example code for hidapi.
https://www.ontrak.net/hidapic.htm

Using C and the HIDAPI library with ADU USB Data Acquisition Products (Linux & OS X)

https://github.com/kerrydwong/MCP2210-Library
This is an open source C/C++ library for Microchip's USB-to-SPI protocol converter chip MCP2210. It works under Linux. This library is released under Apache License, Version 2.0

The library uses signal11's hidapi. You can find the library documentations in the doc directory, or you can view online here. Some code examples are listed below:

 
Last edited:

Thread Starter

bloguetronica

Joined Apr 27, 2007
1,449
Thanks! I'll have to see what is best. Licenses may be an issue if I have to insert libraries into the program or application I'm doing. Also, the documentation seems to be scarce, without examples. It confuses me how hidapi can either use libusb or hidraw as the back-end, and how to choose one or another. If I have to, I rather prefer doing my own MCP2210 library. However, I'm yet to see if hidapi is installable and can be used as an external library, pretty much like libusb. If so, I would go for it.

Nevertheless, I'll have to contemplate another USB to SPI option, the CY7C65211 from Cypress. I was not happy to see the relatively big number of packets for a single SPI transfer, with the MCP2210. If HID devices are supposed to work that way, it is a big no for me.
 

nsaspook

Joined Aug 27, 2009
8,389
I've quickly hacked a quick and dirty (the ADC read is flaky) example using a modified version of a modified version of the hidtest.c program found on the net.
Eval board leds changing in response to SPI (5MHz sck) commands sent from the Linux program using the hidraw interface.
This seems to work OK for me so I'm unlikely now to take the effort to make a new driver.

You can select which back-end at the linker stage of the compile. I'm using Netbeans for this demo. I'm using the Debian supplied hidapi header and linker files.


code fragment from the demo
C:
    // MCP23S08 config
    buf[0] = 0x42; // transfer SPI data command
    buf[1] = 3; // no. of SPI bytes to transfer
    buf[4] = 0x40; //device address is 01000A1A0, write
    buf[5] = 0x00; //write to IODIR register,
    buf[6] = 0x00; //set all outputs to low

    res = hid_write(handle, buf, 7);
    res = hid_read(handle, rbuf, 7);

    printf("Ctrl c to exit\n"); // ctrl c to exit blink loop and exit program

    // MCP23S08 updates
    buf[4] = 0x40;
    buf[5] = 0x0a;

    while (1) { // blink LED loop
        for (int k = 0; k < 10; k++) {
            //lights up LED0 through LED7 one by one
            for (int i = 0; i < 8; i++) {
                buf[6] = 1 << i;
                res = hid_write(handle, buf, 7);
                res = hid_read(handle, rbuf, 7);
                usleep(20000ul);
            }
            //lights up LED7 through LED0 one by one
            for (int i = 0; i < 8; i++) {
                buf[6] = 0x80 >> i;
                res = hid_write(handle, buf, 7);
                res = hid_read(handle, rbuf, 7);
                usleep(20000ul);
            }
        }
    }
Code:
cd '/root/hidapi/linux_test/hidapi_test'
/usr/bin/make -f Makefile CONF=Debug clean
"/usr/bin/make" -f nbproject/Makefile-Debug.mk QMAKE= SUBPROJECTS= .clean-conf
make[1]: Entering directory '/root/hidapi/linux_test/hidapi_test'
rm -f -r build/Debug
make[1]: Leaving directory '/root/hidapi/linux_test/hidapi_test'

CLEAN SUCCESSFUL (total time: 353ms)
cd '/root/hidapi/linux_test/hidapi_test'
/usr/bin/make -f Makefile CONF=Debug
"/usr/bin/make" -f nbproject/Makefile-Debug.mk QMAKE= SUBPROJECTS= .build-conf
make[1]: Entering directory '/root/hidapi/linux_test/hidapi_test'
"/usr/bin/make"  -f nbproject/Makefile-Debug.mk dist/Debug/GNU-Linux/hidapi_test
make[2]: Entering directory '/root/hidapi/linux_test/hidapi_test'
mkdir -p build/Debug/GNU-Linux
rm -f "build/Debug/GNU-Linux/hidtest.o.d"
g++    -c -g `pkg-config --cflags hidapi-hidraw`   -MMD -MP -MF "build/Debug/GNU-Linux/hidtest.o.d" -o build/Debug/GNU-Linux/hidtest.o hidtest.cpp
mkdir -p dist/Debug/GNU-Linux
g++   -l:libhidapi-hidraw.a  -o dist/Debug/GNU-Linux/hidapi_test build/Debug/GNU-Linux/hidtest.o `pkg-config --libs hidapi-hidraw`
make[2]: Leaving directory '/root/hidapi/linux_test/hidapi_test'
make[1]: Leaving directory '/root/hidapi/linux_test/hidapi_test'

BUILD SUCCESSFUL (total time: 1s)
 

Attachments

Last edited:

Thread Starter

bloguetronica

Joined Apr 27, 2007
1,449
Thanks! Reading your code quicky, some questions arise:
- Is "cur_dev" a C++ structure that is being instantiated dynamically?
- Why using the same instance of "buf" and "rbuf" throughout the code?
- Do you need to enumerate all the HID devices, or you can just jump to hid_open() after hid_init()?

Otherwise, the code seems to be straighthforward to me. It resembles what one would do with libusb. The MCP2210 might deserve a class, similar to what I did with the CP2130. Basically, it will be a wrapper around hiddev. I like what I see!

I'll test this. What libraries are you including in your makefile, by the way?
 

Thread Starter

bloguetronica

Joined Apr 27, 2007
1,449
I've compiled the program, but I had to install "libhidapi-dev" first. However, despite successfully listing all the HID devices, the program can't open the MCP2210. Did you have to create a udev rule?
 
Last edited:
Top