1 /* $OpenBSD: crosec.c,v 1.1 2015/01/26 02:48:24 bmercer Exp $ */ 2 /* 3 * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/device.h> 21 #include <sys/sensors.h> 22 #include <sys/malloc.h> 23 24 #include <armv7/exynos/crosecvar.h> 25 26 #ifdef DEBUG 27 #define DPRINTF(x) printf x 28 #else 29 #define DPRINTF(x) 30 #endif 31 32 int cros_ec_match(struct device *, void *, void *); 33 void cros_ec_attach(struct device *, struct device *, void *); 34 35 int cros_ec_send_command(struct cros_ec_softc *, uint8_t, 36 int, const void *, int, uint8_t **, int); 37 int cros_ec_command(struct cros_ec_softc *, uint8_t, 38 int, const void *, int, void *, int); 39 int cros_ec_command_inptr(struct cros_ec_softc *, uint8_t, 40 int, const void *, int, uint8_t **, int); 41 int cros_ec_i2c_command(struct cros_ec_softc *, uint8_t, 42 int, const uint8_t *, int, uint8_t **, int); 43 int cros_ec_calc_checksum(const uint8_t *, int); 44 45 struct cfattach crosec_ca = { 46 sizeof(struct cros_ec_softc), cros_ec_match, cros_ec_attach 47 }; 48 49 struct cfdriver crosec_cd = { 50 NULL, "crosec", DV_DULL 51 }; 52 53 int 54 cros_ec_match(struct device *parent, void *match, void *aux) 55 { 56 struct i2c_attach_args *ia = aux; 57 58 if (strcmp(ia->ia_name, "crosec") == 0) 59 return 1; 60 return 0; 61 } 62 63 void 64 cros_ec_attach(struct device *parent, struct device *self, void *aux) 65 { 66 struct cros_ec_softc *sc = (struct cros_ec_softc *)self; 67 struct i2c_attach_args *ia = aux; 68 69 sc->sc_tag = ia->ia_tag; 70 sc->sc_addr = ia->ia_addr; 71 72 printf("\n"); 73 74 if (cros_ec_check_version(sc)) { 75 printf("%s: could not initialize ChromeOS EC\n", __func__); 76 return; 77 } 78 79 if (cros_ec_init_keyboard(sc)) { 80 printf("%s: could not initialize keyboard\n", __func__); 81 return; 82 } 83 } 84 85 int 86 cros_ec_check_version(struct cros_ec_softc *sc) 87 { 88 struct ec_params_hello req; 89 struct ec_response_hello *resp; 90 91 sc->cmd_version_is_supported = 1; 92 if (cros_ec_command_inptr(sc, EC_CMD_HELLO, 0, &req, sizeof(req), 93 (uint8_t **)&resp, sizeof(*resp)) > 0) { 94 /* new version supported */ 95 sc->cmd_version_is_supported = 1; 96 } else { 97 printf("%s: old EC interface not supported\n", __func__); 98 return -1; 99 } 100 101 return 0; 102 } 103 104 int 105 cros_ec_i2c_command(struct cros_ec_softc *sc, uint8_t cmd, int cmd_version, 106 const uint8_t *out, int out_len, uint8_t **in, int in_len) 107 { 108 int out_bytes, in_bytes, ret; 109 uint8_t *ptr = sc->out; 110 uint8_t *inptr = sc->in; 111 112 inptr += sizeof(uint64_t); /* returned data should be 64-bit aligned */ 113 if (!sc->cmd_version_is_supported) { 114 /* old-style */ 115 *ptr++ = cmd; 116 out_bytes = out_len + 1; 117 in_bytes = in_len + 2; 118 inptr--; /* status byte */ 119 } else { 120 /* new-style */ 121 *ptr++ = EC_CMD_VERSION0 + cmd_version; 122 *ptr++ = cmd; 123 *ptr++ = out_len; 124 out_bytes = out_len + 4; 125 in_bytes = in_len + 3; 126 inptr -= 2; /* status byte, length */ 127 } 128 memcpy(ptr, out, out_len); 129 ptr += out_len; 130 131 if (sc->cmd_version_is_supported) 132 *ptr++ = (uint8_t) 133 cros_ec_calc_checksum(sc->out, out_len + 3); 134 135 iic_acquire_bus(sc->sc_tag, 0); 136 ret = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 137 sc->sc_addr, NULL, 0, &sc->out, out_bytes, 0); 138 if (ret) { 139 DPRINTF(("%s: I2C write failed\n", __func__)); 140 iic_release_bus(sc->sc_tag, 0); 141 return -1; 142 } 143 144 ret = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 145 sc->sc_addr, NULL, 0, inptr, in_bytes, 0); 146 if (ret) { 147 DPRINTF(("%s: I2C read failed\n", __func__)); 148 iic_release_bus(sc->sc_tag, 0); 149 return -1; 150 } 151 152 iic_release_bus(sc->sc_tag, 0); 153 154 if (*inptr != EC_RES_SUCCESS) { 155 DPRINTF(("%s: bad result\n", __func__)); 156 return -(int)*inptr; 157 } 158 159 if (sc->cmd_version_is_supported) { 160 int len, csum; 161 162 len = inptr[1]; 163 if (len > sizeof(sc->in)) { 164 DPRINTF(("%s: Received length too large\n", __func__)); 165 return -1; 166 } 167 csum = cros_ec_calc_checksum(inptr, 2 + len); 168 if (csum != inptr[2 + len]) { 169 DPRINTF(("%s: Invalid checksum\n", __func__)); 170 return -1; 171 } 172 in_len = min(in_len, len); 173 } 174 175 *in = sc->in + sizeof(uint64_t); 176 177 return in_len; 178 } 179 180 int 181 cros_ec_send_command(struct cros_ec_softc *sc, uint8_t cmd, int cmd_version, 182 const void *out, int out_len, uint8_t **in, int in_len) 183 { 184 return cros_ec_i2c_command(sc, cmd, cmd_version, 185 (const uint8_t *)out, out_len, in, in_len); 186 } 187 188 int 189 cros_ec_command(struct cros_ec_softc *sc, uint8_t cmd, 190 int cmd_version, const void *out, int out_len, 191 void *in, int in_len) { 192 uint8_t *in_buffer; 193 int len; 194 195 len = cros_ec_command_inptr(sc, cmd, cmd_version, out, out_len, 196 &in_buffer, in_len); 197 198 if (len > 0) { 199 if (in && in_buffer) { 200 len = min(in_len, len); 201 memmove(in, in_buffer, len); 202 } 203 } 204 return len; 205 } 206 207 int 208 cros_ec_command_inptr(struct cros_ec_softc *sc, uint8_t cmd, 209 int cmd_version, const void *out, int out_len, 210 uint8_t **inp, int in_len) { 211 uint8_t *in; 212 int len; 213 214 len = cros_ec_send_command(sc, cmd, cmd_version, 215 (const uint8_t *)out, out_len, &in, in_len); 216 217 /* Wait for the command to complete. */ 218 if (len == -EC_RES_IN_PROGRESS) { 219 struct ec_response_get_comms_status *resp; 220 221 do { 222 int ret; 223 224 delay(50000); 225 cros_ec_send_command(sc, EC_CMD_GET_COMMS_STATUS, 0, 226 NULL, 0, 227 (uint8_t **)&resp, sizeof(*resp)); 228 if (ret < 0) 229 return ret; 230 231 //timeout CROS_EC_CMD_TIMEOUT_MS 232 //return -EC_RES_TIMEOUT 233 } while (resp->flags & EC_COMMS_STATUS_PROCESSING); 234 235 /* Let's get the response. */ 236 len = cros_ec_send_command(sc, EC_CMD_RESEND_RESPONSE, 0, 237 out, out_len, &in, in_len); 238 } 239 240 if (inp != NULL) 241 *inp = in; 242 243 return len; 244 } 245 246 int 247 cros_ec_calc_checksum(const uint8_t *data, int size) 248 { 249 int csum, i; 250 251 for (i = csum = 0; i < size; i++) 252 csum += data[i]; 253 return csum & 0xff; 254 } 255 256 int 257 cros_ec_scan_keyboard(struct cros_ec_softc *sc, uint8_t *scan, int len) 258 { 259 if (cros_ec_command(sc, EC_CMD_CROS_EC_STATE, 0, NULL, 0, scan, 260 len) < len) 261 return -1; 262 263 return 0; 264 } 265 266 int 267 cros_ec_info(struct cros_ec_softc *sc, struct ec_response_cros_ec_info *info) 268 { 269 if (cros_ec_command(sc, EC_CMD_CROS_EC_INFO, 0, NULL, 0, info, 270 sizeof(*info)) < sizeof(*info)) 271 return -1; 272 273 return 0; 274 } 275