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