2018年1月7日 星期日

USB HID Host on Linux : Based on SiLab C8051 Example Firmware, USBHIDtoUART



 This post implementation the Linux host of SiLab 's C8051 firmwar example code : USBHIDtoUART, to complementary my previous post, the same stuff on Windows. 
The code be :


#include <linux/limits.h>

//#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <errno.h>
#include <stdint.h>
#include <limits.h>

#include <linux/types.h>
#include <linux/input.h>
#include <linux/hidraw.h>

#include <pthread.h>

#include <sys/select.h>

#include <byteswap.h>

#include <signal.h>

#define DEVICE_VENDOR_ID     (0x10C4)
#define DEVICE_PRODUCT_ID     (0x8468)
#define DEVICE_VERSION_NUMER    (0x0000)


#define HID_BUFFER_SIZE      (64)
#define OUT_DATA_SIZE      (60 + 1)

int g_is_leave_thread = 0;

int is_this_device(char *p_device_name, uint16_t vendor_id, uint16_t product_id)
{
 int i;
 int res;
 int haw_hid_file_desc;
 struct hidraw_devinfo info;

 char buf[HID_BUFFER_SIZE];

 haw_hid_file_desc = open(p_device_name, O_RDWR|O_NONBLOCK);

 if(-1 == haw_hid_file_desc)
 {
  printf("open %s error:: %s \r\n", p_device_name, strerror(errno));
  return 0;
 }/*if */

 memset(&info, 0x0, sizeof(info));
 memset(buf, 0x0, sizeof(buf));

 /* Get Raw Info */
 res = ioctl(haw_hid_file_desc, HIDIOCGRAWINFO, &info);


 if (res < 0) 
 {
  perror("HIDIOCGRAWINFO");
  return 0;
 } 
 else 
 {
  printf("Raw Info:\n");

  /*bustype number defined in linux/input.h*/
  printf("\tbustype: 0x%02x\n", info.bustype);
  printf("\tvendor id : 0x%04hx\r\n", info.vendor);
  printf("\tproduct id : 0x%04hx\r\n", info.product);
 }/*if */


 if( BUS_USB != info.bustype 
  || vendor_id != (uint16_t)info.vendor 
  || product_id != (uint16_t)info.product )
 {
  printf("ERROR : device %s is not march \r\n", p_device_name);
  printf("target vendor_id = 0x%04x, product_id = 0x%04x\r\n", 
   (uint16_t)vendor_id, (uint16_t)product_id);

  close(haw_hid_file_desc); haw_hid_file_desc = -1;
  return 0;
 }/*if */


 /* Get Raw Name */
 res = ioctl(haw_hid_file_desc, HIDIOCGRAWNAME(256), buf);
 if (res < 0)
  perror("HIDIOCGRAWNAME");
 else
  printf("Raw Name: %s\n", buf);

 /* Get Physical Location */
 res = ioctl(haw_hid_file_desc, HIDIOCGRAWPHYS(256), buf);
 if (res < 0)
  perror("HIDIOCGRAWPHYS");
 else
  printf("Raw HID Phyical Location: %s\n", buf);

 int desc_size;
 res = ioctl(haw_hid_file_desc, HIDIOCGRDESCSIZE, &desc_size);
 if (res < 0)
  perror("HIDIOCGRDESCSIZE");
 else
  printf("Report Descriptor Size: %d\n", desc_size);

 /* Get Report Descriptor */
 struct hidraw_report_descriptor rpt_desc;
 rpt_desc.size = desc_size;
 res = ioctl(haw_hid_file_desc, HIDIOCGRDESC, &rpt_desc);
 if (res < 0) {
  perror("HIDIOCGRDESC");
 } else {
  printf("Report Descriptor:\n");
  for (i = 0; i < rpt_desc.size; i++)
   printf("0x%02x ", (uint8_t)rpt_desc.value[i]);
  puts("\n");
 }

 /*dummy in C8051 USBHIDtoUART firmwire functions: Get_Report/Set_Report
  F3xx_USB0_InterruptServiceRoutine.c */

 /* Set_Report for HIDIOCSFEATURE*/
 /* Set_Report for HIDIOCGFEATURE*/

 close(haw_hid_file_desc); haw_hid_file_desc = -1;

 return 1;

}/*is_this_device*/



void *read_customed_hid_routine(void *args)
{
 char *p_device_name;
 int raw_hid_file_desc;

 p_device_name = (char*)args;
 if(NULL == p_device_name)
  pthread_exit((void *)-1);

 
 raw_hid_file_desc = open(p_device_name, O_RDONLY);

 if(-1 == raw_hid_file_desc)
  pthread_exit((void *)-2);


 while(1)
 {
  fd_set set;
  struct timeval timeout_value;

  ssize_t read_size;
  char buffer[HID_BUFFER_SIZE];

  int ret;
  int i;

  if(0 != g_is_leave_thread)
   break;

  memset(&buffer[0], 0, sizeof(buffer));

  FD_ZERO(&set);
  FD_SET(raw_hid_file_desc, &set);

  timeout_value.tv_usec = 0;
  timeout_value.tv_sec = 2;

  ret = select(raw_hid_file_desc + 1, &set, NULL, NULL, &timeout_value);

  if(-1 == ret)
  {
   printf("open select error:: %s \r\n", strerror(errno));
   continue;
  }
  else if(0 == ret)
  {
   //printf("timeout\r\n");
   continue;
  }/*if */
  
  read_size = read(raw_hid_file_desc, &buffer[0], sizeof(buffer) );

#define IN_DATA_SIZE   (60 + 1)
#define IN_DATA     (0x01)

  if( IN_DATA_SIZE != read_size)
   continue;

  if(IN_DATA != buffer[0])
   continue;
  
  for(i = 0; i < buffer[1]; i++){
   printf("%c", buffer[2 + i]);
  }/*for i*/

 }/*while*/

 close(raw_hid_file_desc);
 pthread_exit((void *)0);
}/*read_customed_hid_routine*/


