#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/timeout.h>
#include <machine/bus.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdevs.h>
#include <dev/usb/usbcdc.h>
#define ONERNG_BUFSIZ 128
#define ONERNG_MSECS 100
#define ONERNG_TIMEOUT 1000
#ifdef ONERNG_MEASURE_RATE
#define ONERNG_RATE_SECONDS 30
#endif
#define ONERNG_OP_ENABLE "cmdO\n"
#define ONERNG_OP_DISABLE "cmdo\n"
#define ONERNG_OP_FLUSH_ENTROPY "cmdw\n"
#define ONERNG_OP_EXTRACT_FIRMWARE "cmdX\n"
#define ONERNG_AVALANCHE_WHITENER "cmd0\n"
#define ONERNG_AVALANCHE "cmd1\n"
#define ONERNG_AVALANCHE_RF_WHITENER "cmd2\n"
#define ONERNG_AVALANCHE_RF "cmd3\n"
#define ONERNG_SILENT "cmd4\n"
#define ONERNG_SILENT2 "cmd5\n"
#define ONERNG_RF_WHITENER "cmd6\n"
#define ONERNG_RF "cmd7\n"
#define ONERNG_IFACE_CTRL_INDEX 0
#define ONERNG_IFACE_DATA_INDEX 1
#define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname)
struct uonerng_softc {
struct device sc_dev;
struct usbd_device *sc_udev;
int sc_ctl_iface_no;
struct usbd_interface *sc_data_iface;
struct usbd_pipe *sc_inpipe;
struct usbd_pipe *sc_outpipe;
struct timeout sc_timeout;
struct usb_task sc_task;
struct usbd_xfer *sc_xfer;
int *sc_buf;
#ifdef ONERNG_MEASURE_RATE
struct timeval sc_start;
struct timeval sc_cur;
int sc_counted_bytes;
#endif
u_char sc_dtr;
u_char sc_rts;
u_char sc_first_run;
};
int uonerng_match(struct device *, void *, void *);
void uonerng_attach(struct device *, struct device *, void *);
int uonerng_detach(struct device *, int);
void uonerng_task(void *);
void uonerng_timeout(void *);
int uonerng_enable(struct uonerng_softc *sc);
void uonerng_cleanup(struct uonerng_softc *sc);
usbd_status uonerng_set_line_state(struct uonerng_softc *sc);
usbd_status uonerng_rts(struct uonerng_softc *sc, int onoff);
struct cfdriver uonerng_cd = {
NULL, "uonerng", DV_DULL
};
const struct cfattach uonerng_ca = {
sizeof(struct uonerng_softc), uonerng_match, uonerng_attach, uonerng_detach
};
int
uonerng_match(struct device *parent, void *match, void *aux)
{
struct usb_attach_arg *uaa = aux;
if (uaa->iface == NULL)
return UMATCH_NONE;
if (uaa->vendor != USB_VENDOR_OPENMOKO2 ||
uaa->product != USB_PRODUCT_OPENMOKO2_ONERNG)
return UMATCH_NONE;
return UMATCH_VENDOR_PRODUCT;
}
void
uonerng_attach(struct device *parent, struct device *self, void *aux)
{
struct uonerng_softc *sc = (struct uonerng_softc *)self;
struct usb_attach_arg *uaa = aux;
struct usbd_interface *iface = uaa->iface;
usb_interface_descriptor_t *id;
usb_endpoint_descriptor_t *ed;
int ep_ibulk = -1, ep_obulk = -1;
usbd_status err;
int i;
sc->sc_udev = uaa->device;
sc->sc_dtr = -1;
sc->sc_rts = -1;
sc->sc_first_run = 1;
usb_init_task(&sc->sc_task, uonerng_task, sc, USB_TASK_TYPE_GENERIC);
err = usbd_device2interface_handle(sc->sc_udev,
ONERNG_IFACE_CTRL_INDEX, &iface);
if (err || iface == NULL) {
printf("%s: failed to locate control interface, err=%s\n",
DEVNAME(sc), usbd_errstr(err));
goto fail;
}
id = usbd_get_interface_descriptor(iface);
if (id != NULL &&
id->bInterfaceClass == UICLASS_CDC &&
id->bInterfaceSubClass == UISUBCLASS_ABSTRACT_CONTROL_MODEL &&
id->bInterfaceProtocol == UIPROTO_CDC_AT) {
sc->sc_ctl_iface_no = id->bInterfaceNumber;
} else {
printf("%s: control interface number not found\n",
DEVNAME(sc));
goto fail;
}
err = usbd_device2interface_handle(sc->sc_udev,
ONERNG_IFACE_DATA_INDEX, &sc->sc_data_iface);
if (err || sc->sc_data_iface == NULL) {
printf("%s: failed to locate data interface, err=%s\n",
DEVNAME(sc), usbd_errstr(err));
goto fail;
}
id = usbd_get_interface_descriptor(sc->sc_data_iface);
if (id == NULL ||
id->bInterfaceClass != UICLASS_CDC_DATA ||
id->bInterfaceSubClass != UISUBCLASS_DATA) {
printf("%s: no data interface descriptor\n", DEVNAME(sc));
goto fail;
}
for (i = 0; i < id->bNumEndpoints; i++) {
ed = usbd_interface2endpoint_descriptor(sc->sc_data_iface, i);
if (ed == NULL) {
printf("%s: no endpoint descriptor for %d\n",
DEVNAME(sc), i);
goto fail;
}
if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
ep_ibulk = ed->bEndpointAddress;
} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
ep_obulk = ed->bEndpointAddress;
}
}
if (ep_ibulk == -1) {
printf("%s: Could not find data bulk in\n", DEVNAME(sc));
goto fail;
}
if (ep_obulk == -1) {
printf("%s: Could not find data bulk out\n", DEVNAME(sc));
goto fail;
}
err = usbd_open_pipe(sc->sc_data_iface, ep_ibulk,
USBD_EXCLUSIVE_USE, &sc->sc_inpipe);
if (err) {
printf("%s: failed to open bulk-in pipe: %s\n",
DEVNAME(sc), usbd_errstr(err));
goto fail;
}
err = usbd_open_pipe(sc->sc_data_iface, ep_obulk,
USBD_EXCLUSIVE_USE, &sc->sc_outpipe);
if (err) {
printf("%s: failed to open bulk-out pipe: %s\n",
DEVNAME(sc), usbd_errstr(err));
goto fail;
}
sc->sc_xfer = usbd_alloc_xfer(sc->sc_udev);
if (sc->sc_xfer == NULL) {
printf("%s: could not alloc xfer\n", DEVNAME(sc));
goto fail;
}
sc->sc_buf = usbd_alloc_buffer(sc->sc_xfer, ONERNG_BUFSIZ);
if (sc->sc_buf == NULL) {
printf("%s: could not alloc %d-byte buffer\n", DEVNAME(sc),
ONERNG_BUFSIZ);
goto fail;
}
if (uonerng_enable(sc) != 0) {
goto fail;
}
timeout_set(&sc->sc_timeout, uonerng_timeout, sc);
uonerng_task(sc);
usb_add_task(sc->sc_udev, &sc->sc_task);
return;
fail:
usbd_deactivate(sc->sc_udev);
uonerng_cleanup(sc);
}
int
uonerng_enable(struct uonerng_softc *sc)
{
int err;
if ((err = uonerng_rts(sc, 0))) {
printf("%s: failed to clear RTS: %s\n", DEVNAME(sc),
usbd_errstr(err));
return (1);
}
usbd_setup_xfer(sc->sc_xfer, sc->sc_outpipe, sc,
ONERNG_AVALANCHE_WHITENER, sizeof(ONERNG_AVALANCHE_WHITENER),
USBD_SYNCHRONOUS, ONERNG_TIMEOUT, NULL);
if ((err = usbd_transfer(sc->sc_xfer))) {
printf("%s: failed to set operating mode: %s\n",
DEVNAME(sc), usbd_errstr(err));
return (1);
}
usbd_setup_xfer(sc->sc_xfer, sc->sc_outpipe, sc,
ONERNG_OP_ENABLE, sizeof(ONERNG_OP_ENABLE),
USBD_SYNCHRONOUS, ONERNG_TIMEOUT, NULL);
if ((err = usbd_transfer(sc->sc_xfer))) {
printf("%s: failed to enable device: %s\n",
DEVNAME(sc), usbd_errstr(err));
return (1);
}
return (0);
}
int
uonerng_detach(struct device *self, int flags)
{
struct uonerng_softc *sc = (struct uonerng_softc *)self;
usb_rem_task(sc->sc_udev, &sc->sc_task);
if (timeout_initialized(&sc->sc_timeout)) {
timeout_del(&sc->sc_timeout);
}
uonerng_cleanup(sc);
return (0);
}
void
uonerng_cleanup(struct uonerng_softc *sc)
{
if (sc->sc_inpipe != NULL) {
usbd_close_pipe(sc->sc_inpipe);
sc->sc_inpipe = NULL;
}
if (sc->sc_outpipe != NULL) {
usbd_close_pipe(sc->sc_outpipe);
sc->sc_outpipe = NULL;
}
if (sc->sc_xfer != NULL) {
usbd_free_xfer(sc->sc_xfer);
sc->sc_xfer = NULL;
}
}
usbd_status
uonerng_rts(struct uonerng_softc *sc, int onoff)
{
if (sc->sc_rts == onoff)
return USBD_NORMAL_COMPLETION;
sc->sc_rts = onoff;
return uonerng_set_line_state(sc);
}
usbd_status
uonerng_set_line_state(struct uonerng_softc *sc)
{
usb_device_request_t req;
int ls;
ls = (sc->sc_dtr ? UCDC_LINE_DTR : 0) |
(sc->sc_rts ? UCDC_LINE_RTS : 0);
req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
USETW(req.wValue, ls);
USETW(req.wIndex, sc->sc_ctl_iface_no);
USETW(req.wLength, 0);
return usbd_do_request(sc->sc_udev, &req, 0);
}
void
uonerng_task(void *arg)
{
struct uonerng_softc *sc = (struct uonerng_softc *) arg;
usbd_status error;
u_int32_t len, int_count, i;
#ifdef ONERNG_MEASURE_RATE
time_t elapsed;
int rate;
#endif
usbd_setup_xfer(sc->sc_xfer, sc->sc_inpipe, NULL, sc->sc_buf,
ONERNG_BUFSIZ,
USBD_SHORT_XFER_OK | USBD_SYNCHRONOUS | USBD_NO_COPY,
ONERNG_TIMEOUT, NULL);
error = usbd_transfer(sc->sc_xfer);
if (error) {
printf("%s: xfer failed: %s\n", DEVNAME(sc),
usbd_errstr(error));
goto bail;
}
usbd_get_xfer_status(sc->sc_xfer, NULL, NULL, &len, NULL);
if (len < sizeof(int)) {
printf("%s: xfer too short (%u bytes) - dropping\n",
DEVNAME(sc), len);
goto bail;
}
#ifdef ONERNG_MEASURE_RATE
if (sc->sc_first_run) {
sc->sc_counted_bytes = 0;
getmicrotime(&(sc->sc_start));
}
sc->sc_counted_bytes += len;
getmicrotime(&(sc->sc_cur));
elapsed = sc->sc_cur.tv_sec - sc->sc_start.tv_sec;
if (elapsed >= ONERNG_RATE_SECONDS) {
rate = (8 * sc->sc_counted_bytes) / (elapsed * 1024);
printf("%s: transfer rate = %d kb/s\n", DEVNAME(sc), rate);
sc->sc_counted_bytes = 0;
getmicrotime(&(sc->sc_start));
}
#endif
int_count = len / sizeof(int);
for (i = 0; i < int_count; i++) {
enqueue_randomness(sc->sc_buf[i]);
}
bail:
if (sc->sc_first_run) {
sc->sc_first_run = 0;
} else {
timeout_add_msec(&sc->sc_timeout, ONERNG_MSECS);
}
}
void
uonerng_timeout(void *arg)
{
struct uonerng_softc *sc = arg;
usb_add_task(sc->sc_udev, &sc->sc_task);
}