Introduction: Use PcDuino As a Mouse When Its Connected to a PC Through USB OTG

In modem life,  our hands are locked by computers.  Could we make computers smarter by designing a better human-computer interface?  In this tutorial, we present a solution with pcDuino.  Gathering gesture signals by various Arduino compatible sensors, pcDuino can achieve all functions of current keyboards and mouse.

Step 1: Modify Kernel

We need to modify the kernel by adding some structures and few lines of code in to file: linux-sunxi/drivers/usb/gadget/hid.c.

The structures and lines of code is shown below (it can also be downloaded from here):
static LIST_HEAD(hidg_func_list);

/* hid descriptor for a keyboard */

static struct hidg_func_descriptor pcduino_keyboard_date = {

.subclass       = 0, /* No subclass */

.protocol       = 1, /* Keyboard */

.report_length      = 8,

.report_desc_length = 63,

.report_desc        = {

0×05, 0×01, /* USAGE_PAGE (Generic Desktop)           */

0×09, 0×06, /* USAGE (Keyboard)                       */

0xa1, 0×01, /* COLLECTION (Application)               */

0×05, 0×07, /*   USAGE_PAGE (Keyboard)                */

0×19, 0xe0, /*   USAGE_MINIMUM (Keyboard LeftControl) */

0×29, 0xe7, /*   USAGE_MAXIMUM (Keyboard Right GUI)   */

0×15, 0×00, /*   LOGICAL_MINIMUM (0)                  */

0×25, 0×01, /*   LOGICAL_MAXIMUM (1)                  */

0×75, 0×01, /*   REPORT_SIZE (1)                      */

0×95, 0×08, /*   REPORT_COUNT (8)                     */

0×81, 0×02, /*   INPUT (Data,Var,Abs)                 */

0×95, 0×01, /*   REPORT_COUNT (1)                     */

0×75, 0×08, /*   REPORT_SIZE (8)                      */

0×81, 0×03, /*   INPUT (Cnst,Var,Abs)                 */

0×95, 0×05, /*   REPORT_COUNT (5)                     */

0×75, 0×01, /*   REPORT_SIZE (1)                      */

0×05, 0×08, /*   USAGE_PAGE (LEDs)                    */

0×19, 0×01, /*   USAGE_MINIMUM (Num Lock)             */

0×29, 0×05, /*   USAGE_MAXIMUM (Kana)                 */

0×91, 0×02, /*   OUTPUT (Data,Var,Abs)                */

0×95, 0×01, /*   REPORT_COUNT (1)                     */

0×75, 0×03, /*   REPORT_SIZE (3)                      */

0×91, 0×03, /*   OUTPUT (Cnst,Var,Abs)                */

0×95, 0×06, /*   REPORT_COUNT (6)                     */

0×75, 0×08, /*   REPORT_SIZE (8)                      */

0×15, 0×00, /*   LOGICAL_MINIMUM (0)                  */

0×25, 0×65, /*   LOGICAL_MAXIMUM (101)                */

0×05, 0×07, /*   USAGE_PAGE (Keyboard)                */

0×19, 0×00, /*   USAGE_MINIMUM (Reserved)             */

0×29, 0×65, /*   USAGE_MAXIMUM (Keyboard Application) */

0×81, 0×00, /*   INPUT (Data,Ary,Abs)                 */

0xc0        /* END_COLLECTION                         */

}

};

static struct platform_device pcduino_hid_keyboard = {

.name           = “hidg”,

.id         = 0,

.num_resources      = 0,

.resource       = 0,

.dev.platform_data  = pcduino_keyboard_date,

};

/* hid descriptor for a mouse */

