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