xref: /openbsd-src/sys/dev/usb/uberry.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
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