static struct hidg_func_descriptor pcduino_mouse_data = {

.subclass = 0, //No SubClass

.protocol = 2, //Mouse

.report_length = 4,

.report_desc_length = 52,

.report_desc = {

0×05, 0×01,  //Usage Page(Generic Desktop Controls)

0×09, 0×02,  //Usage (Mouse)

0xa1, 0×01,  //Collction (Application)

0×09, 0×01,  //Usage (pointer)

0xa1, 0×00,  //Collction (Physical)

0×05, 0×09,  //Usage Page (Button)

0×19, 0×01,  //Usage Minimum(1)

0×29, 0×05,  //Usage Maximum(5)

0×15, 0×00,  //Logical Minimum(1)

0×25, 0×01,  //Logical Maximum(1)

0×95, 0×05,  //Report Count(5)

0×75, 0×01,  //Report Size(1)

0×81, 0×02,  //Input(Data,Variable,Absolute,BitField)

0×95, 0×01,  //Report Count(1)

0×75, 0×03,  //Report Size(3)

0×81, 0×01,  //Input(Constant,Array,Absolute,BitField)

0×05, 0×01,  //Usage Page(Generic Desktop Controls)

0×09, 0×30,  //Usage(x)

0×09, 0×31,  //Usage(y)

0×09, 0×38,  //Usage(Wheel)

0×15, 0×81,  //Logical Minimum(-127)

0×25, 0x7F,  //Logical Maximum(127)

0×75, 0×08,  //Report Size(8)

0×95, 0×03,  //Report Count(3)

0×81, 0×06,  //Input(Data,Variable,Relative,BitField)

0xc0,  //End Collection

0xc0  //End Collection

}

};

static struct platform_device pcduino_hid_mouse = {

.name = “hidg”,

.id = 1,

.num_resources = 0,

.resource = 0,

.dev.platform_data =pcduino_mouse_data,

};

static int __init hidg_init(void)

{

int status;

status = platform_device_register(pcduino_hid_keyboard);

if (status < 0) {

printk(“hid keyboard  reg failed\n”);

platform_device_unregister(pcduino_hid_keyboard);

return status;

}

status = platform_device_register(pcduino_hid_mouse);

if (status <0) {

printk(“hid mouse reg  failed\n”);

platform_device_unregister(pcduino_hid_mouse);

return status;

}

status = platform_driver_probe(hidg_plat_driver,
hidg_plat_driver_probe);

if (status < 0)

return status;

status = usb_composite_probe(hidg_driver, hid_bind);

if (status < 0)

platform_driver_unregister(hidg_plat_driver);

return status;

}

module_init(hidg_init);

static void __exit hidg_cleanup(void)

{

platform_driver_unregister(hidg_plat_driver);

platform_device_unregister(pcduino_hid_keyboard);

platform_device_unregister(pcduino_hid_mouse);

usb_composite_unregister(hidg_driver);

}
Structures need to be added. We also need to register the structures the  two functions. Please be noted according to USB protocol,  each device has a descriptor. The two structures are the descriptors of the HID mouse and keyboard.  In the next step, we are going to configure the kernel. It can also be downloaded from here. config_kernel
Device Drivers  —>

[*] USB support  —>

   USB Gadget Support  —>

     SoftWinner SUN4I USB Peripheral Controller

< >     Dummy HCD (DEVELOPMENT)

USB Gadget Drivers

< >     Gadget Zero (DEVELOPMENT)

< >     Audio Gadget (EXPERIMENTAL)

     Ethernet Gadget (with CDC Ethernet support)

[*]       RNDIS support

[ ]       Ethernet Emulation Model (EEM) support

< >     Network Control Model (NCM) support

< >     Gadget Filesystem (EXPERIMENTAL)

< >     Function Filesystem (EXPERIMENTAL)

< >     File-backed Storage Gadget (DEPRECATED)

< >     Mass Storage Gadget

     Serial Gadget (with CDC ACM and CDC OBEX support)

     MIDI Gadget (EXPERIMENTAL)

     Printer Gadget

< >     CDC Composite Device (Ethernet and ACM)

< >     CDC Composite Device (ACM and mass storage)

< >     Multifunction Composite Gadget (EXPERIMENTAL)

     HID Gadget

< >     EHCI Debug Device Gadget

< >     USB Webcam Gadget

Step 2: Load the New Kernel

For how to build kernel, please refer to other articles.

Here we briefly review the steps:

