1 #include <minix/blockdriver.h> 2 #include <minix/com.h> 3 #include <minix/drivers.h> 4 #include <minix/ds.h> 5 #include <minix/i2c.h> 6 #include <minix/i2cdriver.h> 7 #include <minix/log.h> 8 9 #include <stdio.h> 10 #include <stdlib.h> 11 12 /* constants */ 13 #define NR_DEVS 1 /* number of devices this driver handles */ 14 #define EEPROM_DEV 0 /* index of eeprom device */ 15 16 /* When passing data over a grant one needs to pass 17 * a buffer to sys_safecopy copybuff is used for that*/ 18 #define COPYBUF_SIZE 0x1000 /* 4k buff */ 19 static unsigned char copybuf[COPYBUF_SIZE]; 20 21 static i2c_addr_t valid_addrs[9] = { 22 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x00 23 }; 24 25 /* SEF functions and variables. */ 26 static void sef_local_startup(void); 27 static int sef_cb_init(int type, sef_init_info_t * info); 28 static int sef_cb_lu_state_save(int); 29 static int lu_state_restore(void); 30 31 /* libblockdriver callbacks */ 32 static int cat24c256_blk_open(devminor_t minor, int access); 33 static int cat24c256_blk_close(devminor_t minor); 34 static ssize_t cat24c256_blk_transfer(devminor_t minor, int do_write, 35 u64_t pos, endpoint_t endpt, iovec_t * iov, unsigned int count, int flags); 36 static int cat24c256_blk_ioctl(devminor_t minor, unsigned long request, 37 endpoint_t endpt, cp_grant_id_t grant, endpoint_t user_endpt); 38 static struct device *cat24c256_blk_part(devminor_t minor); 39 static void cat24c256_blk_other(message * m, int ipc_status); 40 41 /* Entry points into the device dependent code of block drivers. */ 42 struct blockdriver cat24c256_tab = { 43 .bdr_type = BLOCKDRIVER_TYPE_OTHER, 44 .bdr_open = cat24c256_blk_open, 45 .bdr_close = cat24c256_blk_close, 46 .bdr_transfer = cat24c256_blk_transfer, 47 .bdr_ioctl = cat24c256_blk_ioctl, /* always returns ENOTTY */ 48 .bdr_part = cat24c256_blk_part, 49 .bdr_other = cat24c256_blk_other /* for notify events from DS */ 50 }; 51 52 static int cat24c256_read128(uint16_t memaddr, void *buf, size_t buflen, int flags); 53 static int cat24c256_read(uint16_t memaddr, void *buf, size_t buflen, int flags); 54 static int cat24c256_write16(uint16_t memaddr, void *buf, size_t buflen, int flags); 55 static int cat24c256_write(uint16_t memaddr, void *buf, size_t buflen, int flags); 56 57 /* globals */ 58 59 /* counts the number of times a device file is open */ 60 static int openct[NR_DEVS]; 61 62 /* base and size of each device */ 63 static struct device geom[NR_DEVS]; 64 65 /* the bus that this device is on (counting starting at 1) */ 66 static uint32_t bus; 67 68 /* slave address of the device */ 69 static i2c_addr_t address; 70 71 /* endpoint for the driver for the bus itself. */ 72 static endpoint_t bus_endpoint; 73 74 /* logging - use with log_warn(), log_info(), log_debug(), log_trace(), etc */ 75 static struct log log = { 76 .name = "cat24c256", 77 .log_level = LEVEL_INFO, 78 .log_func = default_log 79 }; 80 81 static int 82 cat24c256_blk_open(devminor_t minor, int access) 83 { 84 log_trace(&log, "cat24c256_blk_open(%d,%d)\n", minor, access); 85 if (cat24c256_blk_part(minor) == NULL) { 86 return ENXIO; 87 } 88 89 openct[minor]++; 90 91 return OK; 92 } 93 94 static int 95 cat24c256_blk_close(devminor_t minor) 96 { 97 log_trace(&log, "cat24c256_blk_close(%d)\n", minor); 98 if (cat24c256_blk_part(minor) == NULL) { 99 return ENXIO; 100 } 101 102 if (openct[minor] < 1) { 103 log_warn(&log, "closing unopened device %d\n", minor); 104 return EINVAL; 105 } 106 openct[minor]--; 107 108 return OK; 109 } 110 111 static ssize_t 112 cat24c256_blk_transfer(devminor_t minor, int do_write, u64_t pos64, 113 endpoint_t endpt, iovec_t * iov, unsigned int nr_req, int flags) 114 { 115 /* Read or write one the driver's block devices. */ 116 unsigned int count; 117 struct device *dv; 118 u64_t dv_size; 119 int r; 120 u64_t position; 121 cp_grant_id_t grant; 122 ssize_t total = 0; 123 vir_bytes offset; 124 125 log_trace(&log, "cat24c256_blk_transfer()\n"); 126 127 /* Get minor device information. */ 128 dv = cat24c256_blk_part(minor); 129 if (dv == NULL) { 130 return ENXIO; 131 } 132 133 if (nr_req > NR_IOREQS) { 134 return EINVAL; 135 } 136 137 dv_size = dv->dv_size; 138 if (pos64 > dv_size) { 139 return OK; /* Beyond EOF */ 140 } 141 position = pos64; 142 offset = 0; 143 144 while (nr_req > 0) { 145 146 /* How much to transfer and where to / from. */ 147 count = iov->iov_size; 148 grant = (cp_grant_id_t) iov->iov_addr; 149 150 /* check for EOF */ 151 if (position >= dv_size) { 152 return total; 153 } 154 155 /* don't go past the end of the device */ 156 if (position + count > dv_size) { 157 count = dv_size - position; 158 } 159 160 /* don't overflow copybuf */ 161 if (count > COPYBUF_SIZE) { 162 count = COPYBUF_SIZE; 163 } 164 165 log_trace(&log, "transfering 0x%x bytes\n", count); 166 167 if (do_write) { 168 r = sys_safecopyfrom(endpt, grant, (vir_bytes) offset, 169 (vir_bytes) copybuf, count); 170 if (r != OK) { 171 log_warn(&log, "safecopyfrom failed\n"); 172 return EINVAL; 173 } 174 175 r = cat24c256_write(position, copybuf, count, flags); 176 if (r != OK) { 177 log_warn(&log, "write failed (r=%d)\n", r); 178 return r; 179 } 180 } else { 181 r = cat24c256_read(position, copybuf, count, flags); 182 if (r != OK) { 183 log_warn(&log, "read failed (r=%d)\n", r); 184 return r; 185 } 186 187 r = sys_safecopyto(endpt, grant, (vir_bytes) offset, 188 (vir_bytes) copybuf, count); 189 if (r != OK) { 190 log_warn(&log, "safecopyto failed\n"); 191 return EINVAL; 192 } 193 } 194 195 /* Book the number of bytes transferred. */ 196 position += count; 197 total += count; 198 offset += count; 199 200 /* only go on to the next iov when this one is full */ 201 if ((iov->iov_size -= count) == 0) { 202 iov++; 203 nr_req--; 204 offset = 0; 205 } 206 } 207 208 return total; 209 } 210 211 static int 212 cat24c256_blk_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt, 213 cp_grant_id_t grant, endpoint_t UNUSED(user_endpt)) 214 { 215 log_trace(&log, "cat24c256_blk_ioctl(%d)\n", minor); 216 /* no supported ioctls for this device */ 217 return ENOTTY; 218 } 219 220 static struct device * 221 cat24c256_blk_part(devminor_t minor) 222 { 223 log_trace(&log, "cat24c256_blk_part(%d)\n", minor); 224 225 if (minor < 0 || minor >= NR_DEVS) { 226 return NULL; 227 } 228 229 return &geom[minor]; 230 } 231 232 static void 233 cat24c256_blk_other(message * m, int ipc_status) 234 { 235 log_trace(&log, "cat24c256_blk_other(0x%x)\n", m->m_type); 236 237 if (is_ipc_notify(ipc_status)) { 238 if (m->m_source == DS_PROC_NR) { 239 log_debug(&log, 240 "bus driver changed state, update endpoint\n"); 241 i2cdriver_handle_bus_update(&bus_endpoint, bus, 242 address); 243 } 244 } else 245 log_warn(&log, "Invalid message type (0x%x)\n", m->m_type); 246 } 247 248 /* The lower level i2c interface can only read/write 128 bytes at a time. 249 * One might want to do more I/O than that at once w/EEPROM, so there is 250 * cat24c256_read() and cat24c256_read128(). cat24c256_read128() does the 251 * actual reading in chunks up to 128 bytes. cat24c256_read() splits 252 * the request up into chunks and repeatedly calls cat24c256_read128() 253 * until all of the requested EEPROM locations have been read. 254 */ 255 256 static int 257 cat24c256_read128(uint16_t memaddr, void *buf, size_t buflen, int flags) 258 { 259 int r; 260 minix_i2c_ioctl_exec_t ioctl_exec; 261 262 if (buflen > I2C_EXEC_MAX_BUFLEN || buf == NULL 263 || (((uint16_t) (memaddr + buflen)) < memaddr)) { 264 log_warn(&log, 265 "buflen exceeded max or buf == NULL or would overflow\n"); 266 return -1; 267 } 268 269 memset(&ioctl_exec, '\0', sizeof(minix_i2c_ioctl_exec_t)); 270 271 ioctl_exec.iie_op = I2C_OP_READ_WITH_STOP; 272 ioctl_exec.iie_addr = address; 273 274 /* set the memory address to read from */ 275 if ((BDEV_NOPAGE & flags) == BDEV_NOPAGE) { 276 /* reading within the current page */ 277 ioctl_exec.iie_cmd[0] = (memaddr & 0xff); 278 ioctl_exec.iie_cmdlen = 1; 279 } else { 280 ioctl_exec.iie_cmd[0] = ((memaddr >> 8) & 0xff); 281 ioctl_exec.iie_cmd[1] = (memaddr & 0xff); 282 ioctl_exec.iie_cmdlen = 2; 283 } 284 285 ioctl_exec.iie_buflen = buflen; 286 287 r = i2cdriver_exec(bus_endpoint, &ioctl_exec); 288 if (r != OK) { 289 return r; 290 } 291 292 /* call was good, copy results to caller's buffer */ 293 memcpy(buf, ioctl_exec.iie_buf, buflen); 294 295 log_debug(&log, "Read %d bytes from 0x%x 0x%x OK\n", buflen, 296 ioctl_exec.iie_cmd[0], ioctl_exec.iie_cmd[1]); 297 298 return OK; 299 } 300 301 int 302 cat24c256_read(uint16_t memaddr, void *buf, size_t buflen, int flags) 303 { 304 int r; 305 uint16_t i; 306 307 if (buf == NULL || ((memaddr + buflen) < memaddr)) { 308 log_warn(&log, "buf == NULL or would overflow\n"); 309 return -1; 310 } 311 312 for (i = 0; i < buflen; i += 128) { 313 314 r = cat24c256_read128(memaddr + i, buf + i, 315 ((buflen - i) < 128) ? (buflen - i) : 128, flags); 316 if (r != OK) { 317 return r; 318 } 319 320 log_trace(&log, "read %d bytes starting at 0x%x\n", 321 ((buflen - i) < 128) ? (buflen - i) : 128, memaddr + i); 322 } 323 324 return OK; 325 } 326 327 static int 328 cat24c256_write16(uint16_t memaddr, void *buf, size_t buflen, int flags) 329 { 330 int r; 331 int addrlen; 332 minix_i2c_ioctl_exec_t ioctl_exec; 333 334 if (buflen > (I2C_EXEC_MAX_BUFLEN - 2) || buf == NULL 335 || (((uint16_t) (memaddr + buflen + 2)) < memaddr)) { 336 log_warn(&log, 337 "buflen exceeded max or buf == NULL or would overflow\n"); 338 return -1; 339 } 340 341 memset(&ioctl_exec, '\0', sizeof(minix_i2c_ioctl_exec_t)); 342 343 ioctl_exec.iie_op = I2C_OP_WRITE_WITH_STOP; 344 ioctl_exec.iie_addr = address; 345 ioctl_exec.iie_cmdlen = 0; 346 347 /* set the memory address to write to */ 348 if ((BDEV_NOPAGE & flags) == BDEV_NOPAGE) { 349 /* writing within the current page */ 350 ioctl_exec.iie_buf[0] = (memaddr & 0xff); /* address */ 351 addrlen = 1; 352 } else { 353 ioctl_exec.iie_buf[0] = ((memaddr >> 8) & 0xff);/* page */ 354 ioctl_exec.iie_buf[1] = (memaddr & 0xff); /* address */ 355 addrlen = 2; 356 } 357 memcpy(ioctl_exec.iie_buf + addrlen, buf, buflen); 358 ioctl_exec.iie_buflen = buflen + addrlen; 359 360 r = i2cdriver_exec(bus_endpoint, &ioctl_exec); 361 if (r != OK) { 362 return r; 363 } 364 365 log_debug(&log, "Wrote %d bytes to 0x%x 0x%x OK - First = 0x%x\n", 366 buflen, ioctl_exec.iie_buf[0], ioctl_exec.iie_buf[1], 367 ioctl_exec.iie_buf[2]); 368 369 return OK; 370 } 371 372 int 373 cat24c256_write(uint16_t memaddr, void *buf, size_t buflen, int flags) 374 { 375 int r; 376 uint16_t i; 377 378 if (buf == NULL || ((memaddr + buflen) < memaddr)) { 379 log_warn(&log, "buf == NULL or would overflow\n"); 380 return -1; 381 } 382 383 for (i = 0; i < buflen; i += 16) { 384 385 r = cat24c256_write16(memaddr + i, buf + i, 386 ((buflen - i) < 16) ? (buflen - i) : 16, flags); 387 if (r != OK) { 388 return r; 389 } 390 391 log_trace(&log, "wrote %d bytes starting at 0x%x\n", 392 ((buflen - i) < 16) ? (buflen - i) : 16, memaddr + i); 393 } 394 395 return OK; 396 } 397 398 static int 399 sef_cb_lu_state_save(int UNUSED(state)) 400 { 401 ds_publish_u32("bus", bus, DSF_OVERWRITE); 402 ds_publish_u32("address", address, DSF_OVERWRITE); 403 return OK; 404 } 405 406 static int 407 lu_state_restore(void) 408 { 409 /* Restore the state. */ 410 u32_t value; 411 412 ds_retrieve_u32("bus", &value); 413 ds_delete_u32("bus"); 414 bus = (int) value; 415 416 ds_retrieve_u32("address", &value); 417 ds_delete_u32("address"); 418 address = (int) value; 419 420 return OK; 421 } 422 423 static int 424 sef_cb_init(int type, sef_init_info_t * UNUSED(info)) 425 { 426 int r; 427 428 if (type == SEF_INIT_LU) { 429 /* Restore the state. */ 430 lu_state_restore(); 431 } 432 433 geom[EEPROM_DEV].dv_base = ((u64_t) (0)); 434 geom[EEPROM_DEV].dv_size = ((u64_t) (32768)); 435 436 /* look-up the endpoint for the bus driver */ 437 bus_endpoint = i2cdriver_bus_endpoint(bus); 438 if (bus_endpoint == 0) { 439 log_warn(&log, "Couldn't find bus driver.\n"); 440 return EXIT_FAILURE; 441 } 442 443 /* claim the EEPROM device */ 444 r = i2cdriver_reserve_device(bus_endpoint, address); 445 if (r != OK) { 446 log_warn(&log, "Couldn't reserve device 0x%x (r=%d)\n", 447 address, r); 448 return EXIT_FAILURE; 449 } 450 451 if (type != SEF_INIT_LU) { 452 453 /* sign up for updates about the i2c bus going down/up */ 454 r = i2cdriver_subscribe_bus_updates(bus); 455 if (r != OK) { 456 log_warn(&log, "Couldn't subscribe to bus updates\n"); 457 return EXIT_FAILURE; 458 } 459 460 i2cdriver_announce(bus); 461 blockdriver_announce(type); 462 log_debug(&log, "announced\n"); 463 } 464 465 return OK; 466 } 467 468 static void 469 sef_local_startup(void) 470 { 471 /* 472 * Register init callbacks. Use the same function for all event types 473 */ 474 sef_setcb_init_fresh(sef_cb_init); 475 sef_setcb_init_lu(sef_cb_init); 476 sef_setcb_init_restart(sef_cb_init); 477 478 /* 479 * Register live update callbacks. 480 */ 481 /* Agree to update immediately when LU is requested in a valid state. */ 482 sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready); 483 /* Support live update starting from any standard state. */ 484 sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard); 485 /* Register a custom routine to save the state. */ 486 sef_setcb_lu_state_save(sef_cb_lu_state_save); 487 488 /* Let SEF perform startup. */ 489 sef_startup(); 490 } 491 492 int 493 main(int argc, char *argv[]) 494 { 495 int r; 496 497 env_setargs(argc, argv); 498 r = i2cdriver_env_parse(&bus, &address, valid_addrs); 499 if (r < 0) { 500 log_warn(&log, "Expecting -args 'bus=X address=0xYY'\n"); 501 log_warn(&log, "Example -args 'bus=3 address=0x54'\n"); 502 return EXIT_FAILURE; 503 } else if (r > 0) { 504 log_warn(&log, 505 "Invalid slave address for device, expecting 0x50-0x57\n"); 506 return EXIT_FAILURE; 507 } 508 509 sef_local_startup(); 510 511 log_debug(&log, "Startup Complete\n"); 512 blockdriver_task(&cat24c256_tab); 513 log_debug(&log, "Shutting down\n"); 514 515 return OK; 516 } 517