diff options
author | 2008-12-08 18:33:24 +0000 | |
---|---|---|
committer | 2008-12-08 18:33:24 +0000 | |
commit | 781a5db6ffd0ea41beeed3cc1b27c29b904be963 (patch) | |
tree | a1b942c3d2c3e2d8c56b85f3a69cb35fcb076d28 | |
parent | Do not short circuit pseudo commands when we get a match since, depending (diff) | |
download | wireguard-openbsd-781a5db6ffd0ea41beeed3cc1b27c29b904be963.tar.xz wireguard-openbsd-781a5db6ffd0ea41beeed3cc1b27c29b904be963.zip |
Add ability to load firmware for devices which require it.
Tested by deraadt@ and myself.
-rw-r--r-- | sys/dev/usb/uvideo.c | 155 | ||||
-rw-r--r-- | sys/dev/usb/uvideo.h | 6 |
2 files changed, 159 insertions, 2 deletions
diff --git a/sys/dev/usb/uvideo.c b/sys/dev/usb/uvideo.c index ed3189bb676..3f79cf0cffa 100644 --- a/sys/dev/usb/uvideo.c +++ b/sys/dev/usb/uvideo.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uvideo.c,v 1.100 2008/12/06 20:06:39 mglocker Exp $ */ +/* $OpenBSD: uvideo.c,v 1.101 2008/12/08 18:33:24 mglocker Exp $ */ /* * Copyright (c) 2008 Robert Nagy <robert@openbsd.org> @@ -64,6 +64,7 @@ int uvideo_open(void *, int, int *, uint8_t *, void (*)(void *), int uvideo_close(void *); int uvideo_match(struct device *, void *, void *); void uvideo_attach(struct device *, struct device *, void *); +void uvideo_attach_hook(void *); int uvideo_detach(struct device *, int); int uvideo_activate(struct device *, enum devact); @@ -190,6 +191,13 @@ caddr_t uvideo_mappage(void *, off_t, int); int uvideo_get_bufsize(void *); int uvideo_start_read(void *); +/* + * Firmware + */ +int uvideo_ucode_match(struct uvideo_softc *, + struct usb_attach_arg *); +usbd_status uvideo_ucode_loader_ricoh(struct uvideo_softc *); + struct cfdriver uvideo_cd = { NULL, "uvideo", DV_DULL }; @@ -228,6 +236,21 @@ struct video_hw_if uvideo_hw_if = { uvideo_start_read /* start stream for read */ }; +struct uvideo_ucode { + int vendor; + int product; + char ucode_name[64]; + usbd_status (*ucode_loader)(struct uvideo_softc *); +} uvideo_ucode_devs[] = { + { + USB_VENDOR_RICOH, + USB_PRODUCT_RICOH_VGPVCC7, + "r5u87x-05ca-183a.fw", + uvideo_ucode_loader_ricoh + }, + { 0, 0, "", NULL } +}; + /* * Some devices do not report themselfs as UVC compatible although * they are. They report UICLASS_VENDOR in the bInterfaceClass @@ -373,6 +396,27 @@ uvideo_attach(struct device *parent, struct device *self, void *aux) if (error != USBD_NORMAL_COMPLETION) return; + /* if the device needs ucode do mountroothook */ + sc->sc_flags |= uvideo_ucode_match(sc, uaa); + + if (sc->sc_flags & UVIDEO_FLAGS_NEED_UCODE && rootvp == NULL) + mountroothook_establish(uvideo_attach_hook, sc); + else + uvideo_attach_hook(sc); +} + +void +uvideo_attach_hook(void *arg) +{ + struct uvideo_softc *sc = arg; + usbd_status error; + + if (sc->sc_flags & UVIDEO_FLAGS_NEED_UCODE) { + error = (sc->sc_ucode->ucode_loader)(sc); + if (error != USBD_NORMAL_COMPLETION) + return; + } + /* set default video stream interface */ error = usbd_set_interface(sc->sc_vs_cur->ifaceh, 0); if (error != USBD_NORMAL_COMPLETION) @@ -2968,3 +3012,112 @@ uvideo_start_read(void *v) return (0); } + +int +uvideo_ucode_match(struct uvideo_softc *sc, struct usb_attach_arg *uaa) +{ + usb_device_descriptor_t *dd; + int i; + + dd = usbd_get_device_descriptor(uaa->device); + + for (i = 0; uvideo_ucode_devs[i].vendor != 0; i++) { + if (UGETW(dd->idVendor) == uvideo_ucode_devs[i].vendor && + UGETW(dd->idProduct) == uvideo_ucode_devs[i].product) { + sc->sc_ucode = &uvideo_ucode_devs[i]; + return (UVIDEO_FLAGS_NEED_UCODE); + } + } + + return (0); +} + +usbd_status +uvideo_ucode_loader_ricoh(struct uvideo_softc *sc) +{ + usb_device_request_t req; + usbd_status error; + uint8_t *ucode; + size_t ucode_size; + uint8_t buf; + uint16_t len, addr; + int offset, remain; + + /* get device microcode status */ + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = 0xa4; + USETW(req.wIndex, 0); + USETW(req.wValue, 0); + USETW(req.wLength, 1); + buf = 0; + error = usbd_do_request(sc->sc_udev, &req, &buf); + if (error != USBD_NORMAL_COMPLETION) { + printf("%s: ucode status error=%s!\n", + DEVNAME(sc), usbd_errstr(error)); + return (USBD_INVAL); + } + if (buf) { + DPRINTF(1, "%s: microcode already loaded\n", DEVNAME(sc)); + return (USBD_NORMAL_COMPLETION); + } else { + DPRINTF(1, "%s: microcode not loaded\n", DEVNAME(sc)); + } + + /* open ucode file */ + error = loadfirmware(sc->sc_ucode->ucode_name, &ucode, &ucode_size); + if (error != 0) { + printf("%s: loadfirmware error=%d!\n", DEVNAME(sc), error); + return (USBD_INVAL); + } + + /* upload microcode */ + offset = 0; + remain = ucode_size; + while (remain > 0) { + if (remain < 3) { + printf("%s: ucode file incomplete!\n", DEVNAME(sc)); + return (USBD_INVAL); + } + + len = ucode[offset]; + addr = ucode[offset + 1] | (ucode[offset + 2] << 8); + offset += 3; + remain -= 3; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = 0xa0; + USETW(req.wIndex, 0); + USETW(req.wValue, addr); + USETW(req.wLength, len); + error = usbd_do_request(sc->sc_udev, &req, &ucode[offset]); + if (error != USBD_NORMAL_COMPLETION) { + printf("%s: ucode upload error=%s!\n", + DEVNAME(sc), usbd_errstr(error)); + free(ucode, M_DEVBUF); + return (USBD_INVAL); + } + DPRINTF(1, "%s: uploaded %d bytes ucode to addr 0x%x\n", + DEVNAME(sc), len, addr); + + offset += len; + remain -= len; + } + free(ucode, M_DEVBUF); + + /* activate microcode */ + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = 0xa1; + USETW(req.wIndex, 0); + USETW(req.wValue, 0); + USETW(req.wLength, 1); + buf = 0; + error = usbd_do_request(sc->sc_udev, &req, &buf); + if (error != USBD_NORMAL_COMPLETION) { + printf("%s: ucode activate error=%s!\n", + DEVNAME(sc), usbd_errstr(error)); + return (USBD_INVAL); + } + DPRINTF(1, "%s: ucode activated\n", DEVNAME(sc)); + + return (USBD_NORMAL_COMPLETION); +} diff --git a/sys/dev/usb/uvideo.h b/sys/dev/usb/uvideo.h index d8fc68cd0e2..8e422eda018 100644 --- a/sys/dev/usb/uvideo.h +++ b/sys/dev/usb/uvideo.h @@ -1,4 +1,4 @@ -/* $OpenBSD: uvideo.h,v 1.39 2008/12/05 10:44:52 mglocker Exp $ */ +/* $OpenBSD: uvideo.h,v 1.40 2008/12/08 18:33:24 mglocker Exp $ */ /* * Copyright (c) 2007 Robert Nagy <robert@openbsd.org> @@ -558,6 +558,8 @@ struct uvideo_softc { struct device *sc_videodev; +#define UVIDEO_FLAGS_NEED_UCODE 0x1 + int sc_flags; int sc_enabled; int sc_dying; int sc_max_fbuf_size; @@ -599,4 +601,6 @@ struct uvideo_softc { int *sc_uplayer_fsize; uint8_t *sc_uplayer_fbuffer; void (*sc_uplayer_intr)(void *); + + struct uvideo_ucode *sc_ucode; }; |