mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2317 lines
48 KiB
2317 lines
48 KiB
/* |
||
* usb-usbip.c - USB Device Emulation (server side) by USBIP |
||
* |
||
* Copyright (C) 2017, 2018 g10 Code GmbH |
||
* Author: NIIBE Yutaka <[email protected]> |
||
* |
||
* This file is a part of Chopstx, a thread library for embedded. |
||
* |
||
* Chopstx is free software: you can redistribute it and/or modify it |
||
* under the terms of the GNU General Public License as published by |
||
* the Free Software Foundation, either version 3 of the License, or |
||
* (at your option) any later version. |
||
* |
||
* Chopstx is distributed in the hope that it will be useful, but |
||
* WITHOUT ANY WARRANTY; without even the implied warranty of |
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
* General Public License for more details. |
||
* |
||
* You should have received a copy of the GNU General Public License |
||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||
* |
||
*/ |
||
|
||
/* |
||
FIXME: |
||
RESET handling |
||
USB Shutdown |
||
Use reply structure of its own |
||
*/ |
||
|
||
#include <pthread.h> |
||
|
||
#include <unistd.h> |
||
|
||
#include <stdint.h> |
||
#include <stdio.h> |
||
#include <stdlib.h> |
||
#include <string.h> |
||
#include <errno.h> |
||
|
||
#include <arpa/inet.h> |
||
#include <sys/types.h> |
||
#include <sys/socket.h> |
||
|
||
#include <signal.h> |
||
#include <sys/eventfd.h> |
||
#include <poll.h> |
||
|
||
#include <usb_lld.h> |
||
#include <usb_lld_driver.h> |
||
|
||
#include <alloca.h> |
||
|
||
#include "sys.h" /* for debug */ |
||
|
||
static pthread_t tid_main; |
||
static pthread_t tid_usbip; |
||
|
||
#define USBIP_PORT 3240 |
||
|
||
#define CMD_REQ_LIST 0x01118005 |
||
#define CMD_REQ_ATTACH 0x01118003 |
||
#define CMD_URB_SUBMIT 0x00000001 |
||
#define CMD_URB_UNLINK 0x00000002 |
||
|
||
#define REP_URB_SUBMIT 0x00000003 |
||
#define REP_URB_UNLINK 0x00000004 |
||
|
||
struct usbip_msg_head { |
||
uint32_t cmd; |
||
uint32_t seq; |
||
}; |
||
|
||
struct usbip_usb_device { |
||
char path[256]; |
||
char busid[32]; |
||
|
||
uint32_t busnum; |
||
uint32_t devnum; |
||
uint32_t speed; |
||
|
||
uint16_t idVendor; |
||
uint16_t idProduct; |
||
uint16_t bcdDevice; |
||
|
||
uint8_t bDeviceClass; |
||
uint8_t bDeviceSubClass; |
||
uint8_t bDeviceProtocol; |
||
|
||
uint8_t bConfigurationValue; |
||
uint8_t bNumConfigurations; |
||
uint8_t bNumInterfaces; |
||
} __attribute__((packed)); |
||
|
||
struct urb { |
||
struct urb *next; |
||
struct urb *prev; |
||
|
||
uint16_t remain; |
||
char *data_p; |
||
|
||
pthread_t tid; |
||
uint32_t seq; |
||
uint32_t devid; |
||
uint32_t dir; |
||
uint32_t ep; |
||
uint32_t len; |
||
uint8_t setup[8]; |
||
char data[0]; |
||
}; |
||
|
||
/* |
||
* Only support a single device. |
||
*/ |
||
static struct usbip_usb_device usbip_usb_device; |
||
|
||
static struct urb *issue_get_desc (void); |
||
|
||
#define MY_BUS_ID "1-1\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" |
||
|
||
#define USBIP_DIR_OUT 0 |
||
#define USBIP_DIR_IN 1 |
||
|
||
static void |
||
refresh_usb_device (void) |
||
{ |
||
struct urb *urb = issue_get_desc (); |
||
char *desc = urb->data; |
||
|
||
memset (usbip_usb_device.path, 0, 256); |
||
strcpy (usbip_usb_device.path, |
||
"/sys/devices/pci0000:00/0000:00:01.1/usb1/1-1"); |
||
memcpy (usbip_usb_device.busid, MY_BUS_ID, 32); |
||
|
||
usbip_usb_device.busnum = 0; |
||
usbip_usb_device.devnum = 0; |
||
usbip_usb_device.speed = htonl (2); /* Full speed. */ |
||
|
||
/* USB descriptors are little endian. USBIP is network order. */ |
||
|
||
/* 0: size=18 (or follows more desc) */ |
||
/* 1: DEVICE_DESCRIPTOR */ |
||
/* 2: bcdUSB: ignore or use for speed? */ |
||
/* 4: bDeviceClass */ |
||
/* 5: bDeviceSubClass */ |
||
/* 6: bDeviceProtocol */ |
||
/* 7: bMaxPacketSize: ignore */ |
||
/* 8: idVendor */ |
||
/* 10: idProduct */ |
||
/* 12: bcdDevice */ |
||
/* 14: iManufacturer: ignore */ |
||
/* 15: iProduct: ignore */ |
||
/* 16: iSerialNumber: ignore */ |
||
/* 17: bNumConfigurations */ |
||
/* ... */ |
||
usbip_usb_device.idVendor = htons (((desc[9] << 8)|desc[8])); |
||
usbip_usb_device.idProduct = htons (((desc[11] << 8)|desc[10])); |
||
usbip_usb_device.bcdDevice = htons (((desc[13] << 8)|desc[12])); |
||
|
||
usbip_usb_device.bDeviceClass = desc[4]; |
||
usbip_usb_device.bDeviceSubClass = desc[5]; |
||
usbip_usb_device.bDeviceProtocol = desc[6]; |
||
|
||
usbip_usb_device.bConfigurationValue = 0; |
||
usbip_usb_device.bNumConfigurations = desc[17]; |
||
usbip_usb_device.bNumInterfaces = 0; |
||
free (urb); |
||
} |
||
|
||
#define USBIP_REPLY_HEADER_SIZE 8 |
||
#define DEVICE_INFO_SIZE (256+32+12+6+6) |
||
#define INTERFACE_INFO_SIZE 4 |
||
#define DEVICE_LIST_SIZE (DEVICE_INFO_SIZE*1) |
||
|
||
#define USBIP_REPLY_DEVICE_LIST "\x01\x11\x00\x05" |
||
#define USBIP_REPLY_ATTACH "\x01\x11\x00\x03" |
||
|
||
#define NETWORK_UINT32_ZERO "\x00\x00\x00\x00" |
||
#define NETWORK_UINT32_ONE "\x00\x00\x00\x01" |
||
#define NETWORK_UINT32_TWO "\x00\x00\x00\x02" |
||
#define NETWORK_UINT16_ZERO "\x00\x00" |
||
#define NETWORK_UINT16_ONE_ONE "\x01\x01" |
||
|
||
enum { |
||
USB_INTR_NONE = 0, |
||
USB_INTR_SETUP, |
||
USB_INTR_DATA_TRANSFER, |
||
USB_INTR_RESET, |
||
USB_INTR_SUSPEND, |
||
USB_INTR_SHUTDOWN |
||
}; |
||
|
||
struct usb_controller { |
||
pthread_mutex_t mutex; |
||
pthread_cond_t cond; |
||
uint8_t intr; |
||
uint8_t dir; |
||
uint8_t ep_num; |
||
}; |
||
|
||
static struct usb_controller usbc; |
||
|
||
static void |
||
notify_device (uint8_t intr, uint8_t ep_num, uint8_t dir) |
||
{ |
||
pthread_mutex_lock (&usbc.mutex); |
||
if (usbc.intr) |
||
pthread_cond_wait (&usbc.cond, &usbc.mutex); |
||
usbc.intr = intr; |
||
usbc.dir = (dir == USBIP_DIR_IN); |
||
usbc.ep_num = ep_num; |
||
pthread_kill (tid_main, SIGUSR1); |
||
pthread_mutex_unlock (&usbc.mutex); |
||
} |
||
|
||
|
||
static const char * |
||
list_devices (size_t *len_p) |
||
{ |
||
refresh_usb_device (); |
||
*len_p = sizeof (usbip_usb_device); |
||
return (const char *)&usbip_usb_device; |
||
} |
||
|
||
static const char * |
||
attach_device (char busid[32], size_t *len_p) |
||
{ |
||
*len_p = 0; |
||
if (memcmp (busid, MY_BUS_ID, 32) != 0) |
||
return NULL; |
||
|
||
// notify_device (USB_INTR_RESET, 0, 0); |
||
|
||
refresh_usb_device (); |
||
*len_p = sizeof (usbip_usb_device); |
||
return (const char *)&usbip_usb_device; |
||
} |
||
|
||
#define URB_DATA_SIZE 65535 |
||
|
||
struct usbip_msg_cmd { |
||
uint32_t devid; |
||
uint32_t dir; |
||
uint32_t ep; |
||
uint32_t flags; |
||
uint32_t len; |
||
uint32_t rsvd[3]; |
||
}; |
||
|
||
struct usbip_msg_rep { |
||
uint32_t devid; |
||
uint32_t dir; |
||
uint32_t ep; |
||
uint32_t status; |
||
uint32_t len; |
||
uint32_t rsvd[5]; |
||
}; |
||
|
||
static pthread_mutex_t urb_mutex; |
||
static struct urb *urb_list; |
||
|
||
static uint8_t usb_setup[8]; |
||
|
||
static int control_setup_transaction (struct urb *urb); |
||
static int control_write_data_transaction (char *buf, uint16_t count); |
||
static int control_write_status_transaction (void); |
||
static int control_read_data_transaction (char *buf, uint16_t count); |
||
static int control_read_status_transaction (void); |
||
|
||
enum { |
||
USB_STATE_DISABLED = 0, |
||
USB_STATE_STALL, |
||
USB_STATE_NAK, |
||
USB_STATE_READY, |
||
}; |
||
|
||
struct usb_control { |
||
pthread_mutex_t mutex; |
||
int eventfd; |
||
|
||
/* Device side: state, buf, len */ |
||
uint8_t state; |
||
uint8_t *buf; |
||
uint16_t len; |
||
|
||
/* Host controller side: urb */ |
||
struct urb *urb; |
||
}; |
||
|
||
static struct usb_control usbc_ep_in[8]; |
||
static struct usb_control usbc_ep_out[8]; |
||
#define usbc_ep0 usbc_ep_out[0] |
||
/* |
||
* usbc_ep_in[0] not used. |
||
*/ |
||
|
||
static int write_data_transaction (struct usb_control *usbc_p, |
||
int ep_num, char *buf, uint16_t count); |
||
static int read_data_transaction (struct usb_control *usbc_p, |
||
int ep_num, char *buf, uint16_t count); |
||
|
||
static int |
||
hc_handle_control_urb (struct urb *urb) |
||
{ |
||
int r; |
||
uint16_t count; |
||
uint16_t remain = urb->len; |
||
uint64_t l; |
||
|
||
if ((debug & DEBUG_USB)) |
||
puts ("hcu 0"); |
||
|
||
usbc_ep0.urb = urb; |
||
r = control_setup_transaction (urb); |
||
if (r < 0) |
||
goto error; |
||
|
||
if ((debug & DEBUG_USB)) |
||
puts ("hcu 1"); |
||
|
||
if (urb->dir == USBIP_DIR_OUT) |
||
{ /* Output from host to device. */ |
||
if ((debug & DEBUG_USB)) |
||
printf ("hcu: %d\n", r); |
||
|
||
while (r == 0) |
||
{ |
||
if (remain > 64) |
||
count = 64; |
||
else |
||
count = remain; |
||
|
||
read (usbc_ep0.eventfd, &l, sizeof (l)); |
||
r = control_write_data_transaction (urb->data_p, count); |
||
if (r < 0) |
||
break; |
||
|
||
urb->data_p += count; |
||
remain -= count; |
||
if (count < 64) |
||
break; |
||
} |
||
if (r >= 0) |
||
{ |
||
read (usbc_ep0.eventfd, &l, sizeof (l)); |
||
r = control_write_status_transaction (); |
||
} |
||
} |
||
else |
||
{ /* Input from device to host. */ |
||
if ((debug & DEBUG_USB)) |
||
puts ("hcu 2"); |
||
|
||
while (1) |
||
{ |
||
if (remain > 64) |
||
count = 64; |
||
else |
||
count = remain; |
||
|
||
read (usbc_ep0.eventfd, &l, sizeof (l)); |
||
r = control_read_data_transaction (urb->data_p, count); |
||
if (r < 0) |
||
break; |
||
|
||
if ((debug & DEBUG_USB)) |
||
puts ("hcu 3"); |
||
|
||
remain -= r; |
||
urb->data_p += r; |
||
if (r < 64) |
||
break; |
||
} |
||
|
||
if ((debug & DEBUG_USB)) |
||
puts ("hcu 4"); |
||
|
||
if (r >= 0) |
||
{ |
||
if ((debug & DEBUG_USB)) |
||
puts ("hcu 5"); |
||
|
||
read (usbc_ep0.eventfd, &l, sizeof (l)); |
||
r = control_read_status_transaction (); |
||
if (r >= 0) |
||
r = remain; |
||
} |
||
if ((debug & DEBUG_USB)) |
||
puts ("hcu 6"); |
||
} |
||
|
||
if (r < 0) |
||
{ |
||
error: |
||
if ((debug & DEBUG_USB)) |
||
printf ("hcu 7 %d\n", r); |
||
|
||
/* recovery. */ |
||
usbc_ep0.state = USB_STATE_READY; |
||
} |
||
else |
||
/* Wait until the device is ready to accept the SETUP token. */ |
||
read (usbc_ep0.eventfd, &l, sizeof (l)); |
||
|
||
if (urb->dir == USBIP_DIR_IN) |
||
{ |
||
if (r >= 0) /* R>0 means buf remained as unused. */ |
||
urb->len -= r; |
||
else |
||
urb->len = 0; |
||
r = 0; |
||
} |
||
else |
||
urb->len = 0; |
||
|
||
if ((debug & DEBUG_USB)) |
||
printf ("hu-next: %d (%d)\n", urb->len, urb->seq); |
||
|
||
usbc_ep0.urb = NULL; |
||
return r; |
||
} |
||
|
||
static void usbip_finish_urb (struct urb *urb, int r); |
||
|
||
static void |
||
usbip_handle_control_urb (struct urb *urb) |
||
{ |
||
int r = 0; |
||
r = hc_handle_control_urb (urb); |
||
usbip_finish_urb (urb, r); |
||
} |
||
|
||
static int |
||
usbip_handle_data_urb (struct urb *urb) |
||
{ |
||
int r; |
||
struct usb_control *usbc_p; |
||
|
||
if (urb->dir == USBIP_DIR_OUT) |
||
/* Output from host to device. */ |
||
usbc_p = &usbc_ep_out[urb->ep]; |
||
else |
||
/* Input from device to host. */ |
||
usbc_p = &usbc_ep_in[urb->ep]; |
||
|
||
pthread_mutex_lock (&usbc_p->mutex); |
||
if (usbc_p->state == USB_STATE_DISABLED |
||
|| usbc_p->state == USB_STATE_STALL) |
||
r = -EPIPE; |
||
else /* nak or ready */ |
||
{ |
||
if (usbc_p->urb == NULL) |
||
usbc_p->urb = urb; |
||
r = 0; |
||
} |
||
pthread_mutex_unlock (&usbc_p->mutex); |
||
return r; |
||
} |
||
|
||
static int fd; |
||
static int shutdown_notify_fd; |
||
static pthread_mutex_t fd_mutex; |
||
|
||
static void unlink_urb (struct urb *urb); |
||
|
||
static void |
||
usbip_finish_urb (struct urb *urb, int r) |
||
{ |
||
struct usbip_msg_head msg; |
||
struct usbip_msg_rep msg_rep; |
||
|
||
msg.cmd = htonl (REP_URB_SUBMIT); |
||
msg.seq = htonl (urb->seq); |
||
|
||
memset (&msg_rep, 0, sizeof (msg_rep)); |
||
msg_rep.len = htonl (urb->len); |
||
|
||
if ((debug & DEBUG_USB)) |
||
printf ("ufu: %d (%d)\n", r, urb->seq); |
||
|
||
if (r < 0) |
||
msg_rep.status = htonl (r); |
||
|
||
pthread_mutex_lock (&fd_mutex); |
||
if ((size_t)send (fd, &msg, sizeof (msg), 0) != sizeof (msg)) |
||
{ |
||
perror ("reply send"); |
||
} |
||
|
||
if ((size_t)send (fd, &msg_rep, sizeof (msg_rep), 0) != sizeof (msg_rep)) |
||
{ |
||
perror ("reply send"); |
||
} |
||
|
||
if (urb->dir == USBIP_DIR_IN && urb->len) |
||
{ |
||
if (send (fd, urb->data, urb->len, 0) != urb->len) |
||
{ |
||
perror ("reply send"); |
||
} |
||
} |
||
pthread_mutex_unlock (&fd_mutex); |
||
|
||
unlink_urb (urb); |
||
free (urb); |
||
} |
||
|
||
|
||
static int |
||
hc_handle_data_urb (struct usb_control *usbc_p) |
||
{ |
||
int r; |
||
uint16_t count; |
||
struct urb *urb = usbc_p->urb; |
||
|
||
if ((debug & DEBUG_USB)) |
||
puts ("hc_hdu 0"); |
||
|
||
if (urb->remain > 64) |
||
count = 64; |
||
else |
||
count = urb->remain; |
||
|
||
if (urb->dir == USBIP_DIR_OUT) |
||
{ /* Output from host to device. */ |
||
if ((debug & DEBUG_USB)) |
||
puts ("hc_hdu 1"); |
||
|
||
r = write_data_transaction (usbc_p, urb->ep, urb->data_p, count); |
||
if (r < 0) |
||
return r; |
||
|
||
urb->data_p += count; |
||
urb->remain -= count; |
||
|
||
if (urb->remain == 0 || count < 64) |
||
{ |
||
size_t len = urb->len - urb->remain; |
||
|
||
if ((debug & DEBUG_USB)) |
||
printf ("->data: %lu\n", len); |
||
|
||
// successfully finished |
||
if (len) |
||
{ |
||
char *s = alloca (len + 1); |
||
memcpy (s, urb->data, len); |
||
s[len] = 0; |
||
if ((debug & DEBUG_USB)) |
||
printf (" : %s\n", s); |
||
} |
||
return 0; |
||
} |
||
|
||
return 1; |
||
} |
||
else |
||
{ /* Input from device to host. */ |
||
if ((debug & DEBUG_USB)) |
||
puts ("hc_hdu 2"); |
||
|
||
r = read_data_transaction (usbc_p, urb->ep, urb->data_p, count); |
||
if (r < 0) |
||
return r; |
||
|
||
urb->remain -= r; |
||
urb->data_p += r; |
||
if (urb->remain == 0 || r < 64) |
||
{ |
||
size_t len = urb->len - urb->remain; |
||
|
||
if ((debug & DEBUG_USB)) |
||
printf ("<-data: %lu %d\n", len, r); |
||
|
||
// successfully finished |
||
if (len) |
||
{ |
||
char *s = alloca (len + 1); |
||
memcpy (s, urb->data, len); |
||
s[len] = 0; |
||
if ((debug & DEBUG_USB)) |
||
printf (" : %s\n", s); |
||
} |
||
urb->len = len; |
||
return 0; |
||
} |
||
|
||
return 1; |
||
} |
||
} |
||
|
||
#define USB_REQ_GET_DESCRIPTOR 0x06 |
||
|
||
static struct urb * |
||
issue_get_desc (void) |
||
{ |
||
struct urb *urb; |
||
|
||
urb = malloc (sizeof (struct urb) + 64); |
||
|
||
urb->next = urb->prev = urb; |
||
|
||
urb->setup[0] = 0x80; /* Type: GET, Standard, DEVICE */ |
||
urb->setup[1] = USB_REQ_GET_DESCRIPTOR; /* Request */ |
||
urb->setup[2] = 0; /* Value L: desc_index */ |
||
urb->setup[3] = 1; /* Value H: desc_type */ |
||
urb->setup[4] = 0; /* Index */ |
||
urb->setup[5] = 0; |
||
urb->setup[6] = 64; /* Length */ |
||
urb->setup[7] = 0; |
||
urb->data_p = urb->data; |
||
urb->seq = 0; |
||
urb->devid = 0; |
||
urb->dir = USBIP_DIR_IN; |
||
urb->ep = 0; |
||
urb->remain = urb->len = 64; |
||
hc_handle_control_urb (urb); |
||
return urb; |
||
} |
||
|
||
|
||
static void |
||
unlink_urb (struct urb *urb) |
||
{ |
||
if (urb_list == urb) |
||
{ |
||
if (urb->next == urb) |
||
urb_list = NULL; |
||
else |
||
urb_list = urb->next; |
||
} |
||
|
||
urb->next->prev = urb->prev; |
||
urb->prev->next = urb->next; |
||
} |
||
|
||
static void |
||
usbip_handle_urb (uint32_t seq) |
||
{ |
||
int r = 0; |
||
struct usbip_msg_head msg; |
||
struct usbip_msg_cmd msg_cmd; |
||
struct usbip_msg_rep msg_rep; |
||
struct urb *urb = NULL; |
||
|
||
if (recv (fd, (char *)&msg_cmd, sizeof (msg_cmd), 0) != sizeof (msg_cmd)) |
||
{ |
||
perror ("msg recv ctl"); |
||
r = -EINVAL; |
||
goto leave; |
||
} |
||
|
||
if (ntohl (msg_cmd.len) > URB_DATA_SIZE) |
||
{ |
||
perror ("msg len too long"); |
||
r = -EINVAL; |
||
goto leave; |
||
} |
||
|
||
urb = malloc (sizeof (struct urb) + ntohl (msg_cmd.len)); |
||
if (urb == NULL) |
||
{ |
||
perror ("URB alloc"); |
||
exit (1); |
||
} |
||
|
||
pthread_mutex_lock (&urb_mutex); |
||
if (urb_list == NULL) |
||
{ |
||
urb_list = urb; |
||
urb->next = urb->prev = urb; |
||
} |
||
else |
||
{ |
||
urb->next = urb_list; |
||
urb->prev = urb_list->prev; |
||
urb_list->prev->next = urb; |
||
urb_list->prev = urb; |
||
urb_list = urb; |
||
} |
||
pthread_mutex_unlock (&urb_mutex); |
||
|
||
urb->tid = 0; |
||
urb->seq = seq; |
||
urb->devid = ntohl (msg_cmd.devid); |
||
urb->dir = ntohl (msg_cmd.dir); |
||
urb->ep = ntohl (msg_cmd.ep); |
||
urb->remain = urb->len = ntohl (msg_cmd.len); |
||
urb->data_p = urb->data; |
||
|
||
if ((debug & DEBUG_USB)) |
||
printf ("URB: dir=%s, ep=%d, len=%d\n", urb->dir==USBIP_DIR_IN? "IN": "OUT", |
||
urb->ep, urb->len); |
||
|
||
if (recv (fd, (char *)urb->setup, sizeof (urb->setup), 0) != sizeof (urb->setup)) |
||
{ |
||
perror ("msg recv setup"); |
||
r = -EINVAL; |
||
goto leave; |
||
} |
||
|
||
if (urb->ep == 0) |
||
if ((debug & DEBUG_USB)) |
||
printf ("URB: %02x %02x %02x %02x %02x %02x %02x %02x\n", |
||
urb->setup[0], urb->setup[1], urb->setup[2], urb->setup[3], |
||
urb->setup[4], urb->setup[5], urb->setup[6], urb->setup[7]); |
||
|
||
if (urb->dir == USBIP_DIR_OUT && urb->len) |
||
{ |
||
if (recv (fd, urb->data, urb->len, 0) != urb->len) |
||
{ |
||
perror ("msg recv data"); |
||
r = -EINVAL; |
||
goto leave; |
||
} |
||
} |
||
|
||
if (urb->ep == 0) |
||
{ |
||
usbip_handle_control_urb (urb); |
||
return; |
||
} |
||
else |
||
{ |
||
r = usbip_handle_data_urb (urb); |
||
if (r == 0) |
||
return; |
||
} |
||
|
||
leave: |
||
msg.cmd = htonl (REP_URB_SUBMIT); |
||
msg.seq = htonl (urb->seq); |
||
|
||
memset (&msg_rep, 0, sizeof (msg_rep)); |
||
msg_rep.status = htonl (r); |
||
|
||
pthread_mutex_lock (&fd_mutex); |
||
if ((size_t)send (fd, &msg, sizeof (msg), 0) != sizeof (msg)) |
||
{ |
||
perror ("reply send"); |
||
} |
||
|
||
if ((size_t)send (fd, &msg_rep, sizeof (msg_rep), 0) != sizeof (msg_rep)) |
||
{ |
||
perror ("reply send"); |
||
} |
||
|
||
pthread_mutex_unlock (&fd_mutex); |
||
if (urb) |
||
{ |
||
pthread_mutex_lock (&urb_mutex); |
||
unlink_urb (urb); |
||
pthread_mutex_unlock (&urb_mutex); |
||
free (urb); |
||
} |
||
} |
||
|
||
|
||
static void |
||
usbip_send_reply (char *reply, int ok) |
||
{ |
||
char buf[8]; |
||
char *p = buf; |
||
|
||
memcpy (p, reply, 4); |
||
p += 4; |
||
if (ok) |
||
memcpy (p, NETWORK_UINT32_ZERO, 4); |
||
else |
||
memcpy (p, NETWORK_UINT32_ONE, 4); |
||
p += 4; |
||
|
||
if ((size_t)send (fd, buf, 8, 0) != 8) |
||
{ |
||
perror ("reply send"); |
||
} |
||
} |
||
|
||
static int attached = 0; |
||
|
||
|
||
static void |
||
unlink_urb_ep (struct urb *urb) |
||
{ |
||
struct usb_control *usbc_p; |
||
|
||
if (urb->dir == USBIP_DIR_OUT) |
||
usbc_p = &usbc_ep_out[urb->ep]; |
||
else |
||
usbc_p = &usbc_ep_in[urb->ep]; |
||
|
||
pthread_mutex_lock (&usbc_p->mutex); |
||
if (usbc_p->urb == urb) |
||
usbc_p->urb = NULL; |
||
pthread_mutex_unlock (&usbc_p->mutex); |
||
/* FIXME: rescan the list and register??? */ |
||
} |
||
|
||
static int |
||
usbip_process_cmd (void) |
||
{ |
||
struct usbip_msg_head msg; |
||
|
||
if (recv (fd, (char *)&msg, sizeof (msg), 0) != sizeof (msg)) |
||
{ |
||
if (errno) |
||
perror ("msg recv"); |
||
return -1; |
||
} |
||
|
||
msg.cmd = ntohl (msg.cmd); |
||
msg.seq = ntohl (msg.seq); |
||
|
||
if (msg.cmd == CMD_REQ_LIST) |
||
{ |
||
const char *device_list; |
||
size_t device_list_size; |
||
|
||
if ((debug & DEBUG_USB)) |
||
printf ("Device List\n"); |
||
|
||
if (attached) |
||
{ |
||
fprintf (stderr, "REQ list while attached\n"); |
||
return -1; |
||
} |
||
|
||
device_list = list_devices (&device_list_size); |
||
|
||
pthread_mutex_lock (&fd_mutex); |
||
usbip_send_reply (USBIP_REPLY_DEVICE_LIST, !!device_list); |
||
|
||
if (send (fd, NETWORK_UINT32_ONE, 4, 0) == 4 |
||
&& (size_t)send (fd, device_list, device_list_size, 0) == device_list_size) |
||
pthread_mutex_unlock (&fd_mutex); |
||
else |
||
{ |
||
pthread_mutex_unlock (&fd_mutex); |
||
perror ("list send"); |
||
return -1; |
||
} |
||
|
||
close (fd); |
||
return 1; |
||
} |
||
else if (msg.cmd == CMD_REQ_ATTACH) |
||
{ |
||
const char *attach; |
||
size_t attach_size; |
||
char busid[32]; |
||
|
||
if ((debug & DEBUG_USB)) |
||
printf ("Attach device\n"); |
||
|
||
if (attached) |
||
{ |
||
fprintf (stderr, "REQ attach while attached\n"); |
||
return -1; |
||
} |
||
|
||
if (recv (fd, busid, 32, 0) != 32) |
||
{ |
||
perror ("attach recv"); |
||
return -1; |
||
} |
||
|
||
attach = attach_device (busid, &attach_size); |
||
|
||
pthread_mutex_lock (&fd_mutex); |
||
usbip_send_reply (USBIP_REPLY_ATTACH, !!attach); |
||
if (attach |
||
&& (size_t)send (fd, attach, attach_size, 0) == attach_size) |
||
{ |
||
if ((debug & DEBUG_USB)) |
||
printf ("Attach device!\n"); |
||
attached = 1; |
||
pthread_mutex_unlock (&fd_mutex); |
||
} |
||
else |
||
{ |
||
pthread_mutex_unlock (&fd_mutex); |
||
perror ("attach send"); |
||
return -1; |
||
} |
||
} |
||
else if (msg.cmd == CMD_URB_SUBMIT) |
||
{ |
||
if (!attached) |
||
{ |
||
fprintf (stderr, "SUBMIT while not attached\n"); |
||
return -1; |
||
} |
||
|
||
if ((debug & DEBUG_USB)) |
||
printf ("URB SUBMIT! %d\n", msg.seq); |
||
usbip_handle_urb (msg.seq); |
||
} |
||
else if (msg.cmd == CMD_URB_UNLINK) |
||
{ |
||
struct usbip_msg_cmd msg_cmd; |
||
struct usbip_msg_rep msg_rep; |
||
uint32_t seq; |
||
struct urb *urb; |
||
char buf[8]; |
||
int found = 0; |
||
|
||
if (!attached) |
||
{ |
||
fprintf (stderr, "UNLINK while not attached\n"); |
||
return -1; |
||
} |
||
|
||
if (recv (fd, (char *)&msg_cmd, sizeof (msg_cmd), 0) != sizeof (msg_cmd)) |
||
{ |
||
perror ("msg recv ctl"); |
||
return -1; |
||
} |
||
|
||
if (recv (fd, buf, sizeof (buf), 0) != sizeof (buf)) |
||
{ |
||
perror ("msg recv setup"); |
||
return -1; |
||
} |
||
|
||
seq = ntohl (msg_cmd.flags); |
||
|
||
pthread_mutex_lock (&urb_mutex); |
||
if ((urb = urb_list)) |
||
{ |
||
do |
||
if (urb->seq == seq) |
||
{ |
||
found = 1; |
||
break; |
||
} |
||
else |
||
urb = urb->next; |
||
while (urb != urb_list); |
||
|
||
if (found) |
||
{ |
||
unlink_urb (urb); |
||
unlink_urb_ep (urb); |
||
free (urb); |
||
} |
||
} |
||
pthread_mutex_unlock (&urb_mutex); |
||
|
||
msg.cmd = htonl (REP_URB_UNLINK); |
||
msg.seq = htonl (msg.seq); |
||
|
||
memset (&msg_rep, 0, sizeof (msg_rep)); |
||
if (found) |
||
msg_rep.status = htonl(-ECONNRESET); |
||
|
||
if ((debug & DEBUG_USB)) |
||
printf ("URB UNLINK! %d: %s\n", seq, found?"o":"x"); |
||
|
||
pthread_mutex_lock (&fd_mutex); |
||
if ((size_t)send (fd, &msg, sizeof (msg), 0) != sizeof (msg)) |
||
{ |
||
perror ("reply send"); |
||
} |
||
|
||
if ((size_t)send (fd, &msg_rep, sizeof (msg_rep), 0) != sizeof (msg_rep)) |
||
{ |
||
perror ("reply send"); |
||
} |
||
|
||
pthread_mutex_unlock (&fd_mutex); |
||
} |
||
else |
||
{ |
||
fprintf (stderr, "Unknown command %08x, disconnecting.\n", msg.cmd); |
||
return -1; |
||
} |
||
|
||
return 0; |
||
} |
||
|
||
static void |
||
usbip_ep_ready (struct usb_control *usbc_p) |
||
{ |
||
uint64_t l; |
||
int r; |
||
|
||
if (!usbc_p->urb) |
||
{ |
||
if ((debug & DEBUG_USB)) |
||
puts ("???usbip_ep_ready"); |
||
|
||
return; |
||
} |
||
|
||
read (usbc_p->eventfd, &l, sizeof (l)); |
||
pthread_mutex_lock (&usbc_p->mutex); |
||
r = hc_handle_data_urb (usbc_p); |
||
if (r <= 0) |
||
{ |
||
struct urb *urb; |
||
int found = 0; |
||
|
||
urb = usbc_p->urb->prev; |
||
do |
||
{ |
||
if (urb->ep == usbc_p->urb->ep |
||
&& urb->dir == usbc_p->urb->dir) |
||
{ |
||
if (urb != usbc_p->urb) |
||
found = 1; |
||
break; |
||
} |
||
if (urb == urb_list) |
||
break; |
||
urb = urb->prev; |
||
} |
||
while (1); |
||
|
||
usbip_finish_urb (usbc_p->urb, r); |
||
|
||
if (found) |
||
usbc_p->urb = urb; |
||
else |
||
usbc_p->urb = NULL; |
||
} |
||
pthread_mutex_unlock (&usbc_p->mutex); |
||
} |
||
|
||
/* |
||
* In the USBIP protocol, client sends URB (USB Request Block) to this |
||
* server. |
||
* |
||
* This server acts/emulates as a USB host controller, and transforms |
||
* URB into packets to device, transforms packets from device into |
||
* URB. |
||
*/ |
||
static void * |
||
usbip_run_server (void *arg) |
||
{ |
||
int sock; |
||
struct sockaddr_in v4addr; |
||
const int one = 1; |
||
struct pollfd pollfds[16]; |
||
int i; |
||
int r = 0; |
||
|
||
(void)arg; |
||
|
||
pthread_mutex_init (&usbc.mutex, NULL); |
||
pthread_cond_init (&usbc.cond, NULL); |
||
|
||
pthread_mutex_init (&fd_mutex, NULL); |
||
pthread_mutex_init (&urb_mutex, NULL); |
||
|
||
if ((sock = socket (PF_INET, SOCK_STREAM, 0)) < 0) |
||
{ |
||
perror ("socket"); |
||
exit (1); |
||
} |
||
|
||
if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, |
||
(const char*)&one, sizeof (int)) < 0) |
||
perror ("WARN: setsockopt"); |
||
|
||
memset (&v4addr, 0, sizeof (v4addr)); |
||
v4addr.sin_family = AF_INET; |
||
v4addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); |
||
v4addr.sin_port = htons (USBIP_PORT); |
||
|
||
if (bind (sock, (const struct sockaddr *)&v4addr, sizeof (v4addr)) < 0) |
||
{ |
||
perror ("bind"); |
||
exit (1); |
||
} |
||
|
||
/* We only accept a connection from a single client. */ |
||
if (listen (sock, 1) < 0) |
||
{ |
||
perror ("listen"); |
||
exit (1); |
||
} |
||
|
||
pollfds[1].fd = shutdown_notify_fd; |
||
pollfds[1].events = POLLIN; |
||
pollfds[1].revents = 0; |
||
|
||
for (i = 1; i < 8; i++) |
||
{ |
||
pollfds[i*2].fd = usbc_ep_in[i].eventfd; |
||
pollfds[i*2].revents = 0; |
||
pollfds[i*2+1].fd = usbc_ep_out[i].eventfd; |
||
pollfds[i*2+1].revents = 0; |
||
} |
||
|
||
again: |
||
/* We don't care who is connecting. */ |
||
if ((fd = accept (sock, NULL, NULL)) < 0) |
||
{ |
||
perror ("accept"); |
||
exit (1); |
||
} |
||
|
||
/* Since EP0 is handled synchronously, we don't poll on |
||
* usbc_ep0.eventfd. We poll on socket and shutdown request. |
||
*/ |
||
pollfds[0].fd = fd; |
||
pollfds[0].events = POLLIN; |
||
pollfds[0].revents = 0; |
||
|
||
for (;;) |
||
{ |
||
for (i = 1; i < 8; i++) |
||
{ |
||
if (usbc_ep_in[i].urb) |
||
pollfds[i*2].events = POLLIN; |
||
else |
||
pollfds[i*2].events = 0; |
||
|
||
if (usbc_ep_out[i].urb) |
||
pollfds[i*2+1].events = POLLIN; |
||
else |
||
pollfds[i*2+1].events = 0; |
||
|
||
pollfds[i*2].revents = 0; |
||
pollfds[i*2+1].revents = 0; |
||
} |
||
|
||
if (poll (pollfds, 16, -1) < 0) |
||
{ |
||
if (errno == EINTR) |
||
continue; |
||
|
||
perror ("poll"); |
||
exit (1); |
||
} |
||
|
||
if ((pollfds[0].revents & POLLNVAL) |
||
|| (pollfds[0].revents & POLLERR)) |
||
{ |
||
fprintf (stderr, "Error on USBIP client socket: %d\n", |
||
pollfds[0].revents); |
||
exit (1); |
||
} |
||
if ((pollfds[0].revents & POLLIN)) |
||
{ |
||
r = usbip_process_cmd (); |
||
if (r < 0) |
||
break; |
||
if (r) |
||
goto again; |
||
} |
||
|
||
if ((pollfds[1].revents & POLLNVAL) |
||
|| (pollfds[1].revents & POLLERR)) |
||
{ |
||
fprintf (stderr, "Error on USBIP client socket: %d\n", |
||
pollfds[1].revents); |
||
exit (1); |
||
} |
||
if ((pollfds[1].revents & POLLIN)) |
||
{ |
||
/* Shutdown request */ |
||
r = 0; |
||
break; |
||
} |
||
|
||
for (i = 1; i < 8; i++) |
||
{ |
||
if ((pollfds[i*2].revents & POLLNVAL) |
||
|| (pollfds[i*2].revents & POLLERR)) |
||
{ |
||
fprintf (stderr, "Error on ENDP_IN eventfd: %d\n", i); |
||
exit (1); |
||
} |
||
if ((pollfds[i*2].revents & POLLIN)) |
||
{ |
||
if ((debug & DEBUG_USB)) |
||
puts ("poll in read"); |
||
|
||
usbip_ep_ready (&usbc_ep_in[i]); |
||
} |
||
|
||
if ((pollfds[i*2+1].revents & POLLNVAL) |
||
|| (pollfds[i*2+1].revents & POLLERR)) |
||
{ |
||
fprintf (stderr, "Error on ENDP_OUT eventfd: %d\n", i); |
||
exit (1); |
||
} |
||
if ((pollfds[i*2+1].revents & POLLIN)) |
||
{ |
||
if ((debug & DEBUG_USB)) |
||
puts ("poll out read"); |
||
|
||
usbip_ep_ready (&usbc_ep_out[i]); |
||
} |
||
} |
||
} |
||
|
||
{ |
||
struct urb *urb; |
||
|
||
pthread_mutex_lock (&urb_mutex); |
||
if ((urb = urb_list)) |
||
{ |
||
do |
||
{ |
||
unlink_urb (urb); |
||
unlink_urb_ep (urb); |
||
free (urb); |
||
} |
||
while ((urb = urb_list)); |
||
} |
||
pthread_mutex_unlock (&urb_mutex); |
||
} |
||
|
||
close (fd); |
||
close (sock); |
||
|
||
close (shutdown_notify_fd); |
||
close (usbc_ep0.eventfd); |
||
|
||
for (i = 1; i < 8; i++) |
||
{ |
||
close (usbc_ep_in[i].eventfd); |
||
close (usbc_ep_out[i].eventfd); |
||
} |
||
|
||
attached = 0; |
||
|
||
if (r < 0) |
||
/* It was detach request from client. */ |
||
exit (0); |
||
|
||
return NULL; |
||
} |
||
|
||
/** Control setup transaction. |
||
* |
||
* -->[SETUP]-->[DATA0]-->{ACK}-> Success |
||
* \-------> Error |
||
*/ |
||
static int |
||
control_setup_transaction (struct urb *urb) |
||
{ |
||
int r; |
||
|
||
pthread_mutex_lock (&usbc_ep0.mutex); |
||
if (usbc_ep0.state == USB_STATE_READY) |
||
{ |
||
if (urb->dir == USBIP_DIR_OUT |
||
&& urb->setup[6] == 0 && urb->setup[7] == 0) |
||
/* Control Write Transfer with no data stage. */ |
||
r = 1; |
||
else |
||
r = 0; |
||
|
||
usbc_ep0.state = USB_STATE_NAK; |
||
memcpy (usb_setup, urb->setup, sizeof (usb_setup)); |
||
notify_device (USB_INTR_SETUP, 0, urb->dir); |
||
} |
||
else if (usbc_ep0.state == USB_STATE_NAK) |
||
/* something wrong. */ |
||
r = -EAGAIN; |
||
else |
||
{ |
||
if ((debug & DEBUG_USB)) |
||
printf ("cst error %d\n", usbc_ep0.state); |
||
r = -EPIPE; |
||
} |
||
pthread_mutex_unlock (&usbc_ep0.mutex); |
||
return r; |
||
} |
||
|
||
/** Control WRITE transaction. |
||
* |
||
* -->[OUT]-->[DATA0]---->{ACK}---> Success |
||
* \--{NAK}---> again |
||
* \--{STALL}-> Error |
||
* \----------> Error |
||
*/ |
||
static int |
||
control_write_data_transaction (char *buf, uint16_t count) |
||
{ |
||
int r; |
||
|
||
pthread_mutex_lock (&usbc_ep0.mutex); |
||
if (usbc_ep0.state == USB_STATE_READY) |
||
{ |
||
if (usbc_ep0.len < count) |
||
{ |
||
if ((debug & DEBUG_USB)) |
||
printf ("*** usbc_ep0.len < count"); |
||
r = -EPIPE; |
||
usbc_ep0.state = USB_STATE_STALL; |
||
} |
||
else |
||
{ |
||
r = 0; |
||
usbc_ep0.state = USB_STATE_NAK; |
||
memcpy (usbc_ep0.buf, buf, count); |
||
usbc_ep0.len = count; |
||
notify_device (USB_INTR_DATA_TRANSFER, 0, USBIP_DIR_OUT); |
||
} |
||
} |
||
else if (usbc_ep0.state == USB_STATE_NAK) |
||
/* something wrong. */ |
||
r = -EAGAIN; |
||
else |
||
r = -EPIPE; |
||
pthread_mutex_unlock (&usbc_ep0.mutex); |
||
return r; |
||
} |
||
|
||
/** Status transaction for control WRITE. |
||
* zero-len |
||
* -->[IN]--->{DATA0}->[ACK]---> Success |
||
* \--{NAK}---> again |
||
* \--{STALL}-> Error |
||
* \----------> Error |
||
*/ |
||
static int |
||
control_write_status_transaction (void) |
||
{ |
||
int r; |
||
|
||
pthread_mutex_lock (&usbc_ep0.mutex); |
||
if (usbc_ep0.state == USB_STATE_READY) |
||
{ |
||
if ((debug & DEBUG_USB)) |
||
puts ("control_write_status_transaction"); |
||
|
||
if (usbc_ep0.len != 0) |
||
if ((debug & DEBUG_USB)) |
||
printf ("*** ACK length %d\n", usbc_ep0.len); |
||
usbc_ep0.state = USB_STATE_NAK; |
||
notify_device (USB_INTR_DATA_TRANSFER, 0, USBIP_DIR_IN); |
||
r = 0; |
||
} |
||
else if (usbc_ep0.state == USB_STATE_NAK) |
||
r = -EAGAIN; |
||
else |
||
r = -EPIPE; |
||
pthread_mutex_unlock (&usbc_ep0.mutex); |
||
return r; |
||
} |
||
|
||
/** Control READ transaction. |
||
* |
||
* -->[IN]-->{DATAx}---->[ACK]---> Success |
||
* | \---------> Error |
||
* \-{STALL}------------> Error |
||
* \-{NAK}--------------> again |
||
*/ |
||
static int |
||
control_read_data_transaction (char *buf, uint16_t count) |
||
{ |
||
int r; |
||
|
||
pthread_mutex_lock (&usbc_ep0.mutex); |
||
if (usbc_ep0.state == USB_STATE_READY) |
||
{ |
||
if (usbc_ep0.len > count) |
||
{ |
||
if ((debug & DEBUG_USB)) |
||
printf ("***c read: length %d > %d\n", usbc_ep0.len, count); |
||
} |
||
else |
||
count = usbc_ep0.len; |
||
|
||
memcpy (buf, usbc_ep0.buf, count); |
||
usbc_ep0.state = USB_STATE_NAK; |
||
notify_device (USB_INTR_DATA_TRANSFER, 0, USBIP_DIR_IN); |
||
r = count; |
||
} |
||
else if (usbc_ep0.state == USB_STATE_NAK) |
||
r = -EAGAIN; |
||
else |
||
r = -EPIPE; |
||
pthread_mutex_unlock (&usbc_ep0.mutex); |
||
return r; |
||
} |
||
|
||
/* Status transaction for control READ. |
||
* zero-len |
||
* -->[OUT]--->[DATA0]--->{ACK}---> Success |
||
* \--{NAK}---> again |
||
* \--{STALL}-> Error |
||
* \----------> Error |
||
*/ |
||
static int |
||
control_read_status_transaction (void) |
||
{ |
||
int r; |
||
|
||
pthread_mutex_lock (&usbc_ep0.mutex); |
||
if (usbc_ep0.state == USB_STATE_READY) |
||
{ |
||
usbc_ep0.len = 0; |
||
usbc_ep0.state = USB_STATE_NAK; |
||
notify_device (USB_INTR_DATA_TRANSFER, 0, USBIP_DIR_OUT); |
||
r = 0; |
||
} |
||
else if (usbc_ep0.state == USB_STATE_NAK) |
||
r = -EAGAIN; |
||
else |
||
r = -EPIPE; |
||
pthread_mutex_unlock (&usbc_ep0.mutex); |
||
return r; |
||
} |
||
|
||
#define USB_URB_TIMEOUT 1000000 /* nanosecond */ |
||
|
||
/* WRITE transaction |
||
* -->[OUT]--->[DATAx]--->{ACK}---> Success |
||
* \--{NAK}---> again |
||
* \--{STALL}-> Error |
||
* \----------> Error |
||
*/ |
||
static int |
||
write_data_transaction (struct usb_control *usbc_p, |
||
int ep_num, char *buf, uint16_t count) |
||
{ |
||
if (usbc_p->len < count) |
||
{ |
||
usbc_p->state = USB_STATE_STALL; |
||
return -EPIPE; |
||
} |
||
|
||
usbc_p->state = USB_STATE_NAK; |
||
memcpy (usbc_p->buf, buf, count); |
||
usbc_p->len = count; |
||
notify_device (USB_INTR_DATA_TRANSFER, ep_num, USBIP_DIR_OUT); |
||
return count; |
||
} |
||
|
||
/** READ transaction. |
||
* |
||
* -->[IN]-->{DATAx}---->[ACK]---> Success |
||
* | \---------> Error |
||
* \-{STALL}------------> Error |
||
* \-{NAK}--------------> again |
||
*/ |
||
static int |
||
read_data_transaction (struct usb_control *usbc_p, |
||
int ep_num, char *buf, uint16_t count) |
||
{ |
||
if (usbc_p->len > count) |
||
{ |
||
if ((debug & DEBUG_USB)) |
||
printf ("*** length %d > %d\n", usbc_p->len, count); |
||
} |
||
else |
||
count = usbc_p->len; |
||
|
||
usbc_p->state = USB_STATE_NAK; |
||
memcpy (buf, usbc_p->buf, count); |
||
notify_device (USB_INTR_DATA_TRANSFER, ep_num, USBIP_DIR_IN); |
||
return count; |
||
} |
||
|
||
extern void chx_handle_intr (uint32_t irq_num); |
||
|
||
|
||
static void |
||
usb_intr (int signum, siginfo_t *siginfo, void *arg) |
||
{ |
||
extern void chx_sigmask (ucontext_t *uc); |
||
|
||
ucontext_t *uc = arg; |
||
(void)signum; |
||
(void)siginfo; |
||
chx_handle_intr (INTR_REQ_USB); |
||
chx_sigmask (uc); |
||
} |
||
|
||
|
||
void |
||
usb_lld_init (struct usb_dev *dev, uint8_t feature) |
||
{ |
||
int r; |
||
sigset_t sigset; |
||
struct sigaction act; |
||
uint8_t ep; |
||
|
||
sigemptyset (&sigset); |
||
sigaddset (&sigset, SIGUSR1); |
||
sigaddset (&sigset, SIGALRM); |
||
|
||
/* |
||
* Launch the thread for USBIP. This maps to usb_lld_sys_init where |
||
* we initialize USB controller on MCU. |
||
*/ |
||
tid_main = pthread_self (); |
||
|
||
pthread_sigmask (SIG_BLOCK, &sigset, NULL); |
||
|
||
shutdown_notify_fd = eventfd (0, EFD_CLOEXEC); |
||
if (shutdown_notify_fd < 0) |
||
{ |
||
perror ("eventfd"); |
||
exit (1); |
||
} |
||
|
||
pthread_mutex_init (&usbc_ep0.mutex, NULL); |
||
usbc_ep0.urb = NULL; |
||
usbc_ep0.eventfd = eventfd (0, EFD_CLOEXEC); |
||
if (usbc_ep0.eventfd < 0) |
||
{ |
||
perror ("eventfd"); |
||
exit (1); |
||
} |
||
|
||
for (ep = 1; ep < 8; ep++) |
||
{ |
||
pthread_mutex_init (&usbc_ep_in[ep].mutex, NULL); |
||
pthread_mutex_init (&usbc_ep_out[ep].mutex, NULL); |
||
|
||
usbc_ep_in[ep].urb = NULL; |
||
usbc_ep_in[ep].eventfd = eventfd (0, EFD_CLOEXEC); |
||
if (usbc_ep_in[ep].eventfd < 0) |
||
{ |
||
perror ("eventfd"); |
||
exit (1); |
||
} |
||
|
||
usbc_ep_out[ep].urb = NULL; |
||
usbc_ep_out[ep].eventfd = eventfd (0, EFD_CLOEXEC); |
||
if (usbc_ep_out[ep].eventfd < 0) |
||
{ |
||
perror ("eventfd"); |
||
exit (1); |
||
} |
||
} |
||
|
||
r = pthread_create (&tid_usbip, NULL, usbip_run_server, NULL); |
||
if (r) |
||
{ |
||
fprintf (stderr, "usb_lld_init: %s\n", strerror (r)); |
||
exit (1); |
||
} |
||
|
||
act.sa_sigaction = usb_intr; |
||
sigfillset (&act.sa_mask); |
||
act.sa_flags = SA_SIGINFO|SA_RESTART; |
||
sigaction (SIGUSR1, &act, NULL); |
||
|
||
pthread_sigmask (SIG_UNBLOCK, &sigset, NULL); |
||
|
||
dev->configuration = 0; |
||
dev->feature = feature; |
||
dev->state = WAIT_SETUP; |
||
|
||
usbc_ep0.state = USB_STATE_READY; |
||
} |
||
|
||
|
||
void |
||
usb_lld_prepare_shutdown (void) |
||
{ |
||
} |
||
|
||
void |
||
usb_lld_shutdown (void) |
||
{ |
||
const uint64_t l = 1; |
||
|
||
/* |
||
* Tell USBIP server thread about shutdown. |
||
*/ |
||
write (shutdown_notify_fd, &l, sizeof (l)); |
||
pthread_join (tid_usbip, NULL); |
||
notify_device (USB_INTR_SHUTDOWN, 0, 0); |
||
} |
||
|
||
static void |
||
notify_hostcontroller (struct usb_control *usbc_p) |
||
{ |
||
const uint64_t l = 1; |
||
|
||
write (usbc_p->eventfd, &l, sizeof (l)); |
||
} |
||
|
||
|
||
#define USB_MAKE_EV(event) (event<<24) |
||
#define USB_MAKE_TXRX(ep_num,txrx,len) ((txrx? (1<<23):0)|(ep_num<<16)|len) |
||
|
||
static int handle_setup0 (struct usb_dev *dev); |
||
static int usb_handle_transfer (struct usb_dev *dev, uint8_t dir, uint8_t ep_num); |
||
|
||
int |
||
usb_lld_event_handler (struct usb_dev *dev) |
||
{ |
||
uint8_t intr; |
||
uint8_t dir; |
||
uint8_t ep_num; |
||
|
||
pthread_mutex_lock (&usbc.mutex); |
||
intr = usbc.intr; |
||
dir = usbc.dir; |
||
ep_num = usbc.ep_num; |
||
usbc.intr = USB_INTR_NONE; |
||
pthread_cond_signal (&usbc.cond); |
||
pthread_mutex_unlock (&usbc.mutex); |
||
|
||
if (intr == USB_INTR_RESET) |
||
return USB_MAKE_EV (USB_EVENT_DEVICE_RESET); |
||
else if (intr == USB_INTR_SETUP) |
||
return USB_MAKE_EV (handle_setup0 (dev)); |
||
else if (intr == USB_INTR_DATA_TRANSFER) |
||
return usb_handle_transfer (dev, dir, ep_num); |
||
else if (intr == USB_INTR_SHUTDOWN) |
||
{ |
||
if ((debug & DEBUG_USB)) |
||
puts ("USB shutdown"); |
||
} |
||
|
||
return USB_EVENT_OK; |
||
} |
||
|
||
static void handle_datastage_out (struct usb_dev *dev) |
||
{ |
||
struct ctrl_data *data_p = &dev->ctrl_data; |
||
uint32_t len; |
||
|
||
pthread_mutex_lock (&usbc_ep0.mutex); |
||
len = usbc_ep0.len; |
||
data_p->len -= len; |
||
data_p->addr += len; |
||
|
||
len = data_p->len; |
||
if (len > USB_MAX_PACKET_SIZE) |
||
len = USB_MAX_PACKET_SIZE; |
||
|
||
if (dev->ctrl_data.len == 0) |
||
{ |
||
dev->state = WAIT_STATUS_IN; |
||
usbc_ep0.buf = usb_setup; |
||
usbc_ep0.len = 0; |
||
usbc_ep0.state = USB_STATE_READY; |
||
} |
||
else |
||
{ |
||
dev->state = OUT_DATA; |
||
usbc_ep0.buf = data_p->addr; |
||
usbc_ep0.len = len; |
||
usbc_ep0.state = USB_STATE_READY; |
||
} |
||
|
||
notify_hostcontroller (&usbc_ep0); |
||
pthread_mutex_unlock (&usbc_ep0.mutex); |
||
} |
||
|
||
static void handle_datastage_in (struct usb_dev *dev) |
||
{ |
||
struct ctrl_data *data_p = &dev->ctrl_data; |
||
uint32_t len = USB_MAX_PACKET_SIZE; |
||
|
||
if ((data_p->len == 0) && (dev->state == LAST_IN_DATA)) |
||
{ |
||
pthread_mutex_lock (&usbc_ep0.mutex); |
||
|
||
if (data_p->require_zlp) |
||
{ |
||
data_p->require_zlp = 0; |
||
|
||
/* No more data to send. Send empty packet */ |
||
usbc_ep0.buf = usb_setup; |
||
usbc_ep0.len = 0; |
||
usbc_ep0.state = USB_STATE_READY; |
||
} |
||
else |
||
{ |
||
/* No more data to send, proceed to receive OUT acknowledge. */ |
||
dev->state = WAIT_STATUS_OUT; |
||
usbc_ep0.buf = usb_setup; |
||
usbc_ep0.len = 0; |
||
usbc_ep0.state = USB_STATE_READY; |
||
} |
||
|
||
notify_hostcontroller (&usbc_ep0); |
||
pthread_mutex_unlock (&usbc_ep0.mutex); |
||
return; |
||
} |
||
|
||
dev->state = (data_p->len <= len) ? LAST_IN_DATA : IN_DATA; |
||
|
||
if (len > data_p->len) |
||
len = data_p->len; |
||
|
||
pthread_mutex_lock (&usbc_ep0.mutex); |
||
usbc_ep0.buf = data_p->addr; |
||
usbc_ep0.len = len; |
||
usbc_ep0.state = USB_STATE_READY; |
||
data_p->len -= len; |
||
data_p->addr += len; |
||
notify_hostcontroller (&usbc_ep0); |
||
pthread_mutex_unlock (&usbc_ep0.mutex); |
||
} |
||
|
||
typedef int (*HANDLER) (struct usb_dev *dev); |
||
|
||
static int std_none (struct usb_dev *dev) |
||
{ |
||
(void)dev; |
||
return -1; |
||
} |
||
|
||
static uint16_t status_info; |
||
|
||
static int std_get_status (struct usb_dev *dev) |
||
{ |
||
struct device_req *arg = &dev->dev_req; |
||
uint8_t rcp = (arg->type & RECIPIENT); |
||
|
||
if (arg->value != 0 || arg->len != 2 || (arg->index >> 8) != 0 |
||
|| USB_SETUP_SET (arg->type)) |
||
return -1; |
||
|
||
if (rcp == DEVICE_RECIPIENT) |
||
{ |
||
if (arg->index == 0) |
||
{ |
||
/* Get Device Status */ |
||
uint8_t feature = dev->feature; |
||
|
||
/* Remote Wakeup enabled */ |
||
if ((feature & (1 << 5))) |
||
status_info |= 2; |
||
else |
||
status_info &= ~2; |
||
|
||
/* Bus-powered */ |
||
if ((feature & (1 << 6))) |
||
status_info |= 1; |
||
else /* Self-powered */ |
||
status_info &= ~1; |
||
|
||
return usb_lld_ctrl_send (dev, &status_info, 2); |
||
} |
||
} |
||
else if (rcp == INTERFACE_RECIPIENT) |
||
{ |
||
if (dev->configuration == 0) |
||
return -1; |
||
|
||
return USB_EVENT_GET_STATUS_INTERFACE; |
||
} |
||
else if (rcp == ENDPOINT_RECIPIENT) |
||
{ |
||
uint8_t ep_num = (arg->index & 0x0f); |
||
|
||
if ((arg->index & 0x70) || ep_num == ENDP0) |
||
return -1; |
||
|
||
if ((arg->index & 0x80)) |
||
{ |
||
if (usbc_ep_in[ep_num].state == USB_STATE_DISABLED) |
||
return -1; |
||
else if (usbc_ep_in[ep_num].state == USB_STATE_STALL) |
||
status_info |= 1; |
||
} |
||
else |
||
{ |
||
if (usbc_ep_out[ep_num].state == USB_STATE_DISABLED) |
||
return -1; |
||
else if (usbc_ep_out[ep_num].state == USB_STATE_STALL) |
||
status_info |= 1; |
||
} |
||
|
||
return usb_lld_ctrl_send (dev, &status_info, 2); |
||
} |
||
|
||
return -1; |
||
} |
||
|
||
static int std_clear_feature (struct usb_dev *dev) |
||
{ |
||
struct device_req *arg = &dev->dev_req; |
||
uint8_t rcp = arg->type & RECIPIENT; |
||
|
||
if (USB_SETUP_GET (arg->type)) |
||
return -1; |
||
|
||
if (rcp == DEVICE_RECIPIENT) |
||
{ |
||
if (arg->len != 0 || arg->index != 0) |
||
return -1; |
||
|
||
if (arg->value == FEATURE_DEVICE_REMOTE_WAKEUP) |
||
{ |
||
dev->feature &= ~(1 << 5); |
||
return USB_EVENT_CLEAR_FEATURE_DEVICE; |
||
} |
||
} |
||
else if (rcp == ENDPOINT_RECIPIENT) |
||
{ |
||
uint8_t ep_num = (arg->index & 0x0f); |
||
|
||
if (dev->configuration == 0) |
||
return -1; |
||
|
||
if (arg->len != 0 || (arg->index >> 8) != 0 |
||
|| arg->value != FEATURE_ENDPOINT_HALT || ep_num == ENDP0) |
||
return -1; |
||
|
||
if ((arg->index & 0x80)) |
||
{ |
||
if (usbc_ep_in[ep_num].state == USB_STATE_DISABLED) |
||
return -1; |
||
|
||
usbc_ep_in[ep_num].state = USB_STATE_NAK; |
||
} |
||
else |
||
{ |
||
if (usbc_ep_out[ep_num].state == USB_STATE_DISABLED) |
||
return -1; |
||
|
||
usbc_ep_out[ep_num].state = USB_STATE_NAK; |
||
} |
||
|
||
return USB_EVENT_CLEAR_FEATURE_ENDPOINT; |
||
} |
||
|
||
return -1; |
||
} |
||
|
||
static int std_set_feature (struct usb_dev *dev) |
||
{ |
||
struct device_req *arg = &dev->dev_req; |
||
uint8_t rcp = arg->type & RECIPIENT; |
||
|
||
if (USB_SETUP_GET (arg->type)) |
||
return -1; |
||
|
||
if (rcp == DEVICE_RECIPIENT) |
||
{ |
||
if (arg->len != 0 || arg->index != 0) |
||
return -1; |
||
|
||
if (arg->value == FEATURE_DEVICE_REMOTE_WAKEUP) |
||
{ |
||
dev->feature |= 1 << 5; |
||
return USB_EVENT_SET_FEATURE_DEVICE; |
||
} |
||
} |
||
else if (rcp == ENDPOINT_RECIPIENT) |
||
{ |
||
uint8_t ep_num = (arg->index & 0x0f); |
||
|
||
if (dev->configuration == 0) |
||
return -1; |
||
|
||
if (arg->len != 0 || (arg->index >> 8) != 0 |
||
|| arg->value != FEATURE_ENDPOINT_HALT || ep_num == ENDP0) |
||
return -1; |
||
|
||
if ((arg->index & 0x80)) |
||
{ |
||
if (usbc_ep_in[ep_num].state == USB_STATE_DISABLED) |
||
return -1; |
||
|
||
usbc_ep_in[ep_num].state = USB_STATE_STALL; |
||
} |
||
else |
||
{ |
||
if (usbc_ep_out[ep_num].state == USB_STATE_DISABLED) |
||
return -1; |
||
|
||
usbc_ep_out[ep_num].state = USB_STATE_STALL; |
||
} |
||
|
||
return USB_EVENT_SET_FEATURE_ENDPOINT; |
||
} |
||
|
||
return -1; |
||
} |
||
|
||
static int std_set_address (struct usb_dev *dev) |
||
{ |
||
struct device_req *arg = &dev->dev_req; |
||
uint8_t rcp = arg->type & RECIPIENT; |
||
|
||
if (USB_SETUP_GET (arg->type)) |
||
return -1; |
||
|
||
if (rcp == DEVICE_RECIPIENT && arg->len == 0 && arg->value <= 127 |
||
&& arg->index == 0 && dev->configuration == 0) |
||
return usb_lld_ctrl_ack (dev); |
||
|
||
return -1; |
||
} |
||
|
||
static int std_get_descriptor (struct usb_dev *dev) |
||
{ |
||
struct device_req *arg = &dev->dev_req; |
||
if (USB_SETUP_SET (arg->type)) |
||
return -1; |
||
|
||
return USB_EVENT_GET_DESCRIPTOR; |
||
} |
||
|
||
static int std_get_configuration (struct usb_dev *dev) |
||
{ |
||
struct device_req *arg = &dev->dev_req; |
||
uint8_t rcp = arg->type & RECIPIENT; |
||
|
||
if (USB_SETUP_SET (arg->type)) |
||
return -1; |
||
|
||
if (arg->value != 0 || arg->index != 0 || arg->len != 1) |
||
return -1; |
||
|
||
if (rcp == DEVICE_RECIPIENT) |
||
return usb_lld_ctrl_send (dev, &dev->configuration, 1); |
||
|
||
return -1; |
||
} |
||
|
||
static int std_set_configuration (struct usb_dev *dev) |
||
{ |
||
struct device_req *arg = &dev->dev_req; |
||
uint8_t rcp = arg->type & RECIPIENT; |
||
|
||
if (USB_SETUP_GET (arg->type)) |
||
return -1; |
||
|
||
if (arg->index != 0 || arg->len != 0) |
||
return -1; |
||
|
||
if (rcp == DEVICE_RECIPIENT) |
||
return USB_EVENT_SET_CONFIGURATION; |
||
|
||
return -1; |
||
} |
||
|
||
static int std_get_interface (struct usb_dev *dev) |
||
{ |
||
struct device_req *arg = &dev->dev_req; |
||
uint8_t rcp = arg->type & RECIPIENT; |
||
|
||
if (USB_SETUP_SET (arg->type)) |
||
return -1; |
||
|
||
if (arg->value != 0 || (arg->index >> 8) != 0 || arg->len != 1) |
||
return -1; |
||
|
||
if (dev->configuration == 0) |
||
return -1; |
||
|
||
if (rcp == INTERFACE_RECIPIENT) |
||
return USB_EVENT_GET_INTERFACE; |
||
|
||
return -1; |
||
} |
||
|
||
static int std_set_interface (struct usb_dev *dev) |
||
{ |
||
struct device_req *arg = &dev->dev_req; |
||
uint8_t rcp = arg->type & RECIPIENT; |
||
|
||
if (USB_SETUP_GET (arg->type) || rcp != INTERFACE_RECIPIENT |
||
|| arg->len != 0 || (arg->index >> 8) != 0 |
||
|| (arg->value >> 8) != 0 || dev->configuration == 0) |
||
return -1; |
||
|
||
return USB_EVENT_SET_INTERFACE; |
||
} |
||
|
||
|
||
static int |
||
handle_setup0 (struct usb_dev *dev) |
||
{ |
||
uint8_t req_no; |
||
HANDLER handler; |
||
|
||
dev->dev_req.type = usb_setup[0]; |
||
dev->dev_req.request = req_no = usb_setup[1]; |
||
dev->dev_req.value = (usb_setup[3] << 8) + usb_setup[2]; |
||
dev->dev_req.index = (usb_setup[5] << 8) + usb_setup[4]; |
||
dev->dev_req.len = (usb_setup[7] << 8) + usb_setup[6]; |
||
|
||
dev->ctrl_data.addr = NULL; |
||
dev->ctrl_data.len = 0; |
||
dev->ctrl_data.require_zlp = 0; |
||
|
||
if ((dev->dev_req.type & REQUEST_TYPE) == STANDARD_REQUEST) |
||
{ |
||
int r; |
||
|
||
switch (req_no) |
||
{ |
||
case 0: handler = std_get_status; break; |
||
case 1: handler = std_clear_feature; break; |
||
case 3: handler = std_set_feature; break; |
||
case 5: handler = std_set_address; break; |
||
case 6: handler = std_get_descriptor; break; |
||
case 8: handler = std_get_configuration; break; |
||
case 9: handler = std_set_configuration; break; |
||
case 10: handler = std_get_interface; break; |
||
case 11: handler = std_set_interface; break; |
||
default: handler = std_none; break; |
||
} |
||
|
||
if ((r = (*handler) (dev)) < 0) |
||
{ |
||
usb_lld_ctrl_error (dev); |
||
return USB_EVENT_OK; |
||
} |
||
else |
||
return r; |
||
} |
||
else |
||
return USB_EVENT_CTRL_REQUEST; |
||
} |
||
|
||
static int handle_in0 (struct usb_dev *dev) |
||
{ |
||
int r = 0; |
||
|
||
if (dev->state == IN_DATA || dev->state == LAST_IN_DATA) |
||
handle_datastage_in (dev); |
||
else if (dev->state == WAIT_STATUS_IN) |
||
{ |
||
if ((dev->dev_req.request == SET_ADDRESS) && |
||
((dev->dev_req.type & (REQUEST_TYPE | RECIPIENT)) |
||
== (STANDARD_REQUEST | DEVICE_RECIPIENT))) |
||
{ |
||
/* XXX: record the assigned address of this device??? */ |
||
if ((debug & DEBUG_USB)) |
||
printf ("Set Address: %d\n", dev->dev_req.value); |
||
|
||
r = USB_EVENT_DEVICE_ADDRESSED; |
||
} |
||
else |
||
r = USB_EVENT_CTRL_WRITE_FINISH; |
||
dev->state = WAIT_SETUP; |
||
pthread_mutex_lock (&usbc_ep0.mutex); |
||
usbc_ep0.buf = usb_setup; |
||
usbc_ep0.len = 8; |
||
usbc_ep0.state = USB_STATE_READY; |
||
notify_hostcontroller (&usbc_ep0); |
||
pthread_mutex_unlock (&usbc_ep0.mutex); |
||
} |
||
else |
||
{ |
||
if ((debug & DEBUG_USB)) |
||
puts ("handle_in0 error"); |
||
|
||
dev->state = STALLED; |
||
pthread_mutex_lock (&usbc_ep0.mutex); |
||
usbc_ep0.state = USB_STATE_STALL; |
||
notify_hostcontroller (&usbc_ep0); |
||
pthread_mutex_unlock (&usbc_ep0.mutex); |
||
} |
||
|
||
return r; |
||
} |
||
|
||
static void handle_out0 (struct usb_dev *dev) |
||
{ |
||
if (dev->state == OUT_DATA) |
||
/* Usual case. */ |
||
handle_datastage_out (dev); |
||
else if (dev->state == WAIT_STATUS_OUT) |
||
{ |
||
/* |
||
* Control READ transfer finished by ZLP. |
||
* Leave ENDP0 status RX_NAK, TX_NAK. |
||
*/ |
||
dev->state = WAIT_SETUP; |
||
pthread_mutex_lock (&usbc_ep0.mutex); |
||
usbc_ep0.buf = usb_setup; |
||
usbc_ep0.len = 8; |
||
usbc_ep0.state = USB_STATE_READY; |
||
notify_hostcontroller (&usbc_ep0); |
||
pthread_mutex_unlock (&usbc_ep0.mutex); |
||
} |
||
else |
||
{ |
||
/* |
||
* dev->state == IN_DATA || dev->state == LAST_IN_DATA |
||
* (Host aborts the transfer before finish) |
||
* Or else, unexpected state. |
||
* STALL the endpoint, until we receive the next SETUP token. |
||
*/ |
||
if ((debug & DEBUG_USB)) |
||
puts ("handle_out0 error"); |
||
|
||
dev->state = STALLED; |
||
pthread_mutex_lock (&usbc_ep0.mutex); |
||
usbc_ep0.state = USB_STATE_STALL; |
||
notify_hostcontroller (&usbc_ep0); |
||
pthread_mutex_unlock (&usbc_ep0.mutex); |
||
} |
||
} |
||
|
||
|
||
static int |
||
usb_handle_transfer (struct usb_dev *dev, uint8_t dir, uint8_t ep_num) |
||
{ |
||
if (ep_num == 0) |
||
{ |
||
if (dir) |
||
return USB_MAKE_EV (handle_in0 (dev)); |
||
else |
||
{ |
||
handle_out0 (dev); |
||
return USB_EVENT_OK; |
||
} |
||
} |
||
else |
||
{ |
||
uint16_t len; |
||
|
||
if (dir) |
||
{ |
||
len = usbc_ep_in[ep_num].len; |
||
return USB_MAKE_TXRX (ep_num, 1, len); |
||
} |
||
else |
||
{ |
||
len = usbc_ep_out[ep_num].len; |
||
return USB_MAKE_TXRX (ep_num, 0, len); |
||
} |
||
} |
||
|
||
return USB_EVENT_OK; |
||
} |
||
|
||
int |
||
usb_lld_ctrl_ack (struct usb_dev *dev) |
||
{ |
||
dev->state = WAIT_STATUS_IN; |
||
pthread_mutex_lock (&usbc_ep0.mutex); |
||
usbc_ep0.buf = usb_setup; |
||
usbc_ep0.len = 0; |
||
usbc_ep0.state = USB_STATE_READY; |
||
notify_hostcontroller (&usbc_ep0); |
||
pthread_mutex_unlock (&usbc_ep0.mutex); |
||
return USB_EVENT_OK; |
||
} |
||
|
||
int |
||
usb_lld_ctrl_recv (struct usb_dev *dev, void *p, size_t len) |
||
{ |
||
struct ctrl_data *data_p = &dev->ctrl_data; |
||
data_p->addr = p; |
||
data_p->len = len; |
||
dev->state = OUT_DATA; |
||
if (len > USB_MAX_PACKET_SIZE) |
||
len = USB_MAX_PACKET_SIZE; |
||
pthread_mutex_lock (&usbc_ep0.mutex); |
||
usbc_ep0.state = USB_STATE_READY; |
||
usbc_ep0.buf = p; |
||
usbc_ep0.len = len; |
||
notify_hostcontroller (&usbc_ep0); |
||
pthread_mutex_unlock (&usbc_ep0.mutex); |
||
return USB_EVENT_OK; |
||
} |
||
|
||
int |
||
usb_lld_ctrl_send (struct usb_dev *dev, const void *buf, size_t buflen) |
||
{ |
||
struct ctrl_data *data_p = &dev->ctrl_data; |
||
uint32_t len_asked = dev->dev_req.len; |
||
uint32_t len; |
||
|
||
data_p->addr = (void *)buf; |
||
data_p->len = buflen; |
||
|
||
/* Restrict the data length to be the one host asks for */ |
||
if (data_p->len >= len_asked) |
||
data_p->len = len_asked; |
||
/* ZLP is only required when host doesn't expect the end of packets. */ |
||
else if (data_p->len != 0 && (data_p->len % USB_MAX_PACKET_SIZE) == 0) |
||
data_p->require_zlp = 1; |
||
|
||
if (data_p->len < USB_MAX_PACKET_SIZE) |
||
{ |
||
len = data_p->len; |
||
dev->state = LAST_IN_DATA; |
||
} |
||
else |
||
{ |
||
len = USB_MAX_PACKET_SIZE; |
||
dev->state = IN_DATA; |
||
} |
||
|
||
pthread_mutex_lock (&usbc_ep0.mutex); |
||
usbc_ep0.buf = data_p->addr; |
||
usbc_ep0.len = len; |
||
usbc_ep0.state = USB_STATE_READY; |
||
data_p->len -= len; |
||
data_p->addr += len; |
||
notify_hostcontroller (&usbc_ep0); |
||
pthread_mutex_unlock (&usbc_ep0.mutex); |
||
return USB_EVENT_OK; |
||
} |
||
|
||
uint8_t |
||
usb_lld_current_configuration (struct usb_dev *dev) |
||
{ |
||
return dev->configuration; |
||
} |
||
|
||
|
||
void |
||
usb_lld_ctrl_error (struct usb_dev *dev) |
||
{ |
||
if ((debug & DEBUG_USB)) |
||
puts ("ctrl_error"); |
||
|
||
dev->state = STALLED; |
||
pthread_mutex_lock (&usbc_ep0.mutex); |
||
usbc_ep0.state = USB_STATE_STALL; |
||
notify_hostcontroller (&usbc_ep0); |
||
pthread_mutex_unlock (&usbc_ep0.mutex); |
||
} |
||
|
||
/* FIXME: ??? */ |
||
void |
||
usb_lld_reset (struct usb_dev *dev, uint8_t feature) |
||
{ |
||
usb_lld_set_configuration (dev, 0); |
||
dev->feature = feature; |
||
usbc_ep0.state = USB_STATE_READY; |
||
} |
||
|
||
void |
||
usb_lld_set_configuration (struct usb_dev *dev, uint8_t config) |
||
{ |
||
dev->configuration = config; |
||
} |
||
|
||
|
||
void |
||
usb_lld_setup_endp (struct usb_dev *dev, int ep_num, int rx_en, int tx_en) |
||
{ |
||
(void)dev; |
||
|
||
if (ep_num == 0) |
||
return; |
||
|
||
if (rx_en) |
||
{ |
||
usbc_ep_out[ep_num].buf = NULL; |
||
usbc_ep_out[ep_num].len = 0; |
||
usbc_ep_out[ep_num].state = USB_STATE_NAK; |
||
} |
||
|
||
if (tx_en) |
||
{ |
||
usbc_ep_in[ep_num].buf = NULL; |
||
usbc_ep_in[ep_num].len = 0; |
||
usbc_ep_in[ep_num].state = USB_STATE_NAK; |
||
} |
||
} |
||
|
||
|
||
void |
||
usb_lld_stall_tx (int ep_num) |
||
{ |
||
struct usb_control *usbc_p = &usbc_ep_in[ep_num]; |
||
|
||
pthread_mutex_lock (&usbc_p->mutex); |
||
usbc_p->state = USB_STATE_STALL; |
||
notify_hostcontroller (usbc_p); |
||
pthread_mutex_unlock (&usbc_p->mutex); |
||
|
||
if ((debug & DEBUG_USB)) |
||
printf ("stall tx %d\n", ep_num); |
||
} |
||
|
||
void |
||
usb_lld_stall_rx (int ep_num) |
||
{ |
||
struct usb_control *usbc_p = &usbc_ep_out[ep_num]; |
||
|
||
pthread_mutex_lock (&usbc_p->mutex); |
||
usbc_p->state = USB_STATE_STALL; |
||
notify_hostcontroller (usbc_p); |
||
pthread_mutex_unlock (&usbc_p->mutex); |
||
if ((debug & DEBUG_USB)) |
||
printf ("stall rx %d\n", ep_num); |
||
} |
||
|
||
void |
||
usb_lld_rx_enable_buf (int ep_num, void *buf, size_t len) |
||
{ |
||
struct usb_control *usbc_p = &usbc_ep_out[ep_num]; |
||
|
||
pthread_mutex_lock (&usbc_p->mutex); |
||
usbc_p->state = USB_STATE_READY; |
||
usbc_p->buf = buf; |
||
usbc_p->len = len; |
||
|
||
notify_hostcontroller (usbc_p); |
||
pthread_mutex_unlock (&usbc_p->mutex); |
||
if ((debug & DEBUG_USB)) |
||
printf ("usb_lld_rx_enable_buf: %d\n", ep_num); |
||
} |
||
|
||
|
||
void |
||
usb_lld_tx_enable_buf (int ep_num, const void *buf, size_t len) |
||
{ |
||
struct usb_control *usbc_p = &usbc_ep_in[ep_num]; |
||
|
||
pthread_mutex_lock (&usbc_p->mutex); |
||
usbc_p->state = USB_STATE_READY; |
||
usbc_p->buf = (void *)buf; |
||
usbc_p->len = len; |
||
notify_hostcontroller (usbc_p); |
||
pthread_mutex_unlock (&usbc_p->mutex); |
||
if ((debug & DEBUG_USB)) |
||
printf ("usb_lld_tx_enable_buf: %d %ld\n", ep_num, len); |
||
}
|
||
|