/** * Read \a _buf lenght \a size byte from serial flash memmory. * * For read in serial flash memory we * enble cs pin and send one byte of read opcode, * and then 3 byte of address of memory cell we * want to read. After the last byte of address we * can read data from so pin. * * \return the number of bytes read. */ static size_t flash25_read(struct KFile *_fd, void *buf, size_t size) { uint8_t *data = (uint8_t *)buf; Flash25 *fd = FLASH25_CAST(_fd); ASSERT(fd->fd.seek_pos + (kfile_off_t)size <= fd->fd.size); size = MIN((kfile_off_t)size, fd->fd.size - fd->fd.seek_pos); //kprintf("Reading at addr[%lu], size[%d]\n", fd->seek_pos, size); SS_ACTIVE(); kfile_putc(FLASH25_READ, fd->channel); /* * Address that we want to read. */ kfile_putc((fd->fd.seek_pos >> 16) & 0xFF, fd->channel); kfile_putc((fd->fd.seek_pos >> 8) & 0xFF, fd->channel); kfile_putc(fd->fd.seek_pos & 0xFF, fd->channel); kfile_read(fd->channel, data, size); SS_INACTIVE(); fd->fd.seek_pos += size; return size; }
/** * Generic getc() implementation using \a fd->read. */ int kfile_getc(struct KFile *fd) { unsigned char c; if (kfile_read(fd, &c, sizeof(c)) == sizeof(c)) return (int)((unsigned char)c); else return EOF; }
/** * KFile read/write subtest. * Try to write/read in the same \a f file location \a size bytes. * \return true if all is ok, false otherwise * \note Restore file position at exit (if no error) * \note Test buffer \a buf must be filled with * the following statement: * \code * buf[i] = i & 0xff * \endcode */ static bool kfile_rwTest(KFile *f, uint8_t *buf, size_t size) { /* * Write test buffer */ if (kfile_write(f, buf, size) != size) { LOG_ERR("error writing buf"); return false; } kfile_seek(f, -(kfile_off_t)size, KSM_SEEK_CUR); /* * Reset test buffer */ memset(buf, 0, size); /* * Read file in test buffer */ if (kfile_read(f, buf, size) != size) { LOG_ERR("error reading buf"); return false; } kfile_seek(f, -(kfile_off_t)size, KSM_SEEK_CUR); /* * Check test result */ for (size_t i = 0; i < size; i++) { if (buf[i] != (i & 0xff)) { LOG_ERR("error comparing at index [%d] read [%02x] expected [%02x]\n", i, buf[i], i); return false; } } return true; }
kfile_off_t kfile_copy(KFile *src, KFile *dst, kfile_off_t size) { char buf[32]; kfile_off_t cp_len = 0; while (size) { size_t len = MIN(sizeof(buf), (size_t)size); if (kfile_read(src, buf, len) != len) break; size_t wr_len = kfile_write(dst, buf, len); cp_len += wr_len; size -= len; if (wr_len != len) break; } return cp_len; }
/** * \brief Transmit some data using the XModem protocol. * * \param ch Channel to use for transfer * \param fd Source file * * \note This function allocates a large amount of stack for * the XModem transfer buffer (\see XM_BUFSIZE). */ bool xmodem_send(KFile *ch, KFile *fd) { char block_buffer[XM_BUFSIZE]; /* Buffer to hold a block of data */ size_t size = -1; int blocknr = 1, retries = 0, c, i; bool proceed, usecrc = false; uint16_t crc; uint8_t sum; /* * Reading a block can be very slow, so we read the first block early * to avoid receiving double XM_C char. * This could happen if we check for XM_C and then read the block, giving * the receiving device time to send another XM_C char misinterpretating * the blocks sent. */ size = kfile_read(fd, block_buffer, XM_BUFSIZE); kfile_clearerr(ch); LOG_INFO("Wait remote host\n"); for(;;) { proceed = false; do { if (XMODEM_CHECK_ABORT) return false; switch (c = kfile_getc(ch)) { case XM_NAK: LOG_INFO("Resend blk %d\n", blocknr); proceed = true; break; case XM_C: if (c == XM_C) { LOG_INFO("Tx start (CRC)\n"); usecrc = true; } else { LOG_INFO("Tx start (BCC)\n"); } proceed = true; break; case XM_ACK: /* End of transfer? */ if (!size) return true; /* Call user function to read in one block */ size = kfile_read(fd, block_buffer, XM_BUFSIZE); LOG_INFO("Send blk %d\n", blocknr); blocknr++; retries = 0; proceed = true; break; case EOF: kfile_clearerr(ch); retries++; LOG_INFO("Retries %d\n", retries); if (retries <= CONFIG_XMODEM_MAXRETRIES) break; /* falling through! */ case XM_CAN: LOG_INFO("Transfer aborted\n"); return false; default: LOG_INFO("Skipping garbage\n"); break; } } while (!proceed); if (!size) { kfile_putc(XM_EOT, ch); continue; } /* Pad block with 0xFF if it's partially full */ memset(block_buffer + size, 0xFF, XM_BUFSIZE - size); /* Send block header (STX, blocknr, ~blocknr) */ #if XM_BUFSIZE == 128 kfile_putc(XM_SOH, ch); #else kfile_putc(XM_STX, ch); #endif kfile_putc(blocknr & 0xFF, ch); kfile_putc(~blocknr & 0xFF, ch); /* Send block and compute its CRC/checksum */ sum = 0; crc = 0; for (i = 0; i < XM_BUFSIZE; i++) { kfile_putc(block_buffer[i], ch); crc = UPDCRC16(block_buffer[i], crc); sum += block_buffer[i]; } /* Send CRC/Checksum */ if (usecrc) { kfile_putc(crc >> 8, ch); kfile_putc(crc & 0xFF, ch); } else kfile_putc(sum, ch); }
/* * ext3301: modified to encrypt/decrypt files moving to/from * an encrypted folder. * Rename still succeeds if an encrypt/decrypt operation fails. */ static int ext2_rename (struct inode * old_dir, struct dentry * old_dentry, struct inode * new_dir, struct dentry * new_dentry ) { struct inode * old_inode = old_dentry->d_inode; struct inode * new_inode = new_dentry->d_inode; struct page * dir_page = NULL; struct ext2_dir_entry_2 * dir_de = NULL; struct page * old_page; struct ext2_dir_entry_2 * old_de; int err = -ENOENT; bool is_encryptable, src_encrypt, dest_encrypt; int i; struct file * fcrypt; ssize_t nchunk, nread, nwritten; loff_t fpos, fseekpos; unsigned int fsize, fremaining; char * buf, * strbuf1, * strbuf2, * path_src, * path_dest; size_t blocksize = INODE_BLKSIZE(old_inode); // dquot_initialize(old_dir); dquot_initialize(new_dir); old_de = ext2_find_entry (old_dir, &old_dentry->d_name, &old_page); if (!old_de) goto out; if (S_ISDIR(old_inode->i_mode)) { err = -EIO; dir_de = ext2_dotdot(old_inode, &dir_page); if (!dir_de) goto out_old; } if (new_inode) { struct page *new_page; struct ext2_dir_entry_2 *new_de; err = -ENOTEMPTY; if (dir_de && !ext2_empty_dir (new_inode)) goto out_dir; err = -ENOENT; new_de = ext2_find_entry (new_dir, &new_dentry->d_name, &new_page); if (!new_de) goto out_dir; ext2_set_link(new_dir, new_de, new_page, old_inode, 1); new_inode->i_ctime = CURRENT_TIME_SEC; if (dir_de) drop_nlink(new_inode); inode_dec_link_count(new_inode); } else { err = ext2_add_link(new_dentry, old_inode); if (err) goto out_dir; if (dir_de) inode_inc_link_count(new_dir); } // allocate buffers strbuf1 = kmalloc((size_t)512, GFP_KERNEL); strbuf2 = kmalloc((size_t)512, GFP_KERNEL); buf = kmalloc(blocksize, GFP_KERNEL); if (!buf || !strbuf1 || !strbuf2) return -ENOMEM; // check if the source XOR destination lie under /encrypt, // and both entries are regular or immediate files is_encryptable = (I_ISIM(old_inode) || I_ISREG(old_inode)); src_encrypt = ext3301_isencrypted(old_dentry); dest_encrypt = ext3301_isencrypted(new_dentry); path_src = ext3301_getpath(old_dentry, strbuf1, blocksize); path_dest = ext3301_getpath(new_dentry, strbuf2, blocksize); // decide whether to encrypt dbg(KERN_DEBUG "rename (%s --> %s)\n", path_src, path_dest); if (is_encryptable) { dbg_cr(KERN_DEBUG "- File encryptable type (regular/immediate)\n"); if (src_encrypt && dest_encrypt) { dbg_cr(KERN_DEBUG "- File moving inside /encrypt (no change))\n"); } else if (src_encrypt) { dbg_cr(KERN_DEBUG "- File moving out of /encrypt. Decrypting..\n"); goto cryptstart; } else if (dest_encrypt) { dbg_cr(KERN_DEBUG "- File moving into /encrypt. Encrypting..\n"); goto cryptstart; } else { dbg_cr(KERN_DEBUG "- Src/dest directories not encryptable\n"); } } else { dbg_cr(KERN_DEBUG "- File not an encryptable type\n"); } goto cryptdone; /* encrypt/decrypt file */ cryptstart: // open file if (!path_src) goto cryptfail; fcrypt = kfile_open(path_src, O_RDWR); if (!fcrypt) goto cryptfail; fsize = FILP_FSIZE(fcrypt); fremaining = fsize; fpos = 0; fseekpos = 0; dbg_cr(KERN_DEBUG " - Opened %s (Fsize: %d)\n", FILP_NAME(fcrypt), fsize); // special case: nothing to encrypt if (fsize==0) goto cryptclose; // loop: read, encrypt, write while (fremaining > 0) { // choose a chunk size nchunk = (fremaining > blocksize ? blocksize : (ssize_t)fremaining); dbg_cr(KERN_DEBUG " - Starting a %d-byte chunk at pos %u.\n", (int)nchunk, (unsigned int)fpos); // read a chunk; make sure we read all bytes requested. fpos = fseekpos; nread = kfile_read(fcrypt, buf, (size_t)nchunk, &fpos); // this inequality covers error conditions (nread<0) and // partial reads (0<=nread<=nchunk && nread != nchunk) if (nread != nchunk) { kfile_close(fcrypt); goto cryptfail; } // encrypt the buffer for (i=0; i<nchunk; i++) buf[i] ^= crypter_key; // write the chunk back fpos = fseekpos; nwritten = kfile_write(fcrypt, buf, (size_t)nchunk, &fpos); if (nwritten != nchunk) { kfile_close(fcrypt); goto cryptfail; } // move the file marker forward, decrease the #bytes remaining fseekpos += nchunk; fremaining -= nchunk; } // sync the read/write operations to disk. Very important! kfile_sync(fcrypt); cryptclose: kfile_close(fcrypt); goto cryptdone; cryptfail: // encrypt/decrypt failed if (dest_encrypt) printk(KERN_WARNING "Crypting file entering /%s failed: ino %lu\n", crypter_dir, INODE_INO(old_inode)); else if (src_encrypt) printk(KERN_WARNING "Decrypting file leaving /%s failed: ino %lu\n", crypter_dir, INODE_INO(old_inode)); goto cryptdone; cryptdone: /* * Like most other Unix systems, set the ctime for inodes on a * rename. */ old_inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(old_inode); ext2_delete_entry (old_de, old_page); if (dir_de) { if (old_dir != new_dir) ext2_set_link(old_inode, dir_de, dir_page, new_dir, 0); else { kunmap(dir_page); page_cache_release(dir_page); } inode_dec_link_count(old_dir); } goto out_free; out_dir: if (dir_de) { kunmap(dir_page); page_cache_release(dir_page); } out_old: kunmap(old_page); page_cache_release(old_page); out: return err; out_free: // free buffers kfree(strbuf1); kfree(strbuf2); kfree(buf); return 0; }
/** * KFile read/write test. * This function write and read \a test_buf long \a size * on \a fd handler. * \a save_buf can be NULL or a buffer where to save previous file content. */ int kfile_testRunGeneric(KFile *fd, uint8_t *test_buf, uint8_t *save_buf, size_t size) { /* * Part of test buf size that you would write. * This var is used in test 3 to check kfile_write * when writing beyond filesize limit. */ kfile_off_t len = size / 2; /* Fill test buffer */ for (size_t i = 0; i < size; i++) test_buf[i] = (i & 0xff); /* * If necessary, user can save content, * for later restore. */ if (save_buf) { LOG_INFO("Saved content..form [%ld] to [%ld]\n", fd->seek_pos, fd->seek_pos + size); kfile_read(fd, save_buf, size); } /* TEST 1 BEGIN. */ LOG_INFO("Test 1: write from pos 0 to [%ld]\n", (long)size); /* * Seek to addr 0. */ if (kfile_seek(fd, 0, KSM_SEEK_SET) != 0) goto kfile_test_end; /* * Test read/write to address 0..size */ if (!kfile_rwTest(fd, test_buf, size)) goto kfile_test_end; LOG_INFO("Test 1: ok!\n"); /* * Restore previous read content. */ if (save_buf) { kfile_seek(fd, 0, KSM_SEEK_SET); if (kfile_write(fd, save_buf, size) != size) goto kfile_test_end; LOG_INFO("Restore content..form [%ld] to [%ld]\n", fd->seek_pos, fd->seek_pos + size); } /* TEST 1 END. */ /* TEST 2 BEGIN. */ LOG_INFO("Test 2: write from pos [%ld] to [%ld]\n", fd->size/2 , fd->size/2 + size); /* * Go to half test size. */ kfile_seek(fd, (fd->size / 2), KSM_SEEK_SET); /* * If necessary, user can save content * for later restore. */ if (save_buf) { kfile_read(fd, save_buf, size); kfile_seek(fd, -(kfile_off_t)size, KSM_SEEK_CUR); LOG_INFO("Saved content..form [%ld] to [%ld]\n", fd->seek_pos, fd->seek_pos + size); } /* * Test read/write to address filesize/2 ... filesize/2 + size */ if (!kfile_rwTest(fd, test_buf, size)) goto kfile_test_end; LOG_INFO("Test 2: ok!\n"); /* * Restore previous content. */ if (save_buf) { kfile_seek(fd, -(kfile_off_t)size, KSM_SEEK_CUR); if (kfile_write(fd, save_buf, size) != size) goto kfile_test_end; LOG_INFO("Restore content..form [%ld] to [%ld]\n", fd->seek_pos, fd->seek_pos + size); } /* TEST 2 END. */ /* TEST 3 BEGIN. */ LOG_INFO("Test 3: write outside of fd->size limit [%ld]\n", fd->size); /* * Go to the Flash end */ kfile_seek(fd, -len, KSM_SEEK_END); /* * If necessary, user can save content, * for later restore. */ if (save_buf) { kfile_read(fd, save_buf, len); kfile_seek(fd, -len, KSM_SEEK_CUR); LOG_INFO("Saved content..form [%ld] to [%ld]\n", fd->seek_pos, fd->seek_pos + len); } /* * Test read/write to address (filesize - size) ... filesize */ if (kfile_rwTest(fd, test_buf, size)) goto kfile_test_end; kprintf("Test 3: ok!\n"); /* * Restore previous read content */ if (save_buf) { kfile_seek(fd, -len, KSM_SEEK_END); if ((kfile_off_t)kfile_write(fd, save_buf, len) != len) goto kfile_test_end; LOG_INFO("Restore content..form [%ld] to [%ld]\n", fd->seek_pos, fd->seek_pos + len); } /* TEST 3 END. */ kfile_close(fd); return 0; kfile_test_end: kfile_close(fd); LOG_ERR("One kfile_test failed!\n"); return EOF; }
/** * @return 1 if a valid message has been retrived, 0 on no valid message, -1 * on parsing error. */ int8_t gsmSMSByIndex(gsmSMSMessage_t * msg, uint8_t index) { char c; uint8_t i; char buff[13]; char *text; // SMS indexes are 1..10 if (!index || index>10) return 0; // Get the SMS message by the specified index sprintf(buff, "AT+CMGR=%d", index); _gsmWriteLine(buff); // Example responce: // +CMGR: "REC READ","+393357963938","","10/12/14,22:59:15+04"<0D><0A> // $NUM+393473153808$NUM+3355763944$RES$MSG:PiazzaleLargo e Lungo, Milano, Italy$12345<0D> // <0D><0A> // <0D><0A> // 0<0D> //***** Reading message Type, record format: // +CMGR: <TYPE> // wherem TYPE is: // Type Bytes Description // "REC UNREAD" 12 Received unread messages // "REC READ" 10 Received read messages // "STO UNSENT" 12 Stored unsent messages // "STO SENT" 10 Stored sent messages // Minimum unique substring: 6 Bytes // Thus, to parse the actual type, we read: // "+CMGR: " + 6Bytes = 13Bytes // Check if this message index is empty // In this case it is returned just "0<0D>" c = kfile_getc(&(gsm->fd)); if (c == EOF) goto parse_error; if (c == '0') { msg->from[0] = 0; msg->time[0] = 0; msg->text[0] = 0; LOG_INFO("SMS, P: %d, EMPTY\n", index); return 0; } // Read the rest of the initial record identifier if (!kfile_read(&(gsm->fd), buff, 12)) goto parse_error; // TODO: Parse message type // //***** Sender number // Scanning for second '"', than parse the sender number for (i=2; i; ) { c = kfile_getc(&(gsm->fd)); if (c == EOF) goto parse_error; if (c=='"') i--; } // Save the sender number, till next '"' for (i=0; i<15; i++) { c = kfile_getc(&(gsm->fd)); if (c == EOF) goto parse_error; if (c=='"') break; msg->from[i] = c; } msg->from[i] = '\0'; //***** Timestamp parsing // Scanning for three '"', than parse the timestamp for (i=3; i; ) { c = kfile_getc(&(gsm->fd)); if (c == EOF) goto parse_error; if (c=='"') i--; } // Save the timestamp (20 Bytes + terminator) if (!kfile_read(&(gsm->fd), msg->time, 20)) goto parse_error; msg->time[20] = '\0'; // Discard remaining chars till line termination ["<0D><0A>] if (!kfile_read(&(gsm->fd), buff, 3)) goto parse_error; //***** Message parsing text = msg->text; (*text) = kfile_getc(&(gsm->fd)); if ((*text) == EOF) goto parse_error; if ((*text) == '$') { // Scanning for first ':', than parse the message for (i=0; i<64; i++) { c = kfile_getc(&(gsm->fd)); if (c == EOF) goto parse_error; if (c==':') break; } // Save the message if (_gsmRead(text, 160) == -1) goto parse_error; } else { // We are already at the beginning of the message, // save the remainder of the text if (_gsmRead(text+1, 159) == -1) goto parse_error; } LOG_INFO("SMS, P: %d, T: %s, N: %s, M: %s\n", index, msg->time, msg->from, msg->text); return 1; parse_error: gsmDebug("Parse FAILED\n"); return -1; }