xref: /openbsd-src/sys/dev/usb/uberry.c (revision 81508fe356eb7772a68118f65f91723ce5261d7d)
1*81508fe3Sjsg /*	$OpenBSD: uberry.c,v 1.25 2024/05/23 03:21:09 jsg Exp $	*/
2297adcccSderaadt 
3297adcccSderaadt /*-
4297adcccSderaadt  * Copyright (c) 2006 Theo de Raadt <deraadt@openbsd.org>
5297adcccSderaadt  *
6297adcccSderaadt  * Permission to use, copy, modify, and distribute this software for any
7297adcccSderaadt  * purpose with or without fee is hereby granted, provided that the above
8297adcccSderaadt  * copyright notice and this permission notice appear in all copies.
9297adcccSderaadt  *
10297adcccSderaadt  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11297adcccSderaadt  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12297adcccSderaadt  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13297adcccSderaadt  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14297adcccSderaadt  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15297adcccSderaadt  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16297adcccSderaadt  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17297adcccSderaadt  */
18297adcccSderaadt 
19297adcccSderaadt #include <sys/param.h>
20297adcccSderaadt #include <sys/systm.h>
21297adcccSderaadt #include <sys/device.h>
22297adcccSderaadt 
23297adcccSderaadt #include <machine/bus.h>
24297adcccSderaadt 
25297adcccSderaadt #include <dev/usb/usb.h>
26297adcccSderaadt #include <dev/usb/usbdi.h>
2701e61936Sderaadt #include <dev/usb/usbdivar.h>
28297adcccSderaadt #include <dev/usb/usbdi_util.h>
29297adcccSderaadt #include <dev/usb/usbdevs.h>
30297adcccSderaadt 
31297adcccSderaadt struct uberry_softc {
328c5d01eeSmk 	struct device			sc_dev;
33ab0b1be7Smglocker 	struct usbd_device		*sc_udev;
34ab0b1be7Smglocker 	struct usbd_interface		*sc_iface;
35297adcccSderaadt };
36297adcccSderaadt 
37fdedb769Sderaadt #define UBERRY_INTERFACE_NO		0
3801e61936Sderaadt #define UBERRY_CONFIG_NO		1
39297adcccSderaadt 
40f0462a21Sderaadt /*
41f0462a21Sderaadt  * Do not match on the following device, because it is type umass
42f0462a21Sderaadt  * { USB_VENDOR_RIM, USB_PRODUCT_RIM_PEARL_DUAL },
43f0462a21Sderaadt  */
4478315254Smbalmer struct usb_devno const uberry_devices[] = {
4501e61936Sderaadt 	{ USB_VENDOR_RIM, USB_PRODUCT_RIM_BLACKBERRY },
4601e61936Sderaadt 	{ USB_VENDOR_RIM, USB_PRODUCT_RIM_PEARL }
47297adcccSderaadt };
48297adcccSderaadt 
499f5f6d50Smbalmer int uberry_match(struct device *, void *, void *);
509f5f6d50Smbalmer void uberry_attach(struct device *, struct device *, void *);
519f5f6d50Smbalmer int uberry_detach(struct device *, int);
529f5f6d50Smbalmer 
5301e61936Sderaadt void uberry_pearlmode(struct uberry_softc *);
5401e61936Sderaadt void uberry_charge(struct uberry_softc *);
5501e61936Sderaadt 
569f5f6d50Smbalmer struct cfdriver uberry_cd = {
579f5f6d50Smbalmer 	NULL, "uberry", DV_DULL
589f5f6d50Smbalmer };
599f5f6d50Smbalmer 
609f5f6d50Smbalmer const struct cfattach uberry_ca = {
61cf8c8cdaSmpi 	sizeof(struct uberry_softc), uberry_match, uberry_attach, uberry_detach
629f5f6d50Smbalmer };
63297adcccSderaadt 
64de5d9ff0Sjsg int
uberry_match(struct device * parent,void * match,void * aux)65de5d9ff0Sjsg uberry_match(struct device *parent, void *match, void *aux)
66297adcccSderaadt {
67de5d9ff0Sjsg 	struct usb_attach_arg *uaa = aux;
68297adcccSderaadt 
69f4b7d08eSmpi 	if (uaa->iface == NULL || uaa->configno != UBERRY_CONFIG_NO)
70297adcccSderaadt 		return UMATCH_NONE;
71297adcccSderaadt 
72297adcccSderaadt 	return (usb_lookup(uberry_devices, uaa->vendor, uaa->product) != NULL) ?
73297adcccSderaadt 	    UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
74297adcccSderaadt }
75297adcccSderaadt 
76de5d9ff0Sjsg void
uberry_attach(struct device * parent,struct device * self,void * aux)77de5d9ff0Sjsg uberry_attach(struct device *parent, struct device *self, void *aux)
78297adcccSderaadt {
79de5d9ff0Sjsg 	struct uberry_softc *sc = (struct uberry_softc *)self;
80de5d9ff0Sjsg 	struct usb_attach_arg *uaa = aux;
8101e61936Sderaadt 	usb_device_descriptor_t *dd;
82297adcccSderaadt 
83297adcccSderaadt 	sc->sc_udev = uaa->device;
84297adcccSderaadt 
8501e61936Sderaadt 	dd = usbd_get_device_descriptor(uaa->device);
8601e61936Sderaadt 
8701e61936Sderaadt 	printf("%s: Charging at %dmA", sc->sc_dev.dv_xname,
8801e61936Sderaadt 	    sc->sc_udev->power);
8901e61936Sderaadt 	if (sc->sc_udev->power >= 250)
9001e61936Sderaadt 		printf("\n");
9101e61936Sderaadt 	else {
9201e61936Sderaadt 		printf("... requesting higher-power charging\n");
9301e61936Sderaadt 		uberry_charge(sc);
94fdedb769Sderaadt 		/*
95fdedb769Sderaadt 		 * Older berry's will disconnect/reconnect at this
96fdedb769Sderaadt 		 * point, and come back requesting higher power
97fdedb769Sderaadt 		 */
98fdedb769Sderaadt 	}
99fdedb769Sderaadt 
100fdedb769Sderaadt 	/* On the Pearl, request a change to Dual mode */
101fdedb769Sderaadt 	if (UGETW(dd->idProduct) == USB_PRODUCT_RIM_PEARL)
102fdedb769Sderaadt 		uberry_pearlmode(sc);
103fdedb769Sderaadt 
104fdedb769Sderaadt 	/* Enable the device, then it cannot idle, and will charge */
105fdedb769Sderaadt 	if (usbd_set_config_no(sc->sc_udev, UBERRY_CONFIG_NO, 1) != 0) {
106fdedb769Sderaadt 		printf("%s: could not set configuration no\n",
107fdedb769Sderaadt 		    sc->sc_dev.dv_xname);
108fdedb769Sderaadt 		return;
10901e61936Sderaadt 	}
11001e61936Sderaadt 
11101e61936Sderaadt 	if (UGETW(dd->idProduct) == USB_PRODUCT_RIM_PEARL) {
112fdedb769Sderaadt 		/*
113fdedb769Sderaadt 		 * Pearl does not disconnect/reconnect by itself,
114fdedb769Sderaadt 		 * and therefore needs to be told to reset, so that
115fdedb769Sderaadt 		 * it can come back in Dual mode.
116fdedb769Sderaadt 		 */
11701e61936Sderaadt 		usb_needs_reattach(sc->sc_udev);
11801e61936Sderaadt 	}
119297adcccSderaadt }
120297adcccSderaadt 
121de5d9ff0Sjsg int
uberry_detach(struct device * self,int flags)122de5d9ff0Sjsg uberry_detach(struct device *self, int flags)
123297adcccSderaadt {
1240036ea2dSjakemsr 	/* struct uberry_softc *sc = (struct uberry_softc *)self; */
125f9bafeb6Smiod 
126297adcccSderaadt 	return 0;
127297adcccSderaadt }
128297adcccSderaadt 
12901e61936Sderaadt void
uberry_pearlmode(struct uberry_softc * sc)13001e61936Sderaadt uberry_pearlmode(struct uberry_softc *sc)
13101e61936Sderaadt {
13201e61936Sderaadt 	usb_device_request_t req;
13301e61936Sderaadt 	char buffer[256];
13401e61936Sderaadt 
13501e61936Sderaadt 	req.bmRequestType = UT_READ_VENDOR_DEVICE;
13601e61936Sderaadt 	req.bRequest = 0xa9;
13701e61936Sderaadt 	USETW(req.wValue, 1);
13801e61936Sderaadt 	USETW(req.wIndex, 1);
13901e61936Sderaadt 	USETW(req.wLength, 2);
14001e61936Sderaadt 	(void) usbd_do_request(sc->sc_udev, &req, &buffer);
14101e61936Sderaadt }
14201e61936Sderaadt 
14301e61936Sderaadt void
uberry_charge(struct uberry_softc * sc)14401e61936Sderaadt uberry_charge(struct uberry_softc *sc)
14501e61936Sderaadt {
14601e61936Sderaadt 	usb_device_request_t req;
14701e61936Sderaadt 	char buffer[256];
14801e61936Sderaadt 
14901e61936Sderaadt 	req.bmRequestType = UT_READ_VENDOR_DEVICE;
15001e61936Sderaadt 	req.bRequest = 0xa5;
15101e61936Sderaadt 	USETW(req.wValue, 0);
15201e61936Sderaadt 	USETW(req.wIndex, 1);
15301e61936Sderaadt 	USETW(req.wLength, 2);
15401e61936Sderaadt 	(void) usbd_do_request(sc->sc_udev, &req, &buffer);
15501e61936Sderaadt 
15601e61936Sderaadt 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
15701e61936Sderaadt 	req.bRequest = 0xa2;
15801e61936Sderaadt 	USETW(req.wValue, 0);
15901e61936Sderaadt 	USETW(req.wIndex, 1);
16001e61936Sderaadt 	USETW(req.wLength, 0);
16101e61936Sderaadt 	(void) usbd_do_request(sc->sc_udev, &req, &buffer);
16201e61936Sderaadt }
163