$sudo mount  /dev/nanda  /mnt
$tar xvf pcduino_a10_hwpack_20131129.tar.xz
$cp  kernel/*  /mnt  -f
$cp  rootfs/lib/modules/3.4.29+  /lib/modules/

Then we append the following content to /etc/modules:
vim /etc/modules #add the following content
sw_usb_udc
g_hid

Step 3: Test the Driver

We’re going to use the test program from linux-sunxi/Documentation/usb.

The code is shown below (it can also be downloaded from here):
static LIST_HEAD(hidg_func_list);

/* hid descriptor for a keyboard */

static struct hidg_func_descriptor pcduino_keyboard_date = {

.subclass       = 0, /* No subclass */

.protocol       = 1, /* Keyboard */

.report_length      = 8,

.report_desc_length = 63,

.report_desc        = {

0×05, 0×01, /* USAGE_PAGE (Generic Desktop)           */

0×09, 0×06, /* USAGE (Keyboard)                       */

0xa1, 0×01, /* COLLECTION (Application)               */

0×05, 0×07, /*   USAGE_PAGE (Keyboard)                */

0×19, 0xe0, /*   USAGE_MINIMUM (Keyboard LeftControl) */

0×29, 0xe7, /*   USAGE_MAXIMUM (Keyboard Right GUI)   */

0×15, 0×00, /*   LOGICAL_MINIMUM (0)                  */

0×25, 0×01, /*   LOGICAL_MAXIMUM (1)                  */

0×75, 0×01, /*   REPORT_SIZE (1)                      */

0×95, 0×08, /*   REPORT_COUNT (8)                     */

0×81, 0×02, /*   INPUT (Data,Var,Abs)                 */

0×95, 0×01, /*   REPORT_COUNT (1)                     */

0×75, 0×08, /*   REPORT_SIZE (8)                      */

0×81, 0×03, /*   INPUT (Cnst,Var,Abs)                 */

0×95, 0×05, /*   REPORT_COUNT (5)                     */

0×75, 0×01, /*   REPORT_SIZE (1)                      */

0×05, 0×08, /*   USAGE_PAGE (LEDs)                    */

0×19, 0×01, /*   USAGE_MINIMUM (Num Lock)             */

0×29, 0×05, /*   USAGE_MAXIMUM (Kana)                 */

0×91, 0×02, /*   OUTPUT (Data,Var,Abs)                */

0×95, 0×01, /*   REPORT_COUNT (1)                     */

0×75, 0×03, /*   REPORT_SIZE (3)                      */

0×91, 0×03, /*   OUTPUT (Cnst,Var,Abs)                */

0×95, 0×06, /*   REPORT_COUNT (6)                     */

0×75, 0×08, /*   REPORT_SIZE (8)                      */

0×15, 0×00, /*   LOGICAL_MINIMUM (0)                  */

0×25, 0×65, /*   LOGICAL_MAXIMUM (101)                */

0×05, 0×07, /*   USAGE_PAGE (Keyboard)                */

0×19, 0×00, /*   USAGE_MINIMUM (Reserved)             */

0×29, 0×65, /*   USAGE_MAXIMUM (Keyboard Application) */

0×81, 0×00, /*   INPUT (Data,Ary,Abs)                 */

0xc0        /* END_COLLECTION                         */

}

};

static struct platform_device pcduino_hid_keyboard = {

.name           = “hidg”,

.id         = 0,

.num_resources      = 0,

.resource       = 0,

.dev.platform_data  = &amp;amp;pcduino_keyboard_date,

};

/* hid descriptor for a mouse */

