USB Hid driver Help

Lucas Tanure tanure at linux.com
Tue Feb 4 13:15:10 EST 2020


On Tue, Feb 4, 2020 at 1:27 PM Lucas Tanure <tanure at linux.com> wrote:
>
> On Mon, Feb 3, 2020 at 10:00 PM Lucas Tanure <tanure at linux.com> wrote:
> >
> > On Mon, Feb 3, 2020 at 8:11 PM Greg KH <greg at kroah.com> wrote:
> > >
> > > On Mon, Feb 03, 2020 at 04:32:46PM +0000, Lucas Tanure wrote:
> > > > Hi,
> > > >
> > > > I'm trying to write a Hid driver for MCP2210.
> > >
> > > What type of device is this?
> > It is a USB <-> SPI converter.
> >
> > >
> > > > But the USB Hid specification is quite complicated.
> > >
> > > The kernel should do it "all for you" already, why do you need to create
> > > a custom HID driver for this device?
> > I need a driver that register an SPI controller with in the kernel.
> > So this SPI controller would receive regmap reads/writes and translate
> > to USB HID packages and send to the SPI device attached in the other
> > side of the cable.
> >
> > >
> > > What type of HID reports does the device export and why doesn't the
> > > existing kernel drivers work for you?
> > I don't know enough yet to answer about HID Reports, but at end of
> > this e-mail I attached the lsub -v of the device.
> > I want to use this device in kernel space, registering a SPI
> > controller. And with this SPI controller another SPI slave device will
> > be registered.
> > So, for the SPI slave device it will be like there is no USB in
> > between itself and the kernel.
> >
> > >
> > > > I would like to know how to send and receive data to the device. Any
> > > > links to a good tutorial ?
> > >
> > > HID has the idea of "reports" and data comes in and out in that specific
> > > format, all depending on how the device describes itself.  There's isn't
> > > usually a normal "send/receive" type of thing, but it all depends on the
> > > type of device.
> > Ok. Reading the datasheet of this device I need to send some 64 byte
> > arrays to configure the SPI bus.
> > And after that I start to send reports to communicate with the SPI slave device.
> >
> > >
> > > > This is my current driver is attached.
> > > >
> > > > Thanks
> > > > Lucas
> > >
> > > > #define DEBUG
> > > > #include <linux/module.h>
> > > > #include <linux/hid.h>
> > > > #include <linux/usb.h>
> > > >
> > > > static int mcp2210_probe(struct hid_device *hdev,
> > > >                        const struct hid_device_id *id)
> > > > {
> > > >       struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
> > > >       int ret = 0;
> > > >
> > > >       hid_dbg(hdev, "%s\n", __FUNCTION__);
> > > >
> > > >       ret = hid_parse(hdev);
> > > >       if (ret) {
> > > >               hid_err(hdev, "parse failed\n");
> > > >               return ret;
> > > >       } else {
> > > >               hid_dbg(hdev, "parse success\n");
> > > >       }
> > > >
> > > >       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
> > > >       if (ret) {
> > > >               hid_err(hdev, "hw start failed\n");
> > > >               return ret;
> > > >       } else {
> > > >               hid_dbg(hdev, "start success\n");
> > > >       }
> > >
> > > Does this all work?
> > For this version, it`s takes precedence from HID generic driver, wich is great.
> >
> > >
> > > What fails?
> > If I try to execute small test of data communcation:
> >
> > u8 *buf = kzalloc (64, GFP_KERNEL);
> > buf[0] = 0x50; //read EEPROM command
> > buf[1] = 0x03 ; // Address to read
> >
> > ret = hid_hw_raw_request(hdev, 0, buf, 64, HID_INPUT_REPORT, HID_REQ_GET_REPORT)
> > ret is -EPIPE.
> >
> > >
> > > thanks,
> > >
> > > greg k-h
> >
> > Many Thanks
> > Lucas
> >
> > Bus 001 Device 008: ID 04d8:00de Microchip Technology, Inc. MCP2210
> > USB to SPI Master
> > Couldn't open device, some information will be missing
> > 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
> >  iProduct                2
> >  iSerial                 3
> >  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
>
> Hi,
>
> If I use hid_hw_output_report I can write my 64 bytes buffer to the device.
> And I would expect a 64 bytes answer from the device, but I don't know
> how to get it.
>
> How do I know where my output data is going to ? Control Pipe or
> Interrupt Pipe ?
> How to I get the answer data from the device ?
>
> Thanks
> Lucas
>
> static int mcp2210_probe(struct hid_device *hdev,
>                          const struct hid_device_id *id)
> {
>         //struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
>         int ret = 0;
>         //struct hid_report *report = kzalloc(sizeof(struct
> hid_report), GFP_KERNEL);
>         u8 *buf = kzalloc(64, GFP_KERNEL);
>         u8 *buf2 = kzalloc(64, GFP_KERNEL);
>
>         hid_dbg(hdev, "%s\n", __FUNCTION__);
>
>         ret = hid_parse(hdev);
>         if (ret) {
>                 hid_err(hdev, "parse failed\n");
>                 return ret;
>         } else {
>                 hid_dbg(hdev, "parse success\n");
>         }
>
>         ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
>         if (ret) {
>                 hid_err(hdev, "hw start failed\n");
>                 return ret;
>         } else {
>                 hid_dbg(hdev, "start success\n");
>         }
>         ret = hid_hw_open(hdev);
>         hid_dbg(hdev, "hid_hw_open %d\n", ret);
>
>         buf[0] = 0x50;
>         buf[1] = 0x07;
>         ret = hid_hw_output_report(hdev, buf, 64);
>         hid_dbg(hdev, "hid_hw_output_report %d back
> [%x][%x][%x][%x]\n", ret, buf[0], buf[1], buf[2], buf[3]);
>         //dmesg prints ret 0
>
>         ret = hid_hw_raw_request(hdev, 0x81, buf2, 64,
> HID_INPUT_REPORT, HID_REQ_SET_REPORT);
>         hid_dbg(hdev, "hid_input_report %d back [%x][%x][%x][%x]\n",
> ret, buf2[0], buf2[1], buf2[2], buf2[3]);
>         //dmesg prints ret -32
>
>         kfree(buf);
>         kfree(buf2);
>
>         return 0;
> }
>
> Dmesg:
> [   72.991830] MCP2210 USB SPI Driver 0003:04D8:00DE.0002: mcp2210_probe
> [   73.001793] MCP2210 USB SPI Driver 0003:04D8:00DE.0002: parse success
> [   73.020234] MCP2210 USB SPI Driver 0003:04D8:00DE.0002:
> hiddev96,hidraw0: USB HID v1.11 Device [Microchip Technology Inc.
> MCP2210 USB-to-SPI Master] on usb-3f980000.usb-1.3/input0
> [   73.039798] MCP2210 USB SPI Driver 0003:04D8:00DE.0002: start success
> [   73.111858] MCP2210 USB SPI Driver 0003:04D8:00DE.0002: hid_hw_open 0
> [   73.126599] MCP2210 USB SPI Driver 0003:04D8:00DE.0002:
> hid_hw_output_report 64 back [50][7][0][0]
> [   73.140170] MCP2210 USB SPI Driver 0003:04D8:00DE.0002:
> hid_input_report -32 back [81][0][0][0]

Hi,

Doing some hacks I can see that using a user space library for this device uses:
[   38.188226] [<806b9728>] (usbhid_output_report) from [<806b7db4>]
(hidraw_send_report+0x100/0x180)
And the layer between hidraw_send_report and usbhid_output_report is
hid_hw_output_report.  So the function I should use is
hid_hw_output_report.

[   38.137293] usbhid_output_report START
###############################################################################
[   38.137306] usbhid_output_report 30 0 0 0 1 0 0
[   38.148204] CPU: 3 PID: 475 Comm: hidusbtest Tainted: G         C
     4.19.69-v7+ #8
[   38.148210] Hardware name: BCM2835
[   38.164463] [<80111dac>] (unwind_backtrace) from [<8010d36c>]
(show_stack+0x20/0x24)
[   38.172494] [<8010d36c>] (show_stack) from [<808166bc>]
(dump_stack+0xcc/0x110)
[   38.179928] [<808166bc>] (dump_stack) from [<806b9728>]
(usbhid_output_report+0x94/0x144)
[   38.188226] [<806b9728>] (usbhid_output_report) from [<806b7db4>]
(hidraw_send_report+0x100/0x180)
[   38.197311] [<806b7db4>] (hidraw_send_report) from [<806b7e78>]
(hidraw_write+0x44/0x58)
[   38.205517] [<806b7e78>] (hidraw_write) from [<802ac2ec>]
(__vfs_write+0x48/0x170)
[   38.213193] [<802ac2ec>] (__vfs_write) from [<802ac5fc>]
(vfs_write+0xb4/0x1c4)
[   38.220604] [<802ac5fc>] (vfs_write) from [<802ac8d4>] (ksys_write+0x6c/0xec)
[   38.227841] [<802ac8d4>] (ksys_write) from [<802ac96c>] (sys_write+0x18/0x1c)
[   38.235076] [<802ac96c>] (sys_write) from [<80101000>]
(ret_fast_syscall+0x0/0x28)
[   38.242750] Exception stack(0xb391ffa8 to 0xb391fff0)
[   38.247869] ffa0:                   00000000 00014bec 00000003
7e9ec8b4 00000040 00000000
[   38.256161] ffc0: 00000000 00014bec 00010f38 00000004 00000000
00000000 76fc9000 7e9ec844
[   38.264451] ffe0: 0000006c 7e9ec810 00012f00 76cd4944
[   38.272431] usbhid_output_report END
###############################################################################

And after using hid_hw_output_report my driver get a call at mcp2210_raw_event:
[   38.333979] [<7f32a04c>] (mcp2210_raw_event [mcp2210]) from
[<806ae188>] (hid_input_report+0xf4/0x18c)

So, to receive the success status of the command sent with
hid_hw_output_report the driver receives an event.
Now I need to understand how to register my driver to receive this
event, once the hidraw driver will not be in use.

[   38.284788] hid_irq_in 0
[   38.284798] mcp2210_raw_event START
###############################################################################
[   38.287370] CPU: 0 PID: 322 Comm: rs:main Q:Reg Tainted: G
C        4.19.69-v7+ #8
[   38.306324] Hardware name: BCM2835
[   38.309787] [<80111dac>] (unwind_backtrace) from [<8010d36c>]
(show_stack+0x20/0x24)
[   38.317642] [<8010d36c>] (show_stack) from [<808166bc>]
(dump_stack+0xcc/0x110)
[   38.325062] [<808166bc>] (dump_stack) from [<7f32a04c>]
(mcp2210_raw_event+0x4c/0x120 [mcp2210])
[   38.333979] [<7f32a04c>] (mcp2210_raw_event [mcp2210]) from
[<806ae188>] (hid_input_report+0xf4/0x18c)
[   38.343420] [<806ae188>] (hid_input_report) from [<806ba39c>]
(hid_irq_in+0x20c/0x248)
[   38.351452] [<806ba39c>] (hid_irq_in) from [<8061bc48>]
(__usb_hcd_giveback_urb+0xa0/0x15c)
[   38.359923] [<8061bc48>] (__usb_hcd_giveback_urb) from [<8061befc>]
(usb_hcd_giveback_urb+0xd4/0xf4)
[   38.369188] [<8061befc>] (usb_hcd_giveback_urb) from [<80647434>]
(completion_tasklet_func+0x8c/0xcc)
[   38.378542] [<80647434>] (completion_tasklet_func) from
[<80656374>] (tasklet_callback+0x20/0x24)
[   38.387544] [<80656374>] (tasklet_callback) from [<80126a14>]
(tasklet_action_common.constprop.5+0x64/0xec)
[   38.397426] [<80126a14>] (tasklet_action_common.constprop.5) from
[<80126af4>] (tasklet_hi_action+0x28/0x30)
[   38.407394] [<80126af4>] (tasklet_hi_action) from [<801023f4>]
(__do_softirq+0x184/0x424)
[   38.415687] [<801023f4>] (__do_softirq) from [<801266ac>]
(irq_exit+0xf8/0x134)
[   38.423102] [<801266ac>] (irq_exit) from [<8017f710>]
(__handle_domain_irq+0x70/0xc4)
[   38.431044] [<8017f710>] (__handle_domain_irq) from [<801021a0>]
(bcm2836_arm_irqchip_handle_irq+0x60/0xa8)
[   38.440924] [<801021a0>] (bcm2836_arm_irqchip_handle_irq) from
[<801019bc>] (__irq_svc+0x5c/0x7c)
[   38.449919] Exception stack(0xb63e5bf0 to 0xb63e5c38)
[   38.455038] 5be0:                                     b59d022c
00000005 80c66000 b9bb87c0
[   38.463331] 5c00: b59d00b0 00000400 80c66000 b59d022c b97dc400
b63e5da0 b63e5cd4 b63e5c4c
[   38.471622] 5c20: b63e5c50 b63e5c40 8036a1e4 808322fc 40000013 ffffffff
[   38.478330] [<801019bc>] (__irq_svc) from [<808322fc>]
(_raw_spin_lock+0xc/0x54)
[   38.485834] [<808322fc>] (_raw_spin_lock) from [<8036a1e4>]
(ext4_mark_iloc_dirty+0x108/0x8c8)
[   38.494569] [<8036a1e4>] (ext4_mark_iloc_dirty) from [<8036aca4>]
(ext4_mark_inode_dirty+0x84/0x1f8)
[   38.503832] [<8036aca4>] (ext4_mark_inode_dirty) from [<8037058c>]
(ext4_dirty_inode+0x54/0x70)
[   38.512656] [<8037058c>] (ext4_dirty_inode) from [<802de768>]
(__mark_inode_dirty+0x50/0x414)
[   38.521305] [<802de768>] (__mark_inode_dirty) from [<802c97a8>]
(generic_update_time+0xf4/0x11c)
[   38.530215] [<802c97a8>] (generic_update_time) from [<802c9c88>]
(file_update_time+0x128/0x158)
[   38.539039] [<802c9c88>] (file_update_time) from [<8023b158>]
(__generic_file_write_iter+0xa0/0x1e0)
[   38.548305] [<8023b158>] (__generic_file_write_iter) from
[<80357a20>] (ext4_file_write_iter+0xfc/0x4b8)
[   38.557921] [<80357a20>] (ext4_file_write_iter) from [<802ac3b0>]
(__vfs_write+0x10c/0x170)
[   38.566390] [<802ac3b0>] (__vfs_write) from [<802ac5fc>]
(vfs_write+0xb4/0x1c4)
[   38.573800] [<802ac5fc>] (vfs_write) from [<802ac8d4>] (ksys_write+0x6c/0xec)
[   38.581034] [<802ac8d4>] (ksys_write) from [<802ac96c>] (sys_write+0x18/0x1c)
[   38.588269] [<802ac96c>] (sys_write) from [<80101000>]
(ret_fast_syscall+0x0/0x28)
[   38.595941] Exception stack(0xb63e5fa8 to 0xb63e5ff0)
[   38.601059] 5fa0:                   0000004a 75700bc0 00000007
75700bc0 0000004a 00000000
[   38.609351] 5fc0: 0000004a 75700bc0 00000007 00000004 75700bc0
fffff815 00092410 000924e8
[   38.617641] 5fe0: 00000002 760fe6e8 00000000 76eeb1bc
[   38.622762] MCP2210 USB SPI Driver 0003:04D8:00DE.0001: mcp2210_raw_event 64
[   38.629930] MCP2210 USB SPI Driver 0003:04D8:00DE.0001: 30 0 0 0 1
0 2 2 2 2 2 0 0 ff 1 ff 1 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0
[   38.646944] mcp2210_raw_event END
###############################################################################



More information about the Kernelnewbies mailing list