Compare commits

..

1 Commits

Author SHA1 Message Date
Samuel Brian
896b6757f9 usb-host: support devices with sparse/non-sequential USB interfaces
Some USB devices have sparse interface numbering which is not able to be
passthroughed.
For example, the Sierra Wireless MC7455/MC7430:

  # lsusb  -D /dev/bus/usb/003/003 | egrep '1199|9071|bNumInterfaces|bInterfaceNumber'
  Device: ID 1199:9071 Sierra Wireless, Inc.
    idVendor           0x1199 Sierra Wireless, Inc.
    idProduct          0x9071
      bNumInterfaces          5
        bInterfaceNumber        0
        bInterfaceNumber        2
        bInterfaceNumber        3
        bInterfaceNumber        8
        bInterfaceNumber       10

In this case, the interface numbers are 0, 2, 3, 8, 10 and not the
0, 1, 2, 3, 4 that QEMU tries to claim.

This change allows sparse USB interface numbering.
Instead of only claiming the interfaces in the range reported by the USB
device through bNumInterfaces, QEMU attempts to claim all possible
interfaces.

v2 to fix broken v1 patch formatting.
v3 to fix indentation.

Signed-off-by: Samuel Brian <sam.brian@accelerated.com>
Message-id: 20170613234039.27201-1-sam.brian@accelerated.com
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2017-06-21 15:30:08 +02:00
12 changed files with 171 additions and 148 deletions

View File

