1 /* $OpenBSD: uberry.c,v 1.25 2024/05/23 03:21:09 jsg 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/systm.h>
21 #include <sys/device.h>
22
23 #include <machine/bus.h>
24
25 #include <dev/usb/usb.h>
26 #include <dev/usb/usbdi.h>
27 #include <dev/usb/usbdivar.h>
28 #include <dev/usb/usbdi_util.h>
29 #include <dev/usb/usbdevs.h>
30
31 struct uberry_softc {
32 struct device sc_dev;
33 struct usbd_device *sc_udev;
34 struct usbd_interface *sc_iface;
35 };
36
37 #define UBERRY_INTERFACE_NO 0
38 #define UBERRY_CONFIG_NO 1
39
40 /*
41 * Do not match on the following device, because it is type umass
42 * { USB_VENDOR_RIM, USB_PRODUCT_RIM_PEARL_DUAL },
43 */
44 struct usb_devno const uberry_devices[] = {
45 { USB_VENDOR_RIM, USB_PRODUCT_RIM_BLACKBERRY },
46 { USB_VENDOR_RIM, USB_PRODUCT_RIM_PEARL }
47 };
48
49 int uberry_match(struct device *, void *, void *);
50 void uberry_attach(struct device *, struct device *, void *);
51 int uberry_detach(struct device *, int);
52
53 void uberry_pearlmode(struct uberry_softc *);
54 void uberry_charge(struct uberry_softc *);
55
56 struct cfdriver uberry_cd = {
57 NULL, "uberry", DV_DULL
58 };
59
60 const struct cfattach uberry_ca = {
61 sizeof(struct uberry_softc), uberry_match, uberry_attach, uberry_detach
62 };
63
64 int
uberry_match(struct device * parent,void * match,void * aux)65 uberry_match(struct device *parent, void *match, void *aux)
66 {
67 struct usb_attach_arg *uaa = aux;
68
69 if (uaa->iface == NULL || uaa->configno != UBERRY_CONFIG_NO)
70 return UMATCH_NONE;
71
72 return (usb_lookup(uberry_devices, uaa->vendor, uaa->product) != NULL) ?
73 UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
74 }
75
76 void
uberry_attach(struct device * parent,struct device * self,void * aux)77 uberry_attach(struct device *parent, struct device *self, void *aux)
78 {
79 struct uberry_softc *sc = (struct uberry_softc *)self;
80 struct usb_attach_arg *uaa = aux;
81 usb_device_descriptor_t *dd;
82
83 sc->sc_udev = uaa->device;
84
85 dd = usbd_get_device_descriptor(uaa->device);
86
87 printf("%s: Charging at %dmA", sc->sc_dev.dv_xname,
88 sc->sc_udev->power);
89 if (sc->sc_udev->power >= 250)
90 printf("\n");
91 else {
92 printf("... requesting higher-power charging\n");
93 uberry_charge(sc);
94 /*
95 * Older berry's will disconnect/reconnect at this
96 * point, and come back requesting higher power
97 */
98 }
99
100 /* On the Pearl, request a change to Dual mode */
101 if (UGETW(dd->idProduct) == USB_PRODUCT_RIM_PEARL)
102 uberry_pearlmode(sc);
103
104 /* Enable the device, then it cannot idle, and will charge */
105 if (usbd_set_config_no(sc->sc_udev, UBERRY_CONFIG_NO, 1) != 0) {
106 printf("%s: could not set configuration no\n",
107 sc->sc_dev.dv_xname);
108 return;
109 }
110
111 if (UGETW(dd->idProduct) == USB_PRODUCT_RIM_PEARL) {
112 /*
113 * Pearl does not disconnect/reconnect by itself,
114 * and therefore needs to be told to reset, so that
115 * it can come back in Dual mode.
116 */
117 usb_needs_reattach(sc->sc_udev);
118 }
119 }
120
121 int
uberry_detach(struct device * self,int flags)122 uberry_detach(struct device *self, int flags)
123 {
124 /* struct uberry_softc *sc = (struct uberry_softc *)self; */
125
126 return 0;
127 }
128
129 void
uberry_pearlmode(struct uberry_softc * sc)130 uberry_pearlmode(struct uberry_softc *sc)
131 {
132 usb_device_request_t req;
133 char buffer[256];
134
135 req.bmRequestType = UT_READ_VENDOR_DEVICE;
136 req.bRequest = 0xa9;
137 USETW(req.wValue, 1);
138 USETW(req.wIndex, 1);
139 USETW(req.wLength, 2);
140 (void) usbd_do_request(sc->sc_udev, &req, &buffer);
141 }
142
143 void
uberry_charge(struct uberry_softc * sc)144 uberry_charge(struct uberry_softc *sc)
145 {
146 usb_device_request_t req;
147 char buffer[256];
148
149 req.bmRequestType = UT_READ_VENDOR_DEVICE;
150 req.bRequest = 0xa5;
151 USETW(req.wValue, 0);
152 USETW(req.wIndex, 1);
153 USETW(req.wLength, 2);
154 (void) usbd_do_request(sc->sc_udev, &req, &buffer);
155
156 req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
157 req.bRequest = 0xa2;
158 USETW(req.wValue, 0);
159 USETW(req.wIndex, 1);
160 USETW(req.wLength, 0);
161 (void) usbd_do_request(sc->sc_udev, &req, &buffer);
162 }
163