/*
EIBD eib bus access and management daemon
Copyright (C) 2005-2007 Martin Koegler <mkoegler@auto.tuwien.ac.at>
This program 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 2 of the License, or
(at your option) any later version.
This program 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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <asm/types.h>
#include <linux/hiddev.h>
#include <sys/ioctl.h>
#include "usbif.h"
USBEndpoint
parseUSBEndpoint (const char *addr)
{
USBEndpoint e;
e.bus = -1;
e.device = -1;
e.config = -1;
e.interface = -1;
if (!*addr)
return e;
if (!isdigit (*addr))
return e;
e.bus = atoi (addr);
while (isdigit (*addr))
addr++;
if (*addr != ':')
return e;
addr++;
if (!isdigit (*addr))
return e;
e.device = atoi (addr);
while (isdigit (*addr))
addr++;
if (*addr != ':')
return e;
addr++;
if (!isdigit (*addr))
return e;
e.config = atoi (addr);
while (isdigit (*addr))
addr++;
if (*addr != ':')
return e;
addr++;
if (!isdigit (*addr))
return e;
e.interface = atoi (addr);
return e;
}
void
check_device (libusb_device_id_t cdev, USBEndpoint e)
{
struct usb_device_desc desc;
struct usb_config_desc cfg;
struct usb_interface_desc intf;
struct usb_endpoint_desc ep;
int in, out, outint;
libusb_bus_id_t bus;
unsigned char devnum;
libusb_dev_handle_t h;
int j, k, l;
if (!cdev)
return;
libusb_get_devnum (cdev, &devnum);
libusb_get_bus_id (cdev, &bus);
if (libusb_get_busnum (bus) != e.bus && e.bus != -1)
return;
if (devnum != e.device && e.device != -1)
return;
libusb_get_device_desc (cdev, &desc);
for (j = 0; j < desc.bNumConfigurations; j++)
{
libusb_get_config_desc (cdev, j, &cfg);
if (cfg.bConfigurationValue != e.config && e.config != -1)
continue;
for (k = 0; k < cfg.bNumInterfaces; k++)
{
libusb_get_interface_desc (cdev, j, k, &intf);
if (intf.bInterfaceNumber != e.interface && e.interface != -1)
continue;
if (intf.bInterfaceClass != USB_CLASS_HID)
continue;
in = 0;
out = 0;
outint = 0;
for (l = 0; l < intf.bNumEndpoints; l++)
{
libusb_get_endpoint_desc (cdev, j, k, l, &ep);
if (ep.wMaxPacketSize == 64)
{
if (ep.bEndpointAddress & 0x80)
{
if ((ep.bmAttributes & USB_ENDPOINT_TYPE_MASK) ==
USB_ENDPOINT_TYPE_INTERRUPT)
in = ep.bEndpointAddress;
}
else
{
if (((ep.bmAttributes & USB_ENDPOINT_TYPE_MASK) ==
USB_ENDPOINT_TYPE_CONTROL && !outint && 0)
|| (ep.bmAttributes & USB_ENDPOINT_TYPE_MASK) ==
USB_ENDPOINT_TYPE_INTERRUPT)
{
out = ep.bEndpointAddress;
outint =
(ep.bmAttributes & USB_ENDPOINT_TYPE_MASK) ==
USB_ENDPOINT_TYPE_INTERRUPT;
}
}
}
}
if (!in || !out)
continue;
if (libusb_open (cdev, &h) >= 0)
{
USBDevice e1;
e1.dev = cdev;
e1.config = cfg.bConfigurationValue;
e1.interface = intf.bInterfaceNumber;
e1.sendep = out;
e1.recvep = in;
libusb_close (h);
throw e1;
}
}
}
}
void
check_devlist (libusb_device_id_t dev, USBEndpoint e)
{
libusb_device_id_t cdev;
unsigned char count;
int i;
check_device (dev, e);
libusb_get_child_count (dev, &count);
for (i = 0; i < count; i++)
{
if (libusb_get_child_device_id (dev, i + 1, &cdev) < 0)
continue;
check_devlist (cdev, e);
}
}
USBDevice
detectUSBEndpoint (USBEndpoint e)
{
libusb_bus_id_t bus;
libusb_device_id_t dev;
USBDevice e2;
try
{
for (libusb_get_first_bus_id (&bus); bus;)
{
libusb_get_first_device_id (bus, &dev);
check_devlist (dev, e);
if (libusb_get_next_bus_id (&bus) < 0)
break;
}
}
catch (USBDevice e1)
{
return e1;
}
e2.dev = -1;
return e2;
}
USBLowLevelDriver::USBLowLevelDriver (const char *Dev, Trace * tr)
{
libusb_bus_id_t bus;
unsigned char devnum;
t = tr;
TRACEPRINTF (t, 1, this, "Detect");
USBEndpoint e = parseUSBEndpoint (Dev);
d = detectUSBEndpoint (e);
if (d.dev == -1)
throw Exception (DEV_OPEN_FAIL);
libusb_get_devnum (d.dev, &devnum);
libusb_get_bus_id (d.dev, &bus);
TRACEPRINTF (t, 1, this, "Using %d (%d:%d:%d:%d) (%d:%d)", d.dev,
libusb_get_busnum (bus),
devnum, d.config, d.interface, d.sendep, d.recvep);
if (libusb_open (d.dev, &dev) < 0)
throw Exception (DEV_OPEN_FAIL);
TRACEPRINTF (t, 1, this, "Open");
libusb_detach_kernel_driver_np (dev, d.interface);
if (libusb_set_configuration (dev, d.config) < 0)
throw Exception (DEV_OPEN_FAIL);
if (libusb_claim_interface (dev, d.interface) < 0)
throw Exception (DEV_OPEN_FAIL);
TRACEPRINTF (t, 1, this, "Claimed");
pth_sem_init (&in_signal);
pth_sem_init (&out_signal);
pth_sem_init (&send_empty);
pth_sem_set_value (&send_empty, 1);
getwait = pth_event (PTH_EVENT_SEM, &out_signal);
Start ();
TRACEPRINTF (t, 1, this, "Opened");
}
USBLowLevelDriver::~USBLowLevelDriver ()
{
TRACEPRINTF (t, 1, this, "Close");
Stop ();
pth_event_free (getwait, PTH_FREE_THIS);
TRACEPRINTF (t, 1, this, "Release");
libusb_release_interface (dev, d.interface);
libusb_attach_kernel_driver_np (dev, d.interface);
TRACEPRINTF (t, 1, this, "Close");
libusb_close (dev);
}
bool
USBLowLevelDriver::Connection_Lost ()
{
return 0;
}
void
USBLowLevelDriver::Send_Packet (CArray l)
{
CArray pdu;
t->TracePacket (1, this, "Send", l);
inqueue.put (l);
pth_sem_set_value (&send_empty, 0);
pth_sem_inc (&in_signal, TRUE);
}
void
USBLowLevelDriver::SendReset ()
{
}
bool
USBLowLevelDriver::Send_Queue_Empty ()
{
return inqueue.isempty ();
}
pth_sem_t *
USBLowLevelDriver::Send_Queue_Empty_Cond ()
{
return &send_empty;
}
CArray *
USBLowLevelDriver::Get_Packet (pth_event_t stop)
{
if (stop != NULL)
pth_event_concat (getwait, stop, NULL);
pth_wait (getwait);
if (stop)
pth_event_isolate (getwait);
if (pth_event_status (getwait) == PTH_STATUS_OCCURRED)
{
pth_sem_dec (&out_signal);
CArray *c = outqueue.get ();
t->TracePacket (1, this, "Recv", *c);
return c;
}
else
return 0;
}
LowLevelDriverInterface::EMIVer USBLowLevelDriver::getEMIVer ()
{
return vRaw;
}
void
USBLowLevelDriver::Run (pth_sem_t * stop1)
{
int i, j;
pth_event_t stop = pth_event (PTH_EVENT_SEM, stop1);
pth_event_t input = pth_event (PTH_EVENT_SEM, &in_signal);
uchar recvbuf[64];
uchar sendbuf[64];
libusb_io_handle_t *sendh = 0;
libusb_io_handle_t *recvh = 0;
pth_event_t sende = pth_event (PTH_EVENT_SEM, &in_signal);;
pth_event_t recve = pth_event (PTH_EVENT_SEM, &in_signal);;
while (pth_event_status (stop) != PTH_STATUS_OCCURRED)
{
if (!recvh)
{
recvh =
libusb_submit_interrupt_read (dev, d.recvep, recvbuf,
sizeof (recvbuf), 1000, 0);
if (!recvh)
{
TRACEPRINTF (t, 0, this, "Error StartRecv");
break;
}
TRACEPRINTF (t, 0, this, "StartRecv");
pth_event (PTH_EVENT_FD | PTH_MODE_REUSE | PTH_UNTIL_FD_READABLE |
PTH_UNTIL_FD_WRITEABLE, recve,
libusb_io_wait_handle (recvh));
}
if (recvh && libusb_is_io_completed (recvh))
{
if (libusb_io_comp_status (recvh) < 0)
TRACEPRINTF (t, 0, this, "RecvError %d",
libusb_io_comp_status (recvh));
else
{
TRACEPRINTF (t, 0, this, "RecvComplete %d",
libusb_io_xfer_size (recvh));
CArray res;
res.set (recvbuf, sizeof (recvbuf));
t->TracePacket (0, this, "RecvUSB", res);
outqueue.put (new CArray (res));
pth_sem_inc (&out_signal, 1);
}
libusb_io_free (recvh);
recvh = 0;
continue;
}
if (sendh && libusb_is_io_completed (sendh))
{
if (libusb_io_comp_status (sendh) < 0)
TRACEPRINTF (t, 0, this, "SendError %d",
libusb_io_comp_status (sendh));
else
{
TRACEPRINTF (t, 0, this, "SendComplete %d",
libusb_io_xfer_size (sendh));
pth_sem_dec (&in_signal);
inqueue.get ();
if (inqueue.isempty ())
pth_sem_set_value (&send_empty, 1);
}
libusb_io_free (sendh);
sendh = 0;
continue;
}
if (!sendh && !inqueue.isempty ())
{
const CArray & c = inqueue.top ();
t->TracePacket (0, this, "Send", c);
memset (sendbuf, 0, sizeof (sendbuf));
memcpy (sendbuf, c.array (),
(c () > sizeof (sendbuf) ? sizeof (sendbuf) : c ()));
sendh =
libusb_submit_interrupt_write (dev, d.sendep, sendbuf,
sizeof (sendbuf), 1000, 0);
if (!sendh)
{
TRACEPRINTF (t, 0, this, "Error StartSend");
break;
}
TRACEPRINTF (t, 0, this, "StartSend");
pth_event (PTH_EVENT_FD | PTH_MODE_REUSE | PTH_UNTIL_FD_READABLE |
PTH_UNTIL_FD_WRITEABLE, sende,
libusb_io_wait_handle (sendh));
continue;
}
if (recvh)
pth_event_concat (stop, recve, NULL);
if (sendh)
pth_event_concat (stop, sende, NULL);
else
pth_event_concat (stop, input, NULL);
pth_wait (stop);
pth_event_isolate (sende);
pth_event_isolate (recve);
pth_event_isolate (input);
}
if (sendh)
{
libusb_io_cancel (sendh);
libusb_io_free (sendh);
}
if (recvh)
{
libusb_io_cancel (recvh);
libusb_io_free (recvh);
}
pth_event_free (stop, PTH_FREE_THIS);
pth_event_free (input, PTH_FREE_THIS);
pth_event_free (sende, PTH_FREE_THIS);
pth_event_free (recve, PTH_FREE_THIS);
}
syntax highlighted by Code2HTML, v. 0.9.1