| /* |
| * Copyright(c) 2009 Intel Corporation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms and conditions of the GNU General Public License, |
| * version 2, as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| * more details. |
| * |
| * You should have received a copy of the GNU General Public License along with |
| * this program; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Maintained at www.Open-FCoE.org |
| */ |
| |
| #include <linux/kernel.h> |
| #include <linux/types.h> |
| #include <linux/scatterlist.h> |
| #include <linux/crc32.h> |
| |
| #include <scsi/libfc.h> |
| |
| #include "fc_libfc.h" |
| |
| MODULE_AUTHOR("Open-FCoE.org"); |
| MODULE_DESCRIPTION("libfc"); |
| MODULE_LICENSE("GPL v2"); |
| |
| unsigned int fc_debug_logging; |
| module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR); |
| MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); |
| |
| /** |
| * libfc_init() - Initialize libfc.ko |
| */ |
| static int __init libfc_init(void) |
| { |
| int rc = 0; |
| |
| rc = fc_setup_fcp(); |
| if (rc) |
| return rc; |
| |
| rc = fc_setup_exch_mgr(); |
| if (rc) |
| goto destroy_pkt_cache; |
| |
| rc = fc_setup_rport(); |
| if (rc) |
| goto destroy_em; |
| |
| return rc; |
| destroy_em: |
| fc_destroy_exch_mgr(); |
| destroy_pkt_cache: |
| fc_destroy_fcp(); |
| return rc; |
| } |
| module_init(libfc_init); |
| |
| /** |
| * libfc_exit() - Tear down libfc.ko |
| */ |
| static void __exit libfc_exit(void) |
| { |
| fc_destroy_fcp(); |
| fc_destroy_exch_mgr(); |
| fc_destroy_rport(); |
| } |
| module_exit(libfc_exit); |
| |
| /** |
| * fc_copy_buffer_to_sglist() - This routine copies the data of a buffer |
| * into a scatter-gather list (SG list). |
| * |
| * @buf: pointer to the data buffer. |
| * @len: the byte-length of the data buffer. |
| * @sg: pointer to the pointer of the SG list. |
| * @nents: pointer to the remaining number of entries in the SG list. |
| * @offset: pointer to the current offset in the SG list. |
| * @km_type: dedicated page table slot type for kmap_atomic. |
| * @crc: pointer to the 32-bit crc value. |
| * If crc is NULL, CRC is not calculated. |
| */ |
| u32 fc_copy_buffer_to_sglist(void *buf, size_t len, |
| struct scatterlist *sg, |
| u32 *nents, size_t *offset, |
| enum km_type km_type, u32 *crc) |
| { |
| size_t remaining = len; |
| u32 copy_len = 0; |
| |
| while (remaining > 0 && sg) { |
| size_t off, sg_bytes; |
| void *page_addr; |
| |
| if (*offset >= sg->length) { |
| /* |
| * Check for end and drop resources |
| * from the last iteration. |
| */ |
| if (!(*nents)) |
| break; |
| --(*nents); |
| *offset -= sg->length; |
| sg = sg_next(sg); |
| continue; |
| } |
| sg_bytes = min(remaining, sg->length - *offset); |
| |
| /* |
| * The scatterlist item may be bigger than PAGE_SIZE, |
| * but we are limited to mapping PAGE_SIZE at a time. |
| */ |
| off = *offset + sg->offset; |
| sg_bytes = min(sg_bytes, |
| (size_t)(PAGE_SIZE - (off & ~PAGE_MASK))); |
| page_addr = kmap_atomic(sg_page(sg) + (off >> PAGE_SHIFT), |
| km_type); |
| if (crc) |
| *crc = crc32(*crc, buf, sg_bytes); |
| memcpy((char *)page_addr + (off & ~PAGE_MASK), buf, sg_bytes); |
| kunmap_atomic(page_addr, km_type); |
| buf += sg_bytes; |
| *offset += sg_bytes; |
| remaining -= sg_bytes; |
| copy_len += sg_bytes; |
| } |
| return copy_len; |
| } |