mirror of
https://github.com/AlexisTM/RT-WiFi.git
synced 2024-12-18 21:28:07 +00:00
149 lines
4.2 KiB
C
149 lines
4.2 KiB
C
/*
|
|
* Copyright (c) 2013 Luis R. Rodriguez <mcgrof@do-not-panic.com>
|
|
*
|
|
* Backport compatibility file for Linux for kernels 3.6.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/scatterlist.h>
|
|
#include <linux/export.h>
|
|
#include <linux/bug.h>
|
|
#include <linux/bitmap.h>
|
|
#include <linux/i2c.h>
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0))
|
|
/**
|
|
* __i2c_transfer - unlocked flavor of i2c_transfer
|
|
* @adap: Handle to I2C bus
|
|
* @msgs: One or more messages to execute before STOP is issued to
|
|
* terminate the operation; each message begins with a START.
|
|
* @num: Number of messages to be executed.
|
|
*
|
|
* Returns negative errno, else the number of messages executed.
|
|
*
|
|
* Adapter lock must be held when calling this function. No debug logging
|
|
* takes place. adap->algo->master_xfer existence isn't checked.
|
|
*/
|
|
int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
|
{
|
|
unsigned long orig_jiffies;
|
|
int ret, try;
|
|
|
|
/* Retry automatically on arbitration loss */
|
|
orig_jiffies = jiffies;
|
|
for (ret = 0, try = 0; try <= adap->retries; try++) {
|
|
ret = adap->algo->master_xfer(adap, msgs, num);
|
|
if (ret != -EAGAIN)
|
|
break;
|
|
if (time_after(jiffies, orig_jiffies + adap->timeout))
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(__i2c_transfer);
|
|
#endif
|
|
|
|
/**
|
|
* memweight - count the total number of bits set in memory area
|
|
* @ptr: pointer to the start of the area
|
|
* @bytes: the size of the area
|
|
*/
|
|
size_t memweight(const void *ptr, size_t bytes)
|
|
{
|
|
size_t ret = 0;
|
|
size_t longs;
|
|
const unsigned char *bitmap = ptr;
|
|
|
|
for (; bytes > 0 && ((unsigned long)bitmap) % sizeof(long);
|
|
bytes--, bitmap++)
|
|
ret += hweight8(*bitmap);
|
|
|
|
longs = bytes / sizeof(long);
|
|
if (longs) {
|
|
BUG_ON(longs >= INT_MAX / BITS_PER_LONG);
|
|
ret += bitmap_weight((unsigned long *)bitmap,
|
|
longs * BITS_PER_LONG);
|
|
bytes -= longs * sizeof(long);
|
|
bitmap += longs * sizeof(long);
|
|
}
|
|
/*
|
|
* The reason that this last loop is distinct from the preceding
|
|
* bitmap_weight() call is to compute 1-bits in the last region smaller
|
|
* than sizeof(long) properly on big-endian systems.
|
|
*/
|
|
for (; bytes > 0; bytes--, bitmap++)
|
|
ret += hweight8(*bitmap);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(memweight);
|
|
|
|
/**
|
|
* sg_alloc_table_from_pages - Allocate and initialize an sg table from
|
|
* an array of pages
|
|
* @sgt: The sg table header to use
|
|
* @pages: Pointer to an array of page pointers
|
|
* @n_pages: Number of pages in the pages array
|
|
* @offset: Offset from start of the first page to the start of a buffer
|
|
* @size: Number of valid bytes in the buffer (after offset)
|
|
* @gfp_mask: GFP allocation mask
|
|
*
|
|
* Description:
|
|
* Allocate and initialize an sg table from a list of pages. Contiguous
|
|
* ranges of the pages are squashed into a single scatterlist node. A user
|
|
* may provide an offset at a start and a size of valid data in a buffer
|
|
* specified by the page array. The returned sg table is released by
|
|
* sg_free_table.
|
|
*
|
|
* Returns:
|
|
* 0 on success, negative error on failure
|
|
*/
|
|
int sg_alloc_table_from_pages(struct sg_table *sgt,
|
|
struct page **pages, unsigned int n_pages,
|
|
unsigned long offset, unsigned long size,
|
|
gfp_t gfp_mask)
|
|
{
|
|
unsigned int chunks;
|
|
unsigned int i;
|
|
unsigned int cur_page;
|
|
int ret;
|
|
struct scatterlist *s;
|
|
|
|
/* compute number of contiguous chunks */
|
|
chunks = 1;
|
|
for (i = 1; i < n_pages; ++i)
|
|
if (page_to_pfn(pages[i]) != page_to_pfn(pages[i - 1]) + 1)
|
|
++chunks;
|
|
|
|
ret = sg_alloc_table(sgt, chunks, gfp_mask);
|
|
if (unlikely(ret))
|
|
return ret;
|
|
|
|
/* merging chunks and putting them into the scatterlist */
|
|
cur_page = 0;
|
|
for_each_sg(sgt->sgl, s, sgt->orig_nents, i) {
|
|
unsigned long chunk_size;
|
|
unsigned int j;
|
|
|
|
/* look for the end of the current chunk */
|
|
for (j = cur_page + 1; j < n_pages; ++j)
|
|
if (page_to_pfn(pages[j]) !=
|
|
page_to_pfn(pages[j - 1]) + 1)
|
|
break;
|
|
|
|
chunk_size = ((j - cur_page) << PAGE_SHIFT) - offset;
|
|
sg_set_page(s, pages[cur_page], min(size, chunk_size), offset);
|
|
size -= chunk_size;
|
|
offset = 0;
|
|
cur_page = j;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(sg_alloc_table_from_pages);
|