static struct hidg_func_descriptor pcduino_mouse_data = {

.subclass = 0, //No SubClass

.protocol = 2, //Mouse

.report_length = 4,

.report_desc_length = 52,

.report_desc = {

0×05, 0×01,  //Usage Page(Generic Desktop Controls)

0×09, 0×02,  //Usage (Mouse)

0xa1, 0×01,  //Collction (Application)

0×09, 0×01,  //Usage (pointer)

0xa1, 0×00,  //Collction (Physical)

0×05, 0×09,  //Usage Page (Button)

0×19, 0×01,  //Usage Minimum(1)

0×29, 0×05,  //Usage Maximum(5)

0×15, 0×00,  //Logical Minimum(1)

0×25, 0×01,  //Logical Maximum(1)

0×95, 0×05,  //Report Count(5)

0×75, 0×01,  //Report Size(1)

0×81, 0×02,  //Input(Data,Variable,Absolute,BitField)

0×95, 0×01,  //Report Count(1)

0×75, 0×03,  //Report Size(3)

0×81, 0×01,  //Input(Constant,Array,Absolute,BitField)

0×05, 0×01,  //Usage Page(Generic Desktop Controls)

0×09, 0×30,  //Usage(x)

0×09, 0×31,  //Usage(y)

0×09, 0×38,  //Usage(Wheel)

0×15, 0×81,  //Logical Minimum(-127)

0×25, 0x7F,  //Logical Maximum(127)

0×75, 0×08,  //Report Size(8)

0×95, 0×03,  //Report Count(3)

0×81, 0×06,  //Input(Data,Variable,Relative,BitField)

0xc0,  //End Collection

0xc0  //End Collection

}

};

static struct platform_device pcduino_hid_mouse = {

.name = “hidg”,

.id = 1,

.num_resources = 0,

.resource = 0,

.dev.platform_data = &amp;amp;pcduino_mouse_data,

};

static int __init hidg_init(void)

{

int status;

status = platform_device_register(&amp;amp;pcduino_hid_keyboard);

if (status &amp;lt; 0) {

printk(“hid keyboard  reg failed\n”);

platform_device_unregister(&amp;amp;pcduino_hid_keyboard);

return status;

}

status = platform_device_register(&amp;amp;pcduino_hid_mouse);

if (status &amp;lt; 0) {

printk(“hid mouse reg  failed\n”);

platform_device_unregister(&amp;amp;pcduino_hid_mouse);

return status;

}

status = platform_driver_probe(&amp;amp;hidg_plat_driver,

hidg_plat_driver_probe);

if (status &amp;lt; 0)

return status;

status = usb_composite_probe(&amp;amp;hidg_driver, hid_bind);

if (status &amp;lt; 0)

platform_driver_unregister(&amp;amp;hidg_plat_driver);

return status;

}

module_init(hidg_init);

static void __exit hidg_cleanup(void)

{

platform_driver_unregister(&amp;amp;hidg_plat_driver);

platform_device_unregister(&amp;amp;pcduino_hid_keyboard);

platform_device_unregister(&amp;amp;pcduino_hid_mouse);

usb_composite_unregister(&amp;amp;hidg_driver);

}

Compile:

$gcc  gadget_hid.c
By connect pcDuino to a PC through USB OTG, and then run the following command on pcDuino:

root@ubuntu:/home/ubuntu# ./a.out /dev/hidg0  k   #keyboard

keyboard options:

–hold

–left-ctrl

–right-ctrl

–left-shift

–right-shift

–left-alt

–right-alt

–left-meta

–right-meta

keyboard values:

[a-z] or

–return             –esc

–bckspc             –tab

–spacebar                 –caps-lock

–f1                –f2

–f3                –f4

–f5                –f6

–f7                –f8

–f9                –f10

–f11               –f12

–insert              –home

–pageup           –del

–end              –pagedown

–right               –left

–down            –kp-enter

–up               –num-lock

Please enter: a  b  c  d  e  f

Now you can see something showing up on the screen of PC.

When run the following commands on pcDunio:

root@ubuntu:/home/ubuntu# ./a.out /dev/hidg1 m  #keyboard

mouse options:

–hold

–b1

–b2

–b3

mouse values:

Two signed numbers

–quit to close

Here is a little explanation:

–b1  10  10

when you are running this, it acts like left button of mouse.

–b2  1

when you are running this, it acts like right button of mouse.

–b3  -10  100

when you are running this, it acts like moving mouse around.

Step 4: Make Joystick a Mouse

The code is hosted at:

https://github.com/Pillar1989/arduino

Then run the program at output/test/usb.

Connect your joystick module to pin A4 and A5 on pcDuino, as there is a delay(500), it won’t work perfectly when moving it around.