1 /* $OpenBSD: uberry.c,v 1.20 2011/07/03 15:47:17 matthew 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 29 #include <machine/bus.h> 30 #include <machine/endian.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 usbd_device_handle sc_udev; 42 usbd_interface_handle 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 int uberry_activate(struct device *, int); 61 62 void uberry_pearlmode(struct uberry_softc *); 63 void uberry_charge(struct uberry_softc *); 64 65 struct cfdriver uberry_cd = { 66 NULL, "uberry", DV_DULL 67 }; 68 69 const struct cfattach uberry_ca = { 70 sizeof(struct uberry_softc), 71 uberry_match, 72 uberry_attach, 73 uberry_detach, 74 uberry_activate, 75 }; 76 77 int 78 uberry_match(struct device *parent, void *match, void *aux) 79 { 80 struct usb_attach_arg *uaa = aux; 81 82 if (uaa->iface != NULL) 83 return UMATCH_NONE; 84 85 return (usb_lookup(uberry_devices, uaa->vendor, uaa->product) != NULL) ? 86 UMATCH_VENDOR_PRODUCT : UMATCH_NONE; 87 } 88 89 void 90 uberry_attach(struct device *parent, struct device *self, void *aux) 91 { 92 struct uberry_softc *sc = (struct uberry_softc *)self; 93 struct usb_attach_arg *uaa = aux; 94 usb_device_descriptor_t *dd; 95 96 sc->sc_udev = uaa->device; 97 98 dd = usbd_get_device_descriptor(uaa->device); 99 100 /* Enable configuration, to keep it connected... */ 101 if (usbd_set_config_no(sc->sc_udev, UBERRY_CONFIG_NO, 1) != 0) { 102 /* 103 * Really ancient (ie. 7250) devices when off will 104 * only charge at 100mA when turned off. 105 */ 106 printf("%s: Charging at %dmA\n", sc->sc_dev.dv_xname, 107 sc->sc_udev->power); 108 return; 109 } 110 111 printf("%s: Charging at %dmA", sc->sc_dev.dv_xname, 112 sc->sc_udev->power); 113 if (sc->sc_udev->power >= 250) 114 printf("\n"); 115 else { 116 printf("... requesting higher-power charging\n"); 117 uberry_charge(sc); 118 /* 119 * Older berry's will disconnect/reconnect at this 120 * point, and come back requesting higher power 121 */ 122 } 123 124 /* On the Pearl, request a change to Dual mode */ 125 if (UGETW(dd->idProduct) == USB_PRODUCT_RIM_PEARL) 126 uberry_pearlmode(sc); 127 128 /* Enable the device, then it cannot idle, and will charge */ 129 if (usbd_set_config_no(sc->sc_udev, UBERRY_CONFIG_NO, 1) != 0) { 130 printf("%s: could not set configuration no\n", 131 sc->sc_dev.dv_xname); 132 return; 133 } 134 135 if (UGETW(dd->idProduct) == USB_PRODUCT_RIM_PEARL) { 136 /* 137 * Pearl does not disconnect/reconnect by itself, 138 * and therefore needs to be told to reset, so that 139 * it can come back in Dual mode. 140 */ 141 usb_needs_reattach(sc->sc_udev); 142 } 143 } 144 145 int 146 uberry_detach(struct device *self, int flags) 147 { 148 /* struct uberry_softc *sc = (struct uberry_softc *)self; */ 149 150 return 0; 151 } 152 153 int 154 uberry_activate(struct device *self, int act) 155 { 156 struct uberry_softc *sc = (struct uberry_softc *)self; 157 158 switch (act) { 159 case DVACT_DEACTIVATE: 160 usbd_deactivate(sc->sc_udev); 161 break; 162 } 163 return 0; 164 } 165 166 void 167 uberry_pearlmode(struct uberry_softc *sc) 168 { 169 usb_device_request_t req; 170 char buffer[256]; 171 172 req.bmRequestType = UT_READ_VENDOR_DEVICE; 173 req.bRequest = 0xa9; 174 USETW(req.wValue, 1); 175 USETW(req.wIndex, 1); 176 USETW(req.wLength, 2); 177 (void) usbd_do_request(sc->sc_udev, &req, &buffer); 178 } 179 180 void 181 uberry_charge(struct uberry_softc *sc) 182 { 183 usb_device_request_t req; 184 char buffer[256]; 185 186 req.bmRequestType = UT_READ_VENDOR_DEVICE; 187 req.bRequest = 0xa5; 188 USETW(req.wValue, 0); 189 USETW(req.wIndex, 1); 190 USETW(req.wLength, 2); 191 (void) usbd_do_request(sc->sc_udev, &req, &buffer); 192 193 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 194 req.bRequest = 0xa2; 195 USETW(req.wValue, 0); 196 USETW(req.wIndex, 1); 197 USETW(req.wLength, 0); 198 (void) usbd_do_request(sc->sc_udev, &req, &buffer); 199 } 200