1 /* $OpenBSD: uberry.c,v 1.15 2007/10/11 18:33:14 deraadt 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/sysctl.h> 22 #include <sys/mbuf.h> 23 #include <sys/kernel.h> 24 #include <sys/socket.h> 25 #include <sys/systm.h> 26 #include <sys/timeout.h> 27 #include <sys/conf.h> 28 #include <sys/device.h> 29 30 #include <machine/bus.h> 31 #include <machine/endian.h> 32 #include <machine/intr.h> 33 34 #include <dev/usb/usb.h> 35 #include <dev/usb/usbdi.h> 36 #include <dev/usb/usbdivar.h> 37 #include <dev/usb/usbdi_util.h> 38 #include <dev/usb/usbdevs.h> 39 40 struct uberry_softc { 41 struct device sc_dev; 42 usbd_device_handle sc_udev; 43 usbd_interface_handle sc_iface; 44 }; 45 46 #define UBERRY_INTERFACE_NO 0 47 #define UBERRY_CONFIG_NO 1 48 49 /* 50 * Do not match on the following device, because it is type umass 51 * { USB_VENDOR_RIM, USB_PRODUCT_RIM_PEARL_DUAL }, 52 */ 53 struct usb_devno const uberry_devices[] = { 54 { USB_VENDOR_RIM, USB_PRODUCT_RIM_BLACKBERRY }, 55 { USB_VENDOR_RIM, USB_PRODUCT_RIM_PEARL } 56 }; 57 58 int uberry_match(struct device *, void *, void *); 59 void uberry_attach(struct device *, struct device *, void *); 60 int uberry_detach(struct device *, int); 61 int uberry_activate(struct device *, enum devact); 62 63 void uberry_pearlmode(struct uberry_softc *); 64 void uberry_charge(struct uberry_softc *); 65 66 struct cfdriver uberry_cd = { 67 NULL, "uberry", DV_DULL 68 }; 69 70 const struct cfattach uberry_ca = { 71 sizeof(struct uberry_softc), 72 uberry_match, 73 uberry_attach, 74 uberry_detach, 75 uberry_activate, 76 }; 77 78 int 79 uberry_match(struct device *parent, void *match, void *aux) 80 { 81 struct usb_attach_arg *uaa = aux; 82 83 if (uaa->iface != NULL) 84 return UMATCH_NONE; 85 86 return (usb_lookup(uberry_devices, uaa->vendor, uaa->product) != NULL) ? 87 UMATCH_VENDOR_PRODUCT : UMATCH_NONE; 88 } 89 90 void 91 uberry_attach(struct device *parent, struct device *self, void *aux) 92 { 93 struct uberry_softc *sc = (struct uberry_softc *)self; 94 struct usb_attach_arg *uaa = aux; 95 usb_device_descriptor_t *dd; 96 97 sc->sc_udev = uaa->device; 98 99 dd = usbd_get_device_descriptor(uaa->device); 100 101 /* Enable configuration, to keep it connected... */ 102 if (usbd_set_config_no(sc->sc_udev, UBERRY_CONFIG_NO, 1) != 0) { 103 /* 104 * Really ancient (ie. 7250) devices when off will 105 * only charge at 100mA when turned off. 106 */ 107 printf("%s: Charging at %dmA\n", sc->sc_dev.dv_xname, 108 sc->sc_udev->power); 109 return; 110 } 111 112 printf("%s: Charging at %dmA", sc->sc_dev.dv_xname, 113 sc->sc_udev->power); 114 if (sc->sc_udev->power >= 250) 115 printf("\n"); 116 else { 117 printf("... requesting higher-power charging\n"); 118 uberry_charge(sc); 119 /* 120 * Older berry's will disconnect/reconnect at this 121 * point, and come back requesting higher power 122 */ 123 } 124 125 /* On the Pearl, request a change to Dual mode */ 126 if (UGETW(dd->idProduct) == USB_PRODUCT_RIM_PEARL) 127 uberry_pearlmode(sc); 128 129 /* Enable the device, then it cannot idle, and will charge */ 130 if (usbd_set_config_no(sc->sc_udev, UBERRY_CONFIG_NO, 1) != 0) { 131 printf("%s: could not set configuration no\n", 132 sc->sc_dev.dv_xname); 133 return; 134 } 135 136 if (UGETW(dd->idProduct) == USB_PRODUCT_RIM_PEARL) { 137 /* 138 * Pearl does not disconnect/reconnect by itself, 139 * and therefore needs to be told to reset, so that 140 * it can come back in Dual mode. 141 */ 142 usb_needs_reattach(sc->sc_udev); 143 } 144 145 usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, 146 &sc->sc_dev); 147 } 148 149 int 150 uberry_detach(struct device *self, int flags) 151 { 152 struct uberry_softc *sc = (struct uberry_softc *)self; 153 154 usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, 155 &sc->sc_dev); 156 return 0; 157 } 158 159 int 160 uberry_activate(struct device *self, enum devact act) 161 { 162 switch (act) { 163 case DVACT_ACTIVATE: 164 break; 165 166 case DVACT_DEACTIVATE: 167 break; 168 } 169 return 0; 170 } 171 172 void 173 uberry_pearlmode(struct uberry_softc *sc) 174 { 175 usb_device_request_t req; 176 char buffer[256]; 177 178 req.bmRequestType = UT_READ_VENDOR_DEVICE; 179 req.bRequest = 0xa9; 180 USETW(req.wValue, 1); 181 USETW(req.wIndex, 1); 182 USETW(req.wLength, 2); 183 (void) usbd_do_request(sc->sc_udev, &req, &buffer); 184 } 185 186 void 187 uberry_charge(struct uberry_softc *sc) 188 { 189 usb_device_request_t req; 190 char buffer[256]; 191 192 req.bmRequestType = UT_READ_VENDOR_DEVICE; 193 req.bRequest = 0xa5; 194 USETW(req.wValue, 0); 195 USETW(req.wIndex, 1); 196 USETW(req.wLength, 2); 197 (void) usbd_do_request(sc->sc_udev, &req, &buffer); 198 199 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 200 req.bRequest = 0xa2; 201 USETW(req.wValue, 0); 202 USETW(req.wIndex, 1); 203 USETW(req.wLength, 0); 204 (void) usbd_do_request(sc->sc_udev, &req, &buffer); 205 } 206