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