1 /* $OpenBSD: uberry.c,v 1.23 2014/12/19 22:44:59 guenther Exp $ */ 2 3 /*- 4 * Copyright (c) 2006 Theo de Raadt <deraadt@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/sockio.h> 21 #include <sys/mbuf.h> 22 #include <sys/kernel.h> 23 #include <sys/socket.h> 24 #include <sys/systm.h> 25 #include <sys/timeout.h> 26 #include <sys/conf.h> 27 #include <sys/device.h> 28 #include <sys/endian.h> 29 30 #include <machine/bus.h> 31 #include <machine/intr.h> 32 33 #include <dev/usb/usb.h> 34 #include <dev/usb/usbdi.h> 35 #include <dev/usb/usbdivar.h> 36 #include <dev/usb/usbdi_util.h> 37 #include <dev/usb/usbdevs.h> 38 39 struct uberry_softc { 40 struct device sc_dev; 41 struct usbd_device *sc_udev; 42 struct usbd_interface *sc_iface; 43 }; 44 45 #define UBERRY_INTERFACE_NO 0 46 #define UBERRY_CONFIG_NO 1 47 48 /* 49 * Do not match on the following device, because it is type umass 50 * { USB_VENDOR_RIM, USB_PRODUCT_RIM_PEARL_DUAL }, 51 */ 52 struct usb_devno const uberry_devices[] = { 53 { USB_VENDOR_RIM, USB_PRODUCT_RIM_BLACKBERRY }, 54 { USB_VENDOR_RIM, USB_PRODUCT_RIM_PEARL } 55 }; 56 57 int uberry_match(struct device *, void *, void *); 58 void uberry_attach(struct device *, struct device *, void *); 59 int uberry_detach(struct device *, int); 60 61 void uberry_pearlmode(struct uberry_softc *); 62 void uberry_charge(struct uberry_softc *); 63 64 struct cfdriver uberry_cd = { 65 NULL, "uberry", DV_DULL 66 }; 67 68 const struct cfattach uberry_ca = { 69 sizeof(struct uberry_softc), uberry_match, uberry_attach, uberry_detach 70 }; 71 72 int 73 uberry_match(struct device *parent, void *match, void *aux) 74 { 75 struct usb_attach_arg *uaa = aux; 76 77 if (uaa->iface != NULL) 78 return UMATCH_NONE; 79 80 return (usb_lookup(uberry_devices, uaa->vendor, uaa->product) != NULL) ? 81 UMATCH_VENDOR_PRODUCT : UMATCH_NONE; 82 } 83 84 void 85 uberry_attach(struct device *parent, struct device *self, void *aux) 86 { 87 struct uberry_softc *sc = (struct uberry_softc *)self; 88 struct usb_attach_arg *uaa = aux; 89 usb_device_descriptor_t *dd; 90 91 sc->sc_udev = uaa->device; 92 93 dd = usbd_get_device_descriptor(uaa->device); 94 95 /* Enable configuration, to keep it connected... */ 96 if (usbd_set_config_no(sc->sc_udev, UBERRY_CONFIG_NO, 1) != 0) { 97 /* 98 * Really ancient (ie. 7250) devices when off will 99 * only charge at 100mA when turned off. 100 */ 101 printf("%s: Charging at %dmA\n", sc->sc_dev.dv_xname, 102 sc->sc_udev->power); 103 return; 104 } 105 106 printf("%s: Charging at %dmA", sc->sc_dev.dv_xname, 107 sc->sc_udev->power); 108 if (sc->sc_udev->power >= 250) 109 printf("\n"); 110 else { 111 printf("... requesting higher-power charging\n"); 112 uberry_charge(sc); 113 /* 114 * Older berry's will disconnect/reconnect at this 115 * point, and come back requesting higher power 116 */ 117 } 118 119 /* On the Pearl, request a change to Dual mode */ 120 if (UGETW(dd->idProduct) == USB_PRODUCT_RIM_PEARL) 121 uberry_pearlmode(sc); 122 123 /* Enable the device, then it cannot idle, and will charge */ 124 if (usbd_set_config_no(sc->sc_udev, UBERRY_CONFIG_NO, 1) != 0) { 125 printf("%s: could not set configuration no\n", 126 sc->sc_dev.dv_xname); 127 return; 128 } 129 130 if (UGETW(dd->idProduct) == USB_PRODUCT_RIM_PEARL) { 131 /* 132 * Pearl does not disconnect/reconnect by itself, 133 * and therefore needs to be told to reset, so that 134 * it can come back in Dual mode. 135 */ 136 usb_needs_reattach(sc->sc_udev); 137 } 138 } 139 140 int 141 uberry_detach(struct device *self, int flags) 142 { 143 /* struct uberry_softc *sc = (struct uberry_softc *)self; */ 144 145 return 0; 146 } 147 148 void 149 uberry_pearlmode(struct uberry_softc *sc) 150 { 151 usb_device_request_t req; 152 char buffer[256]; 153 154 req.bmRequestType = UT_READ_VENDOR_DEVICE; 155 req.bRequest = 0xa9; 156 USETW(req.wValue, 1); 157 USETW(req.wIndex, 1); 158 USETW(req.wLength, 2); 159 (void) usbd_do_request(sc->sc_udev, &req, &buffer); 160 } 161 162 void 163 uberry_charge(struct uberry_softc *sc) 164 { 165 usb_device_request_t req; 166 char buffer[256]; 167 168 req.bmRequestType = UT_READ_VENDOR_DEVICE; 169 req.bRequest = 0xa5; 170 USETW(req.wValue, 0); 171 USETW(req.wIndex, 1); 172 USETW(req.wLength, 2); 173 (void) usbd_do_request(sc->sc_udev, &req, &buffer); 174 175 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 176 req.bRequest = 0xa2; 177 USETW(req.wValue, 0); 178 USETW(req.wIndex, 1); 179 USETW(req.wLength, 0); 180 (void) usbd_do_request(sc->sc_udev, &req, &buffer); 181 } 182