Linux Audio

Check our new training course

Loading...
  1/*
  2 * Cryptographic API.
  3 *
  4 * Cipher operations.
  5 *
  6 * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
  7 *               2002 Adam J. Richter <adam@yggdrasil.com>
  8 *               2004 Jean-Luc Cooke <jlcooke@certainkey.com>
  9 *
 10 * This program is free software; you can redistribute it and/or modify it
 11 * under the terms of the GNU General Public License as published by the Free
 12 * Software Foundation; either version 2 of the License, or (at your option)
 13 * any later version.
 14 *
 15 */
 16#include <linux/kernel.h>
 17#include <linux/mm.h>
 18#include <linux/pagemap.h>
 19#include <linux/highmem.h>
 20#include <asm/scatterlist.h>
 21#include "internal.h"
 22#include "scatterwalk.h"
 23
 24void *scatterwalk_whichbuf(struct scatter_walk *walk, unsigned int nbytes, void *scratch)
 25{
 26	if (nbytes <= walk->len_this_page &&
 27	    (((unsigned long)walk->data) & (PAGE_CACHE_SIZE - 1)) + nbytes <=
 28	    PAGE_CACHE_SIZE)
 29		return walk->data;
 30	else
 31		return scratch;
 32}
 33
 34static void memcpy_dir(void *buf, void *sgdata, size_t nbytes, int out)
 35{
 36	if (out)
 37		memcpy(sgdata, buf, nbytes);
 38	else
 39		memcpy(buf, sgdata, nbytes);
 40}
 41
 42void scatterwalk_start(struct scatter_walk *walk, struct scatterlist *sg)
 43{
 44	unsigned int rest_of_page;
 45
 46	walk->sg = sg;
 47
 48	walk->page = sg->page;
 49	walk->len_this_segment = sg->length;
 50
 51	rest_of_page = PAGE_CACHE_SIZE - (sg->offset & (PAGE_CACHE_SIZE - 1));
 52	walk->len_this_page = min(sg->length, rest_of_page);
 53	walk->offset = sg->offset;
 54}
 55
 56void scatterwalk_map(struct scatter_walk *walk)
 57{
 58	walk->data = kmap_atomic(walk->page) + walk->offset;
 59}
 60
 61static void scatterwalk_pagedone(struct scatter_walk *walk, int out,
 62				 unsigned int more)
 63{
 64	/* walk->data may be pointing the first byte of the next page;
 65	   however, we know we transferred at least one byte.  So,
 66	   walk->data - 1 will be a virtual address in the mapped page. */
 67
 68	if (out)
 69		flush_dcache_page(walk->page);
 70
 71	if (more) {
 72		walk->len_this_segment -= walk->len_this_page;
 73
 74		if (walk->len_this_segment) {
 75			walk->page++;
 76			walk->len_this_page = min(walk->len_this_segment,
 77						  (unsigned)PAGE_CACHE_SIZE);
 78			walk->offset = 0;
 79		}
 80		else
 81			scatterwalk_start(walk, sg_next(walk->sg));
 82	}
 83}
 84
 85void scatterwalk_done(struct scatter_walk *walk, int out, int more)
 86{
 87	crypto_kunmap(walk->data, out);
 88	if (walk->len_this_page == 0 || !more)
 89		scatterwalk_pagedone(walk, out, more);
 90}
 91
 92/*
 93 * Do not call this unless the total length of all of the fragments
 94 * has been verified as multiple of the block size.
 95 */
 96int scatterwalk_copychunks(void *buf, struct scatter_walk *walk,
 97			   size_t nbytes)
 98{
 99	if (buf != walk->data) {
100		while (nbytes > walk->len_this_page) {
101			memcpy_dir(buf, walk->data, walk->len_this_page, out);
102			buf += walk->len_this_page;
103			nbytes -= walk->len_this_page;
104
105			kunmap_atomic(walk->data);
106			scatterwalk_pagedone(walk, out, 1);
107			scatterwalk_map(walk);
108		}
109
110		memcpy_dir(buf, walk->data, nbytes, out);
111	}
112
113	walk->offset += nbytes;
114	walk->len_this_page -= nbytes;
115	walk->len_this_segment -= nbytes;
116	return 0;
117}