void *write_customed_hid_routine(void *args)
{
 char *p_device_name;
 int raw_hid_file_desc;

 char str_for_sending[HID_BUFFER_SIZE];

 p_device_name = (char*)args;
 if(NULL == p_device_name)
  pthread_exit((void *)-1);

 
 raw_hid_file_desc = open(p_device_name, O_WRONLY);


 if(-1 == raw_hid_file_desc)
  pthread_exit((void *)-2);

 snprintf(&str_for_sending[0], HID_BUFFER_SIZE, 
  "I want to eat cat meat\r\n");

 while(1)
 {
  ssize_t written_size;
  char buffer[HID_BUFFER_SIZE];

  if(0 != g_is_leave_thread)
   break;

#define OUT_DATA_SIZE      (60 + 1)
#define OUT_DATA       (0x02)

  buffer[0] = OUT_DATA;
  buffer[1] = strlen(&str_for_sending[0]);
  memcpy(&buffer[2], &str_for_sending[0], strlen(&str_for_sending[0]));

  written_size = write(raw_hid_file_desc, &buffer[0], OUT_DATA_SIZE);

  usleep(2*1000*1000);
 }/*while*/

 close(raw_hid_file_desc);
 pthread_exit((void *)0);
}/*write_customed_hid_routine*/


void interrupt_handler(int sig)
{
 g_is_leave_thread = 1;
}/*interrupt_handler*/


int main(int argc, char *argv[])
{
 int k;
 int haw_hid_file_desc;
 char device_name[PATH_MAX];

 unsigned int baudrate;

 pthread_t read_customed_hid_thread, write_customed_hid_thread;
 int thread_ret;

 signal(SIGINT, interrupt_handler); 

 baudrate = UINT_MAX;
 snprintf(&device_name[0], PATH_MAX, "/dev/hidraw0");


 k = 0;
 while(k < argc)
 {
  if(0 == strncmp(argv[k], "-d", strlen("-d")))
  {
   if(k + 1 < argc)
   {
    snprintf(&device_name[0], PATH_MAX, "%s", argv[k + 1]);
   }
   else
   {
    printf("ERROR : -d should be followed by"
     " raw hid device (/dev/hidrawXX)\r\n");
    return 1;
   }/*if */
  }/*if*/

  if(0 == strncmp(argv[k], "-baudrate", strlen("-baudrate")))
  {
   if(k + 1 < argc)
   {
    char *p_end;
    baudrate = strtol(argv[k + 1], &p_end, 10);
   }
   else
   {
    printf("ERROR : -baudrate should be followed by "
     "baudrate value\r\n");
    return 1;
   }/*if */
  }/*if*/

  k++;
 }/*while*/

 printf("raw hid device : %s\r\n", &device_name[0]);

 if(UINT_MAX != baudrate)
  printf("baud rate will be set as %u\r\n", baudrate);

 if(0 == is_this_device(&device_name[0], DEVICE_VENDOR_ID, DEVICE_PRODUCT_ID))
 {
  return 1;
 }

 if(UINT_MAX != baudrate)
 {
  int haw_hid_file_desc;
  uint8_t buffer[HID_BUFFER_SIZE];


  haw_hid_file_desc = open(&device_name[0], O_WRONLY);

  if(-1 == haw_hid_file_desc)
   return -1;

#define OUT_CONTROL     (0xFD)
  buffer[0] = OUT_CONTROL;
  baudrate = bswap_32(baudrate);
  memcpy(&buffer[1], &baudrate, sizeof(unsigned int));

  if(OUT_DATA_SIZE != write(haw_hid_file_desc, &buffer[0], OUT_DATA_SIZE))
  {
   printf("write error:: %s \r\n", strerror(errno));
  }/*if */

  close(haw_hid_file_desc); haw_hid_file_desc = -1;
 }/*UINT_MAX != baudrate*/


 g_is_leave_thread = 0;

 pthread_create(&read_customed_hid_thread, NULL, 
  read_customed_hid_routine, (void*)&device_name[0]);

 pthread_create(&write_customed_hid_thread, NULL, 
  write_customed_hid_routine, (void*)&device_name[0]);


 pthread_join(write_customed_hid_thread, (void**)&thread_ret);
 pthread_join(read_customed_hid_thread, (void**)&thread_ret);

 return 0;
}/*main*/

The correspondent Makefile be :


all:
 gcc main.c -lpthread -o c8051hid_to_uart_example_host
clean:
 rm -rf c8051hid_to_uart_example_host



The argument are:

  -baudrate baudrate_value : as the same, to setting the baudrate.
 -d device_location : to specify which device would be operated. by default, the customed hid device would be /dev/hidrawX (X is a number, for example, /dev/hidraw0)

Result of running this binary is ommited, for it is pretty the same as Windows version.







沒有留言:

張貼留言