@@ -1107,7 +1107,7 @@ static void usb_host_detach_kernel(USBHostDevice *s)
if (rc != 0) {
return;
}
for (i = 0; i < conf->bNumInterfaces; i++) {
for (i = 0; i < USB_MAX_INTERFACES; i++) {
rc = libusb_kernel_driver_active(s->dh, i);
usb_host_libusb_error("libusb_kernel_driver_active", rc);
if (rc != 1) {
@@ -1130,7 +1130,7 @@ static void usb_host_attach_kernel(USBHostDevice *s)
if (rc != 0) {
return;
}
for (i = 0; i < conf->bNumInterfaces; i++) {
for (i = 0; i < USB_MAX_INTERFACES; i++) {
if (!s->ifs[i].detached) {
continue;
}
@@ -1145,7 +1145,7 @@ static int usb_host_claim_interfaces(USBHostDevice *s, int configuration)
{
USBDevice *udev = USB_DEVICE(s);
struct libusb_config_descriptor *conf;
int rc, i;
int rc, i, claimed;
for (i = 0; i < USB_MAX_INTERFACES; i++) {
udev->altsetting[i] = 0;
@@ -1164,14 +1164,19 @@ static int usb_host_claim_interfaces(USBHostDevice *s, int configuration)
return USB_RET_STALL;
}
for (i = 0; i < conf->bNumInterfaces; i++) {
claimed = 0;
for (i = 0; i < USB_MAX_INTERFACES; i++) {
trace_usb_host_claim_interface(s->bus_num, s->addr, configuration, i);
rc = libusb_claim_interface(s->dh, i);
usb_host_libusb_error("libusb_claim_interface", rc);
if (rc != 0) {
return USB_RET_STALL;
if (rc == 0) {
s->ifs[i].claimed = true;
if (++claimed == conf->bNumInterfaces) {
break;
}
}
s->ifs[i].claimed = true;
}
if (claimed != conf->bNumInterfaces) {
return USB_RET_STALL;
}
udev->ninterfaces = conf->bNumInterfaces;
@@ -1183,10 +1188,9 @@ static int usb_host_claim_interfaces(USBHostDevice *s, int configuration)
static void usb_host_release_interfaces(USBHostDevice *s)
{
USBDevice *udev = USB_DEVICE(s);
int i, rc;
for (i = 0; i < udev->ninterfaces; i++) {
for (i = 0; i < USB_MAX_INTERFACES; i++) {
if (!s->ifs[i].claimed) {
continue;
}

View File

@@ -8,21 +8,6 @@
extern EGLDisplay *qemu_egl_display;
extern EGLConfig qemu_egl_config;
typedef struct egl_fb {
int width;
int height;
GLuint texture;
GLuint framebuffer;
bool delete_texture;
} egl_fb;
void egl_fb_destroy(egl_fb *fb);
void egl_fb_setup_default(egl_fb *fb, int width, int height);
void egl_fb_create_for_tex(egl_fb *fb, int width, int height, GLuint texture);
void egl_fb_create_new_tex(egl_fb *fb, int width, int height);
void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip);
void egl_fb_read(void *dst, egl_fb *src);
#ifdef CONFIG_OPENGL_DMABUF
extern int qemu_egl_rn_fd;

View File

@@ -52,8 +52,8 @@ typedef struct VirtualGfxConsole {
EGLSurface esurface;
int glupdates;
int x, y, w, h;
egl_fb guest_fb;
egl_fb win_fb;
GLuint tex_id;
GLuint fbo_id;
bool y0_top;
bool scanout_mode;
#endif

View File

@@ -7,10 +7,6 @@
#include <SDL.h>
#include <SDL_syswm.h>
#ifdef CONFIG_OPENGL
# include "ui/egl-helpers.h"
#endif
struct sdl2_console {
DisplayChangeListener dcl;
DisplaySurface *surface;
@@ -27,8 +23,8 @@ struct sdl2_console {
SDL_GLContext winctx;
#ifdef CONFIG_OPENGL
ConsoleGLState *gls;
egl_fb guest_fb;
egl_fb win_fb;
GLuint tex_id;
GLuint fbo_id;
bool y0_top;
bool scanout_mode;
#endif

View File

@@ -1579,13 +1579,36 @@ bool dpy_gfx_check_format(QemuConsole *con,
return true;
}
/*
* Safe DPY refresh for TCG guests. We use the exclusive mechanism to
* ensure the TCG vCPUs are quiescent so we can avoid races between
* dirty page tracking for direct frame-buffer access by the guest.
*
* This is a temporary stopgap until we've fixed the dirty tracking
* races in display adapters.
*/
static void do_safe_dpy_refresh(DisplayChangeListener *dcl)
{
qemu_mutex_unlock_iothread();
start_exclusive();
qemu_mutex_lock_iothread();
dcl->ops->dpy_refresh(dcl);
qemu_mutex_unlock_iothread();
end_exclusive();
qemu_mutex_lock_iothread();
}
static void dpy_refresh(DisplayState *s)
{
DisplayChangeListener *dcl;
QLIST_FOREACH(dcl, &s->listeners, next) {
if (dcl->ops->dpy_refresh) {
dcl->ops->dpy_refresh(dcl);
if (tcg_enabled()) {
do_safe_dpy_refresh(dcl);
} else {
dcl->ops->dpy_refresh(dcl);
}
}
}
}

View File

@@ -8,13 +8,14 @@
typedef struct egl_dpy {
DisplayChangeListener dcl;
DisplaySurface *ds;
egl_fb guest_fb;
egl_fb blit_fb;
int width, height;
GLuint texture;
GLuint framebuffer;
GLuint blit_texture;
GLuint blit_framebuffer;
bool y_0_top;
} egl_dpy;
/* ------------------------------------------------------------------ */
static void egl_refresh(DisplayChangeListener *dcl)
{
graphic_hw_update(dcl->con);
@@ -37,8 +38,8 @@ static void egl_scanout_disable(DisplayChangeListener *dcl)
{
egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
egl_fb_destroy(&edpy->guest_fb);
egl_fb_destroy(&edpy->blit_fb);
edpy->texture = 0;
/* XXX: delete framebuffers here ??? */
}
static void egl_scanout_texture(DisplayChangeListener *dcl,
@@ -51,17 +52,34 @@ static void egl_scanout_texture(DisplayChangeListener *dcl,
{
egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
edpy->texture = backing_id;
edpy->y_0_top = backing_y_0_top;
/* source framebuffer */
egl_fb_create_for_tex(&edpy->guest_fb,
backing_width, backing_height, backing_id);
if (!edpy->framebuffer) {
glGenFramebuffers(1, &edpy->framebuffer);
}
glBindFramebuffer(GL_FRAMEBUFFER_EXT, edpy->framebuffer);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, edpy->texture, 0);
/* dest framebuffer */
if (edpy->blit_fb.width != backing_width ||
edpy->blit_fb.height != backing_height) {
egl_fb_destroy(&edpy->blit_fb);
egl_fb_create_new_tex(&edpy->blit_fb, backing_width, backing_height);
if (!edpy->blit_framebuffer) {
glGenFramebuffers(1, &edpy->blit_framebuffer);
glGenTextures(1, &edpy->blit_texture);
edpy->width = 0;
edpy->height = 0;
}
if (edpy->width != backing_width || edpy->height != backing_height) {
edpy->width = backing_width;
edpy->height = backing_height;
glBindTexture(GL_TEXTURE_2D, edpy->blit_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
edpy->width, edpy->height,
0, GL_BGRA, GL_UNSIGNED_BYTE, 0);
glBindFramebuffer(GL_FRAMEBUFFER_EXT, edpy->blit_framebuffer);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, edpy->blit_texture, 0);
}
}
@@ -70,17 +88,32 @@ static void egl_scanout_flush(DisplayChangeListener *dcl,
uint32_t w, uint32_t h)
{
egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
GLuint y1, y2;
if (!edpy->guest_fb.texture || !edpy->ds) {
if (!edpy->texture || !edpy->ds) {
return;
}
assert(surface_width(edpy->ds) == edpy->guest_fb.width);
assert(surface_height(edpy->ds) == edpy->guest_fb.height);
assert(surface_width(edpy->ds) == edpy->width);
assert(surface_height(edpy->ds) == edpy->height);
assert(surface_format(edpy->ds) == PIXMAN_x8r8g8b8);
egl_fb_blit(&edpy->blit_fb, &edpy->guest_fb, edpy->y_0_top);
egl_fb_read(surface_data(edpy->ds), &edpy->blit_fb);
/* blit framebuffer, flip if needed */
glBindFramebuffer(GL_READ_FRAMEBUFFER, edpy->framebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, edpy->blit_framebuffer);
glViewport(0, 0, edpy->width, edpy->height);
y1 = edpy->y_0_top ? edpy->height : 0;
y2 = edpy->y_0_top ? 0 : edpy->height;
glBlitFramebuffer(0, y1, edpy->width, y2,
0, 0, edpy->width, edpy->height,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
/* read pixels to surface */
glBindFramebuffer(GL_READ_FRAMEBUFFER, edpy->blit_framebuffer);
glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
glReadPixels(0, 0, edpy->width, edpy->height,
GL_BGRA, GL_UNSIGNED_BYTE, surface_data(edpy->ds));
/* notify about updates */
dpy_gfx_update(edpy->dcl.con, x, y, w, h);
}

View File

@@ -24,82 +24,6 @@
EGLDisplay *qemu_egl_display;
EGLConfig qemu_egl_config;
/* ------------------------------------------------------------------ */
void egl_fb_destroy(egl_fb *fb)
{
if (!fb->framebuffer) {
return;
}
if (fb->delete_texture) {
glDeleteTextures(1, &fb->texture);
fb->delete_texture = false;
}
glDeleteFramebuffers(1, &fb->framebuffer);
fb->width = 0;
fb->height = 0;
fb->texture = 0;
fb->framebuffer = 0;
}
void egl_fb_setup_default(egl_fb *fb, int width, int height)
{
fb->width = width;
fb->height = height;
fb->framebuffer = 0; /* default framebuffer */
}
void egl_fb_create_for_tex(egl_fb *fb, int width, int height, GLuint texture)
{
fb->width = width;
fb->height = height;
fb->texture = texture;
if (!fb->framebuffer) {
glGenFramebuffers(1, &fb->framebuffer);
}
glBindFramebuffer(GL_FRAMEBUFFER_EXT, fb->framebuffer);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, fb->texture, 0);
}
void egl_fb_create_new_tex(egl_fb *fb, int width, int height)
{
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height,
0, GL_BGRA, GL_UNSIGNED_BYTE, 0);
egl_fb_create_for_tex(fb, width, height, texture);
fb->delete_texture = true;
}
void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip)
{
GLuint y1, y2;
glBindFramebuffer(GL_READ_FRAMEBUFFER, src->framebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst->framebuffer);
glViewport(0, 0, dst->width, dst->height);
y1 = flip ? src->height : 0;
y2 = flip ? 0 : src->height;
glBlitFramebuffer(0, y1, src->width, y2,
0, 0, dst->width, dst->height,
GL_COLOR_BUFFER_BIT, GL_LINEAR);
}
void egl_fb_read(void *dst, egl_fb *src)
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, src->framebuffer);
glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
glReadPixels(0, 0, src->width, src->height,
GL_BGRA, GL_UNSIGNED_BYTE, dst);
}
/* ---------------------------------------------------------------------- */
#ifdef CONFIG_OPENGL_DMABUF

View File

@@ -30,7 +30,14 @@ static void gtk_egl_set_scanout_mode(VirtualConsole *vc, bool scanout)
vc->gfx.scanout_mode = scanout;
if (!vc->gfx.scanout_mode) {
egl_fb_destroy(&vc->gfx.guest_fb);
if (vc->gfx.fbo_id) {
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, 0, 0);
glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
glDeleteFramebuffers(1, &vc->gfx.fbo_id);
vc->gfx.fbo_id = 0;
}
if (vc->gfx.surface) {
surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds);
@@ -169,6 +176,7 @@ void gd_egl_scanout_disable(DisplayChangeListener *dcl)
vc->gfx.w = 0;
vc->gfx.h = 0;
vc->gfx.tex_id = 0;
gtk_egl_set_scanout_mode(vc, false);
}
@@ -184,14 +192,20 @@ void gd_egl_scanout_texture(DisplayChangeListener *dcl,
vc->gfx.y = y;
vc->gfx.w = w;
vc->gfx.h = h;
vc->gfx.tex_id = backing_id;
vc->gfx.y0_top = backing_y_0_top;
eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
vc->gfx.esurface, vc->gfx.ectx);
gtk_egl_set_scanout_mode(vc, true);
egl_fb_create_for_tex(&vc->gfx.guest_fb, backing_width, backing_height,
backing_id);
if (!vc->gfx.fbo_id) {
glGenFramebuffers(1, &vc->gfx.fbo_id);
}
glBindFramebuffer(GL_FRAMEBUFFER_EXT, vc->gfx.fbo_id);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, vc->gfx.tex_id, 0);
}
void gd_egl_scanout_flush(DisplayChangeListener *dcl,
@@ -199,22 +213,30 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl,
{
VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
GdkWindow *window;
int ww, wh;
int ww, wh, y1, y2;
if (!vc->gfx.scanout_mode) {
return;
}
if (!vc->gfx.guest_fb.framebuffer) {
if (!vc->gfx.fbo_id) {
return;
}
eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
vc->gfx.esurface, vc->gfx.ectx);
glBindFramebuffer(GL_READ_FRAMEBUFFER, vc->gfx.fbo_id);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
window = gtk_widget_get_window(vc->gfx.drawing_area);
gdk_drawable_get_size(window, &ww, &wh);
egl_fb_setup_default(&vc->gfx.win_fb, ww, wh);
egl_fb_blit(&vc->gfx.win_fb, &vc->gfx.guest_fb, !vc->gfx.y0_top);
glViewport(0, 0, ww, wh);
y1 = vc->gfx.y0_top ? 0 : vc->gfx.h;
y2 = vc->gfx.y0_top ? vc->gfx.h : 0;
glBlitFramebuffer(0, y1, vc->gfx.w, y2,
0, 0, ww, wh,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER_EXT, vc->gfx.fbo_id);
eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
}

View File

@@ -26,7 +26,14 @@ static void gtk_gl_area_set_scanout_mode(VirtualConsole *vc, bool scanout)
vc->gfx.scanout_mode = scanout;
if (!vc->gfx.scanout_mode) {
egl_fb_destroy(&vc->gfx.guest_fb);
if (vc->gfx.fbo_id) {
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, 0, 0);
glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
glDeleteFramebuffers(1, &vc->gfx.fbo_id);
vc->gfx.fbo_id = 0;
}
if (vc->gfx.surface) {
surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds);
@@ -49,11 +56,11 @@ void gd_gl_area_draw(VirtualConsole *vc)
wh = gtk_widget_get_allocated_height(vc->gfx.drawing_area);
if (vc->gfx.scanout_mode) {
if (!vc->gfx.guest_fb.framebuffer) {
if (!vc->gfx.fbo_id) {
return;
}
glBindFramebuffer(GL_READ_FRAMEBUFFER, vc->gfx.guest_fb.framebuffer);
glBindFramebuffer(GL_READ_FRAMEBUFFER, vc->gfx.fbo_id);
/* GtkGLArea sets GL_DRAW_FRAMEBUFFER for us */
glViewport(0, 0, ww, wh);
@@ -174,19 +181,24 @@ void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
vc->gfx.y = y;
vc->gfx.w = w;
vc->gfx.h = h;
vc->gfx.tex_id = backing_id;
vc->gfx.y0_top = backing_y_0_top;
gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
if (vc->gfx.guest_fb.framebuffer == 0 ||
vc->gfx.w == 0 || vc->gfx.h == 0) {
if (vc->gfx.tex_id == 0 || vc->gfx.w == 0 || vc->gfx.h == 0) {
gtk_gl_area_set_scanout_mode(vc, false);
return;
}
gtk_gl_area_set_scanout_mode(vc, true);
egl_fb_create_for_tex(&vc->gfx.guest_fb, backing_width, backing_height,
backing_id);
if (!vc->gfx.fbo_id) {
glGenFramebuffers(1, &vc->gfx.fbo_id);
}
glBindFramebuffer(GL_FRAMEBUFFER_EXT, vc->gfx.fbo_id);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, vc->gfx.tex_id, 0);
}
void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,

View File

@@ -1,4 +1,5 @@
#include "qemu/osdep.h"
#include "hw/qdev.h"
#include "sysemu/sysemu.h"
#include "qapi-types.h"
#include "qemu/error-report.h"

View File

@@ -42,7 +42,14 @@ static void sdl2_set_scanout_mode(struct sdl2_console *scon, bool scanout)
scon->scanout_mode = scanout;
if (!scon->scanout_mode) {
egl_fb_destroy(&scon->guest_fb);
if (scon->fbo_id) {
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, 0, 0);
glDeleteFramebuffers(1, &scon->fbo_id);
glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
scon->fbo_id = 0;
}
if (scon->surface) {
surface_gl_destroy_texture(scon->gls, scon->surface);
surface_gl_create_texture(scon->gls, scon->surface);
@@ -184,6 +191,7 @@ void sdl2_gl_scanout_disable(DisplayChangeListener *dcl)
assert(scon->opengl);
scon->w = 0;
scon->h = 0;
scon->tex_id = 0;
sdl2_set_scanout_mode(scon, false);
}
@@ -202,34 +210,48 @@ void sdl2_gl_scanout_texture(DisplayChangeListener *dcl,
scon->y = y;
scon->w = w;
scon->h = h;
scon->tex_id = backing_id;
scon->y0_top = backing_y_0_top;
SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
sdl2_set_scanout_mode(scon, true);
egl_fb_create_for_tex(&scon->guest_fb, backing_width, backing_height,
backing_id);
if (!scon->fbo_id) {
glGenFramebuffers(1, &scon->fbo_id);
}
glBindFramebuffer(GL_FRAMEBUFFER_EXT, scon->fbo_id);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, scon->tex_id, 0);
}
void sdl2_gl_scanout_flush(DisplayChangeListener *dcl,
uint32_t x, uint32_t y, uint32_t w, uint32_t h)
{
struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
int ww, wh;
int ww, wh, y1, y2;
assert(scon->opengl);
if (!scon->scanout_mode) {
return;
}
if (!scon->guest_fb.framebuffer) {
if (!scon->fbo_id) {
return;
}
SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
glBindFramebuffer(GL_READ_FRAMEBUFFER, scon->fbo_id);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
SDL_GetWindowSize(scon->real_window, &ww, &wh);
egl_fb_setup_default(&scon->win_fb, ww, wh);
egl_fb_blit(&scon->win_fb, &scon->guest_fb, !scon->y0_top);
glViewport(0, 0, ww, wh);
y1 = scon->y0_top ? 0 : scon->h;
y2 = scon->y0_top ? scon->h : 0;
glBlitFramebuffer(0, y1, scon->w, y2,
0, 0, ww, wh,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER_EXT, scon->fbo_id);
SDL_GL_SwapWindow(scon->real_window);
}

View File

@@ -28,6 +28,7 @@
#include "vnc.h"
#include "vnc-jobs.h"
#include "trace.h"
#include "hw/qdev.h"
#include "sysemu/sysemu.h"
#include "qemu/error-report.h"
#include "qemu/sockets.h"