M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions
+57
View File
@@ -0,0 +1,57 @@
obj-m += compat.o
#compat-objs :=
obj-$(CONFIG_COMPAT_FIRMWARE_CLASS) += compat_firmware_class.o
obj-$(CONFIG_COMPAT_NET_SCH_CODEL) += sch_codel.o
sch_fq_codel-y = sch_fq_codel_core.o flow_dissector.o
obj-$(CONFIG_COMPAT_NET_SCH_FQ_CODEL) += sch_fq_codel.o
compat-y += main.o
# Compat kernel compatibility code
compat-$(CONFIG_COMPAT_KERNEL_2_6_14) += compat-2.6.14.o
compat-$(CONFIG_COMPAT_KERNEL_2_6_18) += compat-2.6.18.o
compat-$(CONFIG_COMPAT_KERNEL_2_6_19) += compat-2.6.19.o
compat-$(CONFIG_COMPAT_KERNEL_2_6_21) += compat-2.6.21.o
compat-$(CONFIG_COMPAT_KERNEL_2_6_22) += compat-2.6.22.o
compat-$(CONFIG_COMPAT_KERNEL_2_6_23) += compat-2.6.23.o
compat-$(CONFIG_COMPAT_KERNEL_2_6_24) += compat-2.6.24.o
compat-$(CONFIG_COMPAT_KERNEL_2_6_25) += \
compat-2.6.25.o \
pm_qos_params.o
compat-$(CONFIG_COMPAT_KERNEL_2_6_26) += compat-2.6.26.o
compat-$(CONFIG_COMPAT_KERNEL_2_6_27) += compat-2.6.27.o
compat-$(CONFIG_COMPAT_KERNEL_2_6_28) += compat-2.6.28.o
compat-$(CONFIG_COMPAT_KERNEL_2_6_29) += compat-2.6.29.o
compat-$(CONFIG_COMPAT_KERNEL_2_6_32) += compat-2.6.32.o
compat-$(CONFIG_COMPAT_KERNEL_2_6_33) += compat-2.6.33.o
compat-$(CONFIG_COMPAT_KERNEL_2_6_34) += compat-2.6.34.o
compat-$(CONFIG_COMPAT_KERNEL_2_6_35) += compat-2.6.35.o
compat-$(CONFIG_COMPAT_KERNEL_2_6_36) += compat-2.6.36.o
compat-$(CONFIG_COMPAT_KFIFO) += kfifo.o
compat-$(CONFIG_COMPAT_KERNEL_2_6_37) += compat-2.6.37.o
compat-$(CONFIG_COMPAT_KERNEL_2_6_38) += compat-2.6.38.o
compat-$(CONFIG_COMPAT_KERNEL_2_6_39) += \
compat-2.6.39.o \
kstrtox.o
compat-$(CONFIG_COMPAT_KERNEL_3_0) += compat-3.0.o
compat-$(CONFIG_COMPAT_KERNEL_3_1) += compat-3.1.o
compat-$(CONFIG_COMPAT_KERNEL_3_2) += compat-3.2.o
compat-$(CONFIG_COMPAT_KERNEL_3_3) += \
compat-3.3.o
compat-$(CONFIG_COMPAT_KERNEL_3_4) += compat-3.4.o
compat-$(CONFIG_COMPAT_KERNEL_3_7) += compat-3.7.o
compat-$(CONFIG_COMPAT_CORDIC) += cordic.o
compat-$(CONFIG_COMPAT_CRC8) += crc8.o
ifndef CONFIG_64BIT
ifndef CONFIG_GENERIC_ATOMIC64
compat-y += compat_atomic.o
endif
endif
+14
View File
@@ -0,0 +1,14 @@
/*
* Copyright 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 2.6.14.
*/
#include <net/compat.h>
/* 2.6.14 compat code goes here */
+14
View File
@@ -0,0 +1,14 @@
/*
* Copyright 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 2.6.18.
*/
#include <net/compat.h>
/* 2.6.18 compat code goes here */
+14
View File
@@ -0,0 +1,14 @@
/*
* Copyright 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 2.6.19.
*/
#include <net/compat.h>
/* 2.6.19 compat code goes here */
+14
View File
@@ -0,0 +1,14 @@
/*
* Copyright 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 2.6.21.
*/
#include <net/compat.h>
/* 2.6.21 compat code goes here */
+14
View File
@@ -0,0 +1,14 @@
/*
* Copyright 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 2.6.22.
*/
#include <net/compat.h>
/* 2.6.22 compat code goes here */
+239
View File
@@ -0,0 +1,239 @@
/*
* Copyright 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 2.6.23.
*/
#include <net/compat.h>
/* On net/core/dev.c as of 2.6.24 */
int __dev_addr_delete(struct dev_addr_list **list, int *count,
void *addr, int alen, int glbl)
{
struct dev_addr_list *da;
for (; (da = *list) != NULL; list = &da->next) {
if (memcmp(da->da_addr, addr, da->da_addrlen) == 0 &&
alen == da->da_addrlen) {
if (glbl) {
int old_glbl = da->da_gusers;
da->da_gusers = 0;
if (old_glbl == 0)
break;
}
if (--da->da_users)
return 0;
*list = da->next;
kfree(da);
(*count)--;
return 0;
}
}
return -ENOENT;
}
EXPORT_SYMBOL_GPL(__dev_addr_delete);
/* On net/core/dev.c as of 2.6.24. This is not yet used by mac80211 but
* might as well add it */
int __dev_addr_add(struct dev_addr_list **list, int *count,
void *addr, int alen, int glbl)
{
struct dev_addr_list *da;
for (da = *list; da != NULL; da = da->next) {
if (memcmp(da->da_addr, addr, da->da_addrlen) == 0 &&
da->da_addrlen == alen) {
if (glbl) {
int old_glbl = da->da_gusers;
da->da_gusers = 1;
if (old_glbl)
return 0;
}
da->da_users++;
return 0;
}
}
da = kmalloc(sizeof(*da), GFP_ATOMIC);
if (da == NULL)
return -ENOMEM;
memcpy(da->da_addr, addr, alen);
da->da_addrlen = alen;
da->da_users = 1;
da->da_gusers = glbl ? 1 : 0;
da->next = *list;
*list = da;
(*count)++;
return 0;
}
EXPORT_SYMBOL_GPL(__dev_addr_add);
/* Part of net/core/dev_mcast.c as of 2.6.23. This is a slightly different version.
* Since da->da_synced is not part of 2.6.22 we need to take longer route when
* syncing */
/**
* dev_mc_sync - Synchronize device's multicast list to another device
* @to: destination device
* @from: source device
*
* Add newly added addresses to the destination device and release
* addresses that have no users left. The source device must be
* locked by netif_tx_lock_bh.
*
* This function is intended to be called from the dev->set_multicast_list
* function of layered software devices.
*/
int dev_mc_sync(struct net_device *to, struct net_device *from)
{
struct dev_addr_list *da, *next, *da_to;
int err = 0;
netif_tx_lock_bh(to);
da = from->mc_list;
while (da != NULL) {
int synced = 0;
next = da->next;
da_to = to->mc_list;
/* 2.6.22 does not have da->da_synced so lets take the long route */
while (da_to != NULL) {
if (memcmp(da_to->da_addr, da->da_addr, da_to->da_addrlen) == 0 &&
da->da_addrlen == da_to->da_addrlen)
synced = 1;
break;
}
if (!synced) {
err = __dev_addr_add(&to->mc_list, &to->mc_count,
da->da_addr, da->da_addrlen, 0);
if (err < 0)
break;
da->da_users++;
} else if (da->da_users == 1) {
__dev_addr_delete(&to->mc_list, &to->mc_count,
da->da_addr, da->da_addrlen, 0);
__dev_addr_delete(&from->mc_list, &from->mc_count,
da->da_addr, da->da_addrlen, 0);
}
da = next;
}
if (!err)
__dev_set_rx_mode(to);
netif_tx_unlock_bh(to);
return err;
}
EXPORT_SYMBOL_GPL(dev_mc_sync);
/* Part of net/core/dev_mcast.c as of 2.6.23. This is a slighty different version.
* Since da->da_synced is not part of 2.6.22 we need to take longer route when
* unsyncing */
/**
* dev_mc_unsync - Remove synchronized addresses from the destination
* device
* @to: destination device
* @from: source device
*
* Remove all addresses that were added to the destination device by
* dev_mc_sync(). This function is intended to be called from the
* dev->stop function of layered software devices.
*/
void dev_mc_unsync(struct net_device *to, struct net_device *from)
{
struct dev_addr_list *da, *next, *da_to;
netif_tx_lock_bh(from);
netif_tx_lock_bh(to);
da = from->mc_list;
while (da != NULL) {
bool synced = false;
next = da->next;
da_to = to->mc_list;
/* 2.6.22 does not have da->da_synced so lets take the long route */
while (da_to != NULL) {
if (memcmp(da_to->da_addr, da->da_addr, da_to->da_addrlen) == 0 &&
da->da_addrlen == da_to->da_addrlen)
synced = true;
break;
}
if (!synced) {
da = next;
continue;
}
__dev_addr_delete(&to->mc_list, &to->mc_count,
da->da_addr, da->da_addrlen, 0);
__dev_addr_delete(&from->mc_list, &from->mc_count,
da->da_addr, da->da_addrlen, 0);
da = next;
}
__dev_set_rx_mode(to);
netif_tx_unlock_bh(to);
netif_tx_unlock_bh(from);
}
EXPORT_SYMBOL_GPL(dev_mc_unsync);
/* Added as of 2.6.23 on net/core/dev.c. Slightly modifed, no dev->set_rx_mode on
* 2.6.22 so ignore that. */
/*
* Upload unicast and multicast address lists to device and
* configure RX filtering. When the device doesn't support unicast
* filtering it is put in promiscous mode while unicast addresses
* are present.
*/
void __dev_set_rx_mode(struct net_device *dev)
{
/* dev_open will call this function so the list will stay sane. */
if (!(dev->flags&IFF_UP))
return;
if (!netif_device_present(dev))
return;
/* This needs to be ported to 2.6.22 framework */
#if 0
/* Unicast addresses changes may only happen under the rtnl,
* therefore calling __dev_set_promiscuity here is safe.
*/
if (dev->uc_count > 0 && !dev->uc_promisc) {
__dev_set_promiscuity(dev, 1);
dev->uc_promisc = 1;
} else if (dev->uc_count == 0 && dev->uc_promisc) {
__dev_set_promiscuity(dev, -1);
dev->uc_promisc = 0;
}
#endif
if (dev->set_multicast_list)
dev->set_multicast_list(dev);
}
/**
* pci_try_set_mwi - enables memory-write-invalidate PCI transaction
* @dev: the PCI device for which MWI is enabled
*
* Enables the Memory-Write-Invalidate transaction in %PCI_COMMAND.
* Callers are not required to check the return value.
*
* RETURNS: An appropriate -ERRNO error value on error, or zero for success.
*/
int pci_try_set_mwi(struct pci_dev *dev)
{
int rc = 0;
#ifdef HAVE_PCI_SET_MWI
rc = pci_set_mwi(dev);
#endif
return rc;
}
EXPORT_SYMBOL_GPL(pci_try_set_mwi);
#endif
+158
View File
@@ -0,0 +1,158 @@
/*
* Copyright 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 2.6.24.
*/
#include <net/compat.h>
#include <net/arp.h>
/*
* We simply won't use it though, just declare it for our wrappers and
* for usage with tons of code that makes mention to it.
*/
struct net init_net;
EXPORT_SYMBOL_GPL(init_net);
/* 2.6.22 and 2.6.23 have eth_header_cache_update defined as extern in include/linux/etherdevice.h
* and actually defined in net/ethernet/eth.c but 2.6.24 exports it. Lets export it here */
/**
* eth_header_cache_update - update cache entry
* @hh: destination cache entry
* @dev: network device
* @haddr: new hardware address
*
* Called by Address Resolution module to notify changes in address.
*/
void eth_header_cache_update(struct hh_cache *hh,
struct net_device *dev,
unsigned char *haddr)
{
memcpy(((u8 *) hh->hh_data) + HH_DATA_OFF(sizeof(struct ethhdr)),
haddr, ETH_ALEN);
}
EXPORT_SYMBOL_GPL(eth_header_cache_update);
/* 2.6.22 and 2.6.23 have eth_header_cache defined as extern in include/linux/etherdevice.h
* and actually defined in net/ethernet/eth.c but 2.6.24 exports it. Lets export it here */
/**
* eth_header_cache - fill cache entry from neighbour
* @neigh: source neighbour
* @hh: destination cache entry
* Create an Ethernet header template from the neighbour.
*/
int eth_header_cache(struct neighbour *neigh, struct hh_cache *hh)
{
__be16 type = hh->hh_type;
struct ethhdr *eth;
const struct net_device *dev = neigh->dev;
eth = (struct ethhdr *)
(((u8 *) hh->hh_data) + (HH_DATA_OFF(sizeof(*eth))));
if (type == htons(ETH_P_802_3))
return -1;
eth->h_proto = type;
memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
memcpy(eth->h_dest, neigh->ha, ETH_ALEN);
hh->hh_len = ETH_HLEN;
return 0;
}
EXPORT_SYMBOL_GPL(eth_header_cache);
/* 2.6.22 and 2.6.23 have eth_header() defined as extern in include/linux/etherdevice.h
* and actually defined in net/ethernet/eth.c but 2.6.24 exports it. Lets export it here */
/**
* eth_header - create the Ethernet header
* @skb: buffer to alter
* @dev: source device
* @type: Ethernet type field
* @daddr: destination address (NULL leave destination address)
* @saddr: source address (NULL use device source address)
* @len: packet length (<= skb->len)
*
*
* Set the protocol type. For a packet of type ETH_P_802_3 we put the length
* in here instead. It is up to the 802.2 layer to carry protocol information.
*/
int eth_header(struct sk_buff *skb, struct net_device *dev, unsigned short type,
void *daddr, void *saddr, unsigned len)
{
struct ethhdr *eth = (struct ethhdr *)skb_push(skb, ETH_HLEN);
if (type != ETH_P_802_3)
eth->h_proto = htons(type);
else
eth->h_proto = htons(len);
/*
* Set the source hardware address.
*/
if (!saddr)
saddr = dev->dev_addr;
memcpy(eth->h_source, saddr, dev->addr_len);
if (daddr) {
memcpy(eth->h_dest, daddr, dev->addr_len);
return ETH_HLEN;
}
/*
* Anyway, the loopback-device should never use this function...
*/
if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
memset(eth->h_dest, 0, dev->addr_len);
return ETH_HLEN;
}
return -ETH_HLEN;
}
EXPORT_SYMBOL_GPL(eth_header);
/* 2.6.22 and 2.6.23 have eth_rebuild_header defined as extern in include/linux/etherdevice.h
* and actually defined in net/ethernet/eth.c but 2.6.24 exports it. Lets export it here */
/**
* eth_rebuild_header- rebuild the Ethernet MAC header.
* @skb: socket buffer to update
*
* This is called after an ARP or IPV6 ndisc it's resolution on this
* sk_buff. We now let protocol (ARP) fill in the other fields.
*
* This routine CANNOT use cached dst->neigh!
* Really, it is used only when dst->neigh is wrong.
*/
int eth_rebuild_header(struct sk_buff *skb)
{
struct ethhdr *eth = (struct ethhdr *)skb->data;
struct net_device *dev = skb->dev;
switch (eth->h_proto) {
#ifdef CONFIG_INET
case __constant_htons(ETH_P_IP):
return arp_find(eth->h_dest, skb);
#endif
default:
printk(KERN_DEBUG
"%s: unable to resolve type %X addresses.\n",
dev->name, (int)eth->h_proto);
memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
break;
}
return 0;
}
EXPORT_SYMBOL_GPL(eth_rebuild_header);
+114
View File
@@ -0,0 +1,114 @@
/*
* Copyright 2007-2012 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 2.6.25.
*/
#include <linux/miscdevice.h>
#include <linux/pci.h>
/*
* To backport b718989d correctly pcibios_enable_device()
* is required but we don't have access to it on modules
* as its an architecture specific routine that is not
* exported and as such only core kernel code has access
* to it. We implement a sloppy work around for backporting
* this.
*/
int pci_enable_device_mem(struct pci_dev *dev)
{
int bars = pci_select_bars(dev, IORESOURCE_MEM);
return pci_enable_device_bars(dev, bars);
}
EXPORT_SYMBOL_GPL(pci_enable_device_mem);
/**
* The following things are out of ./lib/vsprintf.c
* The new iwlwifi driver is using them.
*/
/**
* strict_strtoul - convert a string to an unsigned long strictly
* @cp: The string to be converted
* @base: The number base to use
* @res: The converted result value
*
* strict_strtoul converts a string to an unsigned long only if the
* string is really an unsigned long string, any string containing
* any invalid char at the tail will be rejected and -EINVAL is returned,
* only a newline char at the tail is acceptible because people generally
* change a module parameter in the following way:
*
* echo 1024 > /sys/module/e1000/parameters/copybreak
*
* echo will append a newline to the tail.
*
* It returns 0 if conversion is successful and *res is set to the converted
* value, otherwise it returns -EINVAL and *res is set to 0.
*
* simple_strtoul just ignores the successive invalid characters and
* return the converted value of prefix part of the string.
*/
int strict_strtoul(const char *cp, unsigned int base, unsigned long *res);
/**
* strict_strtol - convert a string to a long strictly
* @cp: The string to be converted
* @base: The number base to use
* @res: The converted result value
*
* strict_strtol is similiar to strict_strtoul, but it allows the first
* character of a string is '-'.
*
* It returns 0 if conversion is successful and *res is set to the converted
* value, otherwise it returns -EINVAL and *res is set to 0.
*/
int strict_strtol(const char *cp, unsigned int base, long *res);
#define define_strict_strtoux(type, valtype) \
int strict_strtou##type(const char *cp, unsigned int base, valtype *res)\
{ \
char *tail; \
valtype val; \
size_t len; \
\
*res = 0; \
len = strlen(cp); \
if (len == 0) \
return -EINVAL; \
\
val = simple_strtou##type(cp, &tail, base); \
if ((*tail == '\0') || \
((len == (size_t)(tail - cp) + 1) && (*tail == '\n'))) {\
*res = val; \
return 0; \
} \
\
return -EINVAL; \
} \
#define define_strict_strtox(type, valtype) \
int strict_strto##type(const char *cp, unsigned int base, valtype *res) \
{ \
int ret; \
if (*cp == '-') { \
ret = strict_strtou##type(cp+1, base, res); \
if (!ret) \
*res = -(*res); \
} else \
ret = strict_strtou##type(cp, base, res); \
\
return ret; \
} \
define_strict_strtoux(l, unsigned long)
define_strict_strtox(l, long)
EXPORT_SYMBOL_GPL(strict_strtoul);
EXPORT_SYMBOL_GPL(strict_strtol);
+87
View File
@@ -0,0 +1,87 @@
/*
* Copyright 2007-2010 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 2.6.26.
*
* Copyright holders from ported work:
*
* Copyright (c) 2002-2003 Patrick Mochel <mochel@osdl.org>
* Copyright (c) 2006-2007 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (c) 2006-2007 Novell Inc.
*/
#include <net/compat.h>
/* 2.6.24 does not have the struct kobject with a name */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25))
/**
* kobject_set_name_vargs - Set the name of an kobject
* @kobj: struct kobject to set the name of
* @fmt: format string used to build the name
* @vargs: vargs to format the string.
*/
static
int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
va_list vargs)
{
const char *old_name = kobj->name;
char *s;
if (kobj->name && !fmt)
return 0;
kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);
if (!kobj->name)
return -ENOMEM;
/* ewww... some of these buggers have '/' in the name ... */
while ((s = strchr(kobj->name, '/')))
s[0] = '!';
kfree(old_name);
return 0;
}
#else
static
int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
va_list vargs)
{
struct device *dev;
unsigned int len;
va_list aq;
dev = container_of(kobj, struct device, kobj);
va_copy(aq, vargs);
len = vsnprintf(NULL, 0, fmt, aq);
va_end(aq);
len = len < BUS_ID_SIZE ? (len + 1) : BUS_ID_SIZE;
vsnprintf(dev->bus_id, len, fmt, vargs);
return 0;
}
#endif
/**
* dev_set_name - set a device name
* @dev: device
* @fmt: format string for the device's name
*/
int dev_set_name(struct device *dev, const char *fmt, ...)
{
va_list vargs;
int err;
va_start(vargs, fmt);
err = kobject_set_name_vargs(&dev->kobj, fmt, vargs);
va_end(vargs);
return err;
}
EXPORT_SYMBOL_GPL(dev_set_name);
+239
View File
@@ -0,0 +1,239 @@
/*
* Copyright 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 2.6.27
*/
#include <linux/compat.h>
#include <linux/pci.h>
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24))
#include <linux/mmc/sdio.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#endif
/* rfkill notification chain */
#define RFKILL_STATE_CHANGED 0x0001 /* state of a normal rfkill
switch has changed */
/*
* e5899e1b7d73e67de758a32174a859cc2586c0b9 made pci_pme_capable() external,
* it was defined internally, some drivers want access to this information.
*
* Unfortunately the old kernels do not have ->pm_cap or ->pme_support so
* we have to call the PCI routines directly.
*/
/**
* pci_pme_capable - check the capability of PCI device to generate PME#
* @dev: PCI device to handle.
* @state: PCI state from which device will issue PME#.
*
* This is the backport code for older kernels for compat-drivers, we read stuff
* from the initialization stuff from pci_pm_init().
*/
bool pci_pme_capable(struct pci_dev *dev, pci_power_t state)
{
int pm;
u16 pmc = 0;
u16 pme_support; /* as from the pci dev */
/* find PCI PM capability in list */
pm = pci_find_capability(dev, PCI_CAP_ID_PM);
if (!pm)
return false;
if ((pmc & PCI_PM_CAP_VER_MASK) > 3) {
dev_err(&dev->dev, "unsupported PM cap regs version (%u)\n",
pmc & PCI_PM_CAP_VER_MASK);
return false;
}
pmc &= PCI_PM_CAP_PME_MASK;
if (!pmc)
return false;
pme_support = pmc >> PCI_PM_CAP_PME_SHIFT;
/* Check device's ability to generate PME# */
return !!(pme_support & (1 << state));
}
EXPORT_SYMBOL_GPL(pci_pme_capable);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24))
/**
* mmc_align_data_size - pads a transfer size to a more optimal value
* @card: the MMC card associated with the data transfer
* @sz: original transfer size
*
* Pads the original data size with a number of extra bytes in
* order to avoid controller bugs and/or performance hits
* (e.g. some controllers revert to PIO for certain sizes).
*
* Returns the improved size, which might be unmodified.
*
* Note that this function is only relevant when issuing a
* single scatter gather entry.
*/
unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz)
{
/*
* FIXME: We don't have a system for the controller to tell
* the core about its problems yet, so for now we just 32-bit
* align the size.
*/
sz = ((sz + 3) / 4) * 4;
return sz;
}
EXPORT_SYMBOL_GPL(mmc_align_data_size);
/*
* Calculate the maximum byte mode transfer size
*/
static inline unsigned int sdio_max_byte_size(struct sdio_func *func)
{
unsigned int mval = (unsigned int) min(func->card->host->max_seg_size,
func->card->host->max_blk_size);
mval = min(mval, func->max_blksize);
return min(mval, 512u); /* maximum size for byte mode */
}
/**
* sdio_align_size - pads a transfer size to a more optimal value
* @func: SDIO function
* @sz: original transfer size
*
* Pads the original data size with a number of extra bytes in
* order to avoid controller bugs and/or performance hits
* (e.g. some controllers revert to PIO for certain sizes).
*
* If possible, it will also adjust the size so that it can be
* handled in just a single request.
*
* Returns the improved size, which might be unmodified.
*/
unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz)
{
unsigned int orig_sz;
unsigned int blk_sz, byte_sz;
unsigned chunk_sz;
orig_sz = sz;
/*
* Do a first check with the controller, in case it
* wants to increase the size up to a point where it
* might need more than one block.
*/
sz = mmc_align_data_size(func->card, sz);
/*
* If we can still do this with just a byte transfer, then
* we're done.
*/
if (sz <= sdio_max_byte_size(func))
return sz;
if (func->card->cccr.multi_block) {
/*
* Check if the transfer is already block aligned
*/
if ((sz % func->cur_blksize) == 0)
return sz;
/*
* Realign it so that it can be done with one request,
* and recheck if the controller still likes it.
*/
blk_sz = ((sz + func->cur_blksize - 1) /
func->cur_blksize) * func->cur_blksize;
blk_sz = mmc_align_data_size(func->card, blk_sz);
/*
* This value is only good if it is still just
* one request.
*/
if ((blk_sz % func->cur_blksize) == 0)
return blk_sz;
/*
* We failed to do one request, but at least try to
* pad the remainder properly.
*/
byte_sz = mmc_align_data_size(func->card,
sz % func->cur_blksize);
if (byte_sz <= sdio_max_byte_size(func)) {
blk_sz = sz / func->cur_blksize;
return blk_sz * func->cur_blksize + byte_sz;
}
} else {
/*
* We need multiple requests, so first check that the
* controller can handle the chunk size;
*/
chunk_sz = mmc_align_data_size(func->card,
sdio_max_byte_size(func));
if (chunk_sz == sdio_max_byte_size(func)) {
/*
* Fix up the size of the remainder (if any)
*/
byte_sz = orig_sz % chunk_sz;
if (byte_sz) {
byte_sz = mmc_align_data_size(func->card,
byte_sz);
}
return (orig_sz / chunk_sz) * chunk_sz + byte_sz;
}
}
/*
* The controller is simply incapable of transferring the size
* we want in decent manner, so just return the original size.
*/
return orig_sz;
}
EXPORT_SYMBOL_GPL(sdio_align_size);
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) */
#ifdef CONFIG_DEBUG_FS
/*
* Backport of debugfs_remove_recursive() without using the internals globals
* which are used by the kernel's version with:
* simple_release_fs(&debugfs_mount, &debugfs_mount_count);
*/
void debugfs_remove_recursive(struct dentry *dentry)
{
struct dentry *last = NULL;
/* Sanity checks */
if (!dentry || !dentry->d_parent || !dentry->d_parent->d_inode)
return;
while (dentry != last) {
struct dentry *child = dentry;
/* Find a child without children */
while (!list_empty(&child->d_subdirs))
child = list_entry(child->d_subdirs.next,
struct dentry,
d_u.d_child);
/* Bail out if we already tried to remove that entry */
if (child == last)
return;
last = child;
debugfs_remove(child);
}
}
EXPORT_SYMBOL_GPL(debugfs_remove_recursive);
#endif /* CONFIG_DEBUG_FS */
+463
View File
@@ -0,0 +1,463 @@
/*
* Copyright 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 2.6.28.
*/
#include <linux/compat.h>
#include <linux/usb.h>
#include <linux/tty.h>
#include <asm/poll.h>
/* 2.6.28 compat code goes here */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23))
#if defined(CONFIG_USB) || defined(CONFIG_USB_MODULE)
/*
* Compat-wireless notes for USB backport stuff:
*
* urb->reject exists on 2.6.27, the poison/unpoison helpers
* did not though. The anchor poison does not exist so we cannot use them.
*
* USB anchor poising seems to exist to prevent future driver sumbissions
* of usb_anchor_urb() to an anchor marked as poisoned. For older kernels
* we cannot use that, so new usb_anchor_urb()s will be anchored. The down
* side to this should be submission of URBs will continue being anchored
* on an anchor instead of having them being rejected immediately when the
* driver realized we needed to stop. For ar9170 we poison URBs upon the
* ar9170 mac80211 stop callback(), don't think this should be so bad.
* It mean there is period of time in older kernels for which we continue
* to anchor new URBs to a known stopped anchor. We have two anchors
* (TX, and RX)
*/
#if 0
/**
* usb_poison_urb - reliably kill a transfer and prevent further use of an URB
* @urb: pointer to URB describing a previously submitted request,
* may be NULL
*
* This routine cancels an in-progress request. It is guaranteed that
* upon return all completion handlers will have finished and the URB
* will be totally idle and cannot be reused. These features make
* this an ideal way to stop I/O in a disconnect() callback.
* If the request has not already finished or been unlinked
* the completion handler will see urb->status == -ENOENT.
*
* After and while the routine runs, attempts to resubmit the URB will fail
* with error -EPERM. Thus even if the URB's completion handler always
* tries to resubmit, it will not succeed and the URB will become idle.
*
* This routine may not be used in an interrupt context (such as a bottom
* half or a completion handler), or when holding a spinlock, or in other
* situations where the caller can't schedule().
*
* This routine should not be called by a driver after its disconnect
* method has returned.
*/
void usb_poison_urb(struct urb *urb)
{
might_sleep();
if (!(urb && urb->dev && urb->ep))
return;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28))
spin_lock_irq(&usb_reject_lock);
#endif
++urb->reject;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28))
spin_unlock_irq(&usb_reject_lock);
#endif
/*
* XXX: usb_hcd_unlink_urb() needs backporting... this is defined
* on usb hcd.c but urb.c gets access to it. That is, older kernels
* have usb_hcd_unlink_urb() but its not exported, nor can we
* re-implement it exactly. This essentially dequeues the urb from
* hw, we need to figure out a way to backport this.
*/
//usb_hcd_unlink_urb(urb, -ENOENT);
wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
}
EXPORT_SYMBOL_GPL(usb_poison_urb);
#endif
#endif /* CONFIG_USB */
#if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE)
#include <pcmcia/ds.h>
struct pcmcia_cfg_mem {
tuple_t tuple;
cisparse_t parse;
u8 buf[256];
cistpl_cftable_entry_t dflt;
};
/**
* pcmcia_loop_config() - loop over configuration options
* @p_dev: the struct pcmcia_device which we need to loop for.
* @conf_check: function to call for each configuration option.
* It gets passed the struct pcmcia_device, the CIS data
* describing the configuration option, and private data
* being passed to pcmcia_loop_config()
* @priv_data: private data to be passed to the conf_check function.
*
* pcmcia_loop_config() loops over all configuration options, and calls
* the driver-specific conf_check() for each one, checking whether
* it is a valid one. Returns 0 on success or errorcode otherwise.
*/
int pcmcia_loop_config(struct pcmcia_device *p_dev,
int (*conf_check) (struct pcmcia_device *p_dev,
cistpl_cftable_entry_t *cfg,
cistpl_cftable_entry_t *dflt,
unsigned int vcc,
void *priv_data),
void *priv_data)
{
struct pcmcia_cfg_mem *cfg_mem;
tuple_t *tuple;
int ret;
unsigned int vcc;
cfg_mem = kzalloc(sizeof(struct pcmcia_cfg_mem), GFP_KERNEL);
if (cfg_mem == NULL)
return -ENOMEM;
/* get the current Vcc setting */
vcc = p_dev->socket->socket.Vcc;
tuple = &cfg_mem->tuple;
tuple->TupleData = cfg_mem->buf;
tuple->TupleDataMax = 255;
tuple->TupleOffset = 0;
tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY;
tuple->Attributes = 0;
ret = pcmcia_get_first_tuple(p_dev, tuple);
while (!ret) {
cistpl_cftable_entry_t *cfg = &cfg_mem->parse.cftable_entry;
if (pcmcia_get_tuple_data(p_dev, tuple))
goto next_entry;
if (pcmcia_parse_tuple(tuple, &cfg_mem->parse))
goto next_entry;
/* default values */
p_dev->conf.ConfigIndex = cfg->index;
if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
cfg_mem->dflt = *cfg;
ret = conf_check(p_dev, cfg, &cfg_mem->dflt, vcc, priv_data);
if (!ret)
break;
next_entry:
ret = pcmcia_get_next_tuple(p_dev, tuple);
}
return ret;
}
EXPORT_SYMBOL_GPL(pcmcia_loop_config);
#endif /* CONFIG_PCMCIA */
#if defined(CONFIG_USB) || defined(CONFIG_USB_MODULE)
void usb_unpoison_urb(struct urb *urb)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28))
unsigned long flags;
#endif
if (!urb)
return;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28))
spin_lock_irqsave(&usb_reject_lock, flags);
#endif
--urb->reject;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28))
spin_unlock_irqrestore(&usb_reject_lock, flags);
#endif
}
EXPORT_SYMBOL_GPL(usb_unpoison_urb);
#if 0
/**
* usb_poison_anchored_urbs - cease all traffic from an anchor
* @anchor: anchor the requests are bound to
*
* this allows all outstanding URBs to be poisoned starting
* from the back of the queue. Newly added URBs will also be
* poisoned
*
* This routine should not be called by a driver after its disconnect
* method has returned.
*/
void usb_poison_anchored_urbs(struct usb_anchor *anchor)
{
struct urb *victim;
spin_lock_irq(&anchor->lock);
// anchor->poisoned = 1; /* XXX: Cannot backport */
while (!list_empty(&anchor->urb_list)) {
victim = list_entry(anchor->urb_list.prev, struct urb,
anchor_list);
/* we must make sure the URB isn't freed before we kill it*/
usb_get_urb(victim);
spin_unlock_irq(&anchor->lock);
/* this will unanchor the URB */
usb_poison_urb(victim);
usb_put_urb(victim);
spin_lock_irq(&anchor->lock);
}
spin_unlock_irq(&anchor->lock);
}
EXPORT_SYMBOL_GPL(usb_poison_anchored_urbs);
#endif
/**
* usb_anchor_empty - is an anchor empty
* @anchor: the anchor you want to query
*
* returns 1 if the anchor has no urbs associated with it
*/
int usb_anchor_empty(struct usb_anchor *anchor)
{
return list_empty(&anchor->urb_list);
}
EXPORT_SYMBOL_GPL(usb_anchor_empty);
#endif /* CONFIG_USB */
#endif
void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar)
{
/*
* Make sure the BAR is actually a memory resource, not an IO resource
*/
if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) {
WARN_ON(1);
return NULL;
}
return ioremap_nocache(pci_resource_start(pdev, bar),
pci_resource_len(pdev, bar));
}
EXPORT_SYMBOL_GPL(pci_ioremap_bar);
static unsigned long round_jiffies_common(unsigned long j, int cpu,
bool force_up)
{
int rem;
unsigned long original = j;
/*
* We don't want all cpus firing their timers at once hitting the
* same lock or cachelines, so we skew each extra cpu with an extra
* 3 jiffies. This 3 jiffies came originally from the mm/ code which
* already did this.
* The skew is done by adding 3*cpunr, then round, then subtract this
* extra offset again.
*/
j += cpu * 3;
rem = j % HZ;
/*
* If the target jiffie is just after a whole second (which can happen
* due to delays of the timer irq, long irq off times etc etc) then
* we should round down to the whole second, not up. Use 1/4th second
* as cutoff for this rounding as an extreme upper bound for this.
* But never round down if @force_up is set.
*/
if (rem < HZ/4 && !force_up) /* round down */
j = j - rem;
else /* round up */
j = j - rem + HZ;
/* now that we have rounded, subtract the extra skew again */
j -= cpu * 3;
if (j <= jiffies) /* rounding ate our timeout entirely; */
return original;
return j;
}
/**
* round_jiffies_up - function to round jiffies up to a full second
* @j: the time in (absolute) jiffies that should be rounded
*
* This is the same as round_jiffies() except that it will never
* round down. This is useful for timeouts for which the exact time
* of firing does not matter too much, as long as they don't fire too
* early.
*/
unsigned long round_jiffies_up(unsigned long j)
{
return round_jiffies_common(j, raw_smp_processor_id(), true);
}
EXPORT_SYMBOL_GPL(round_jiffies_up);
void v2_6_28_skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, int off,
int size)
{
skb_fill_page_desc(skb, i, page, off, size);
skb->len += size;
skb->data_len += size;
skb->truesize += size;
}
EXPORT_SYMBOL_GPL(v2_6_28_skb_add_rx_frag);
void tty_write_unlock(struct tty_struct *tty)
{
mutex_unlock(&tty->atomic_write_lock);
wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
}
int tty_write_lock(struct tty_struct *tty, int ndelay)
{
if (!mutex_trylock(&tty->atomic_write_lock)) {
if (ndelay)
return -EAGAIN;
if (mutex_lock_interruptible(&tty->atomic_write_lock))
return -ERESTARTSYS;
}
return 0;
}
/**
* send_prio_char - send priority character
*
* Send a high priority character to the tty even if stopped
*
* Locking: none for xchar method, write ordering for write method.
*/
static int send_prio_char(struct tty_struct *tty, char ch)
{
int was_stopped = tty->stopped;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26))
if (tty->ops->send_xchar) {
tty->ops->send_xchar(tty, ch);
#else
if (tty->driver->send_xchar) {
tty->driver->send_xchar(tty, ch);
#endif
return 0;
}
if (tty_write_lock(tty, 0) < 0)
return -ERESTARTSYS;
if (was_stopped)
start_tty(tty);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26))
tty->ops->write(tty, &ch, 1);
#else
tty->driver->write(tty, &ch, 1);
#endif
if (was_stopped)
stop_tty(tty);
tty_write_unlock(tty);
return 0;
}
int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26))
unsigned long flags;
#endif
int retval;
switch (cmd) {
case TCXONC:
retval = tty_check_change(tty);
if (retval)
return retval;
switch (arg) {
case TCOOFF:
if (!tty->flow_stopped) {
tty->flow_stopped = 1;
stop_tty(tty);
}
break;
case TCOON:
if (tty->flow_stopped) {
tty->flow_stopped = 0;
start_tty(tty);
}
break;
case TCIOFF:
if (STOP_CHAR(tty) != __DISABLED_CHAR)
return send_prio_char(tty, STOP_CHAR(tty));
break;
case TCION:
if (START_CHAR(tty) != __DISABLED_CHAR)
return send_prio_char(tty, START_CHAR(tty));
break;
default:
return -EINVAL;
}
return 0;
case TCFLSH:
return tty_perform_flush(tty, arg);
case TIOCPKT:
{
int pktmode;
if (tty->driver->type != TTY_DRIVER_TYPE_PTY ||
tty->driver->subtype != PTY_TYPE_MASTER)
return -ENOTTY;
if (get_user(pktmode, (int __user *) arg))
return -EFAULT;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26))
spin_lock_irqsave(&tty->ctrl_lock, flags);
#endif
if (pktmode) {
if (!tty->packet) {
tty->packet = 1;
tty->link->ctrl_status = 0;
}
} else
tty->packet = 0;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26))
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
#endif
return 0;
}
default:
/* Try the mode commands */
return tty_mode_ioctl(tty, file, cmd, arg);
}
}
EXPORT_SYMBOL_GPL(n_tty_ioctl_helper);
/**
* pci_wake_from_d3 - enable/disable device to wake up from D3_hot or D3_cold
* @dev: PCI device to prepare
* @enable: True to enable wake-up event generation; false to disable
*
* Many drivers want the device to wake up the system from D3_hot or D3_cold
* and this function allows them to set that up cleanly - pci_enable_wake()
* should not be called twice in a row to enable wake-up due to PCI PM vs ACPI
* ordering constraints.
*
* This function only returns error code if the device is not capable of
* generating PME# from both D3_hot and D3_cold, and the platform is unable to
* enable wake-up power for it.
*/
int pci_wake_from_d3(struct pci_dev *dev, bool enable)
{
return pci_pme_capable(dev, PCI_D3cold) ?
pci_enable_wake(dev, PCI_D3cold, enable) :
pci_enable_wake(dev, PCI_D3hot, enable);
}
EXPORT_SYMBOL_GPL(pci_wake_from_d3);
+166
View File
@@ -0,0 +1,166 @@
/*
* Copyright 2007-2010 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 2.6.29.
*/
#include <linux/compat.h>
#include <linux/usb.h>
#include <linux/etherdevice.h>
/*
* If you don't see your net_device_ops implemented on
* netdev_attach_ops() then you are shit out of luck and
* you must do the nasty ifdef magic, unless you figure
* out a way to squeze your hacks into this routine :)
*/
void netdev_attach_ops(struct net_device *dev,
const struct net_device_ops *ops)
{
dev->open = ops->ndo_open;
dev->init = ops->ndo_init;
dev->stop = ops->ndo_stop;
dev->hard_start_xmit = ops->ndo_start_xmit;
dev->change_rx_flags = ops->ndo_change_rx_flags;
dev->set_multicast_list = ops->ndo_set_multicast_list;
dev->validate_addr = ops->ndo_validate_addr;
dev->do_ioctl = ops->ndo_do_ioctl;
dev->set_config = ops->ndo_set_config;
dev->change_mtu = ops->ndo_change_mtu;
dev->set_mac_address = ops->ndo_set_mac_address;
dev->tx_timeout = ops->ndo_tx_timeout;
if (ops->ndo_get_stats)
dev->get_stats = ops->ndo_get_stats;
dev->vlan_rx_register = ops->ndo_vlan_rx_register;
dev->vlan_rx_add_vid = ops->ndo_vlan_rx_add_vid;
dev->vlan_rx_kill_vid = ops->ndo_vlan_rx_kill_vid;
#ifdef CONFIG_NET_POLL_CONTROLLER
dev->poll_controller = ops->ndo_poll_controller;
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27))
dev->select_queue = ops->ndo_select_queue;
#endif
}
EXPORT_SYMBOL_GPL(netdev_attach_ops);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23))
#if defined(CONFIG_USB) || defined(CONFIG_USB_MODULE)
/**
* usb_unpoison_anchored_urbs - let an anchor be used successfully again
* @anchor: anchor the requests are bound to
*
* Reverses the effect of usb_poison_anchored_urbs
* the anchor can be used normally after it returns
*/
void usb_unpoison_anchored_urbs(struct usb_anchor *anchor)
{
unsigned long flags;
struct urb *lazarus;
spin_lock_irqsave(&anchor->lock, flags);
list_for_each_entry(lazarus, &anchor->urb_list, anchor_list) {
usb_unpoison_urb(lazarus);
}
//anchor->poisoned = 0; /* XXX: cannot backport */
spin_unlock_irqrestore(&anchor->lock, flags);
}
EXPORT_SYMBOL_GPL(usb_unpoison_anchored_urbs);
#endif /* CONFIG_USB */
#endif
/**
* eth_mac_addr - set new Ethernet hardware address
* @dev: network device
* @p: socket address
* Change hardware address of device.
*
* This doesn't change hardware matching, so needs to be overridden
* for most real devices.
*/
int eth_mac_addr(struct net_device *dev, void *p)
{
struct sockaddr *addr = p;
if (netif_running(dev))
return -EBUSY;
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
return 0;
}
EXPORT_SYMBOL_GPL(eth_mac_addr);
/**
* eth_change_mtu - set new MTU size
* @dev: network device
* @new_mtu: new Maximum Transfer Unit
*
* Allow changing MTU size. Needs to be overridden for devices
* supporting jumbo frames.
*/
int eth_change_mtu(struct net_device *dev, int new_mtu)
{
if (new_mtu < 68 || new_mtu > ETH_DATA_LEN)
return -EINVAL;
dev->mtu = new_mtu;
return 0;
}
EXPORT_SYMBOL_GPL(eth_change_mtu);
int eth_validate_addr(struct net_device *dev)
{
if (!is_valid_ether_addr(dev->dev_addr))
return -EADDRNOTAVAIL;
return 0;
}
EXPORT_SYMBOL_GPL(eth_validate_addr);
/* Source: net/ethernet/eth.c */
#define NETREG_DUMMY 5
/**
* init_dummy_netdev - init a dummy network device for NAPI
* @dev: device to init
*
* This takes a network device structure and initialize the minimum
* amount of fields so it can be used to schedule NAPI polls without
* registering a full blown interface. This is to be used by drivers
* that need to tie several hardware interfaces to a single NAPI
* poll scheduler due to HW limitations.
*/
int init_dummy_netdev(struct net_device *dev)
{
/* Clear everything. Note we don't initialize spinlocks
* are they aren't supposed to be taken by any of the
* NAPI code and this dummy netdev is supposed to be
* only ever used for NAPI polls
*/
memset(dev, 0, sizeof(struct net_device));
/* make sure we BUG if trying to hit standard
* register/unregister code path
*/
dev->reg_state = NETREG_DUMMY;
/* initialize the ref count */
atomic_set(&dev->refcnt, 1);
#ifdef CONFIG_NETPOLL
/* NAPI wants this */
INIT_LIST_HEAD(&dev->napi_list);
#endif
/* a dummy interface is started by default */
set_bit(__LINK_STATE_PRESENT, &dev->state);
set_bit(__LINK_STATE_START, &dev->state);
return 0;
}
EXPORT_SYMBOL_GPL(init_dummy_netdev);
/* Source: net/core/dev.c */
+216
View File
@@ -0,0 +1,216 @@
/*
* Copyright 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 2.6.32.
*/
#include <linux/compat.h>
#include <linux/netdevice.h>
int __dev_addr_add(struct dev_addr_list **list, int *count,
void *addr, int alen, int glbl)
{
struct dev_addr_list *da;
for (da = *list; da != NULL; da = da->next) {
if (memcmp(da->da_addr, addr, da->da_addrlen) == 0 &&
da->da_addrlen == alen) {
if (glbl) {
int old_glbl = da->da_gusers;
da->da_gusers = 1;
if (old_glbl)
return 0;
}
da->da_users++;
return 0;
}
}
da = kzalloc(sizeof(*da), GFP_ATOMIC);
if (da == NULL)
return -ENOMEM;
memcpy(da->da_addr, addr, alen);
da->da_addrlen = alen;
da->da_users = 1;
da->da_gusers = glbl ? 1 : 0;
da->next = *list;
*list = da;
(*count)++;
return 0;
}
int __dev_addr_delete(struct dev_addr_list **list, int *count,
void *addr, int alen, int glbl)
{
struct dev_addr_list *da;
for (; (da = *list) != NULL; list = &da->next) {
if (memcmp(da->da_addr, addr, da->da_addrlen) == 0 &&
alen == da->da_addrlen) {
if (glbl) {
int old_glbl = da->da_gusers;
da->da_gusers = 0;
if (old_glbl == 0)
break;
}
if (--da->da_users)
return 0;
*list = da->next;
kfree(da);
(*count)--;
return 0;
}
}
return -ENOENT;
}
int __dev_addr_sync(struct dev_addr_list **to, int *to_count,
struct dev_addr_list **from, int *from_count)
{
struct dev_addr_list *da, *next;
int err = 0;
da = *from;
while (da != NULL) {
next = da->next;
if (!da->da_synced) {
err = __dev_addr_add(to, to_count,
da->da_addr, da->da_addrlen, 0);
if (err < 0)
break;
da->da_synced = 1;
da->da_users++;
} else if (da->da_users == 1) {
__dev_addr_delete(to, to_count,
da->da_addr, da->da_addrlen, 0);
__dev_addr_delete(from, from_count,
da->da_addr, da->da_addrlen, 0);
}
da = next;
}
return err;
}
EXPORT_SYMBOL_GPL(__dev_addr_sync);
void __dev_addr_unsync(struct dev_addr_list **to, int *to_count,
struct dev_addr_list **from, int *from_count)
{
struct dev_addr_list *da, *next;
da = *from;
while (da != NULL) {
next = da->next;
if (da->da_synced) {
__dev_addr_delete(to, to_count,
da->da_addr, da->da_addrlen, 0);
da->da_synced = 0;
__dev_addr_delete(from, from_count,
da->da_addr, da->da_addrlen, 0);
}
da = next;
}
}
EXPORT_SYMBOL_GPL(__dev_addr_unsync);
/*
* Nonzero if YEAR is a leap year (every 4 years,
* except every 100th isn't, and every 400th is).
*/
static int __isleap(long year)
{
return (year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0);
}
/* do a mathdiv for long type */
static long math_div(long a, long b)
{
return a / b - (a % b < 0);
}
/* How many leap years between y1 and y2, y1 must less or equal to y2 */
static long leaps_between(long y1, long y2)
{
long leaps1 = math_div(y1 - 1, 4) - math_div(y1 - 1, 100)
+ math_div(y1 - 1, 400);
long leaps2 = math_div(y2 - 1, 4) - math_div(y2 - 1, 100)
+ math_div(y2 - 1, 400);
return leaps2 - leaps1;
}
/* How many days come before each month (0-12). */
static const unsigned short __mon_yday[2][13] = {
/* Normal years. */
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
/* Leap years. */
{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
};
#define SECS_PER_HOUR (60 * 60)
#define SECS_PER_DAY (SECS_PER_HOUR * 24)
/**
* time_to_tm - converts the calendar time to local broken-down time
*
* @totalsecs the number of seconds elapsed since 00:00:00 on January 1, 1970,
* Coordinated Universal Time (UTC).
* @offset offset seconds adding to totalsecs.
* @result pointer to struct tm variable to receive broken-down time
*/
void time_to_tm(time_t totalsecs, int offset, struct tm *result)
{
long days, rem, y;
const unsigned short *ip;
days = totalsecs / SECS_PER_DAY;
rem = totalsecs % SECS_PER_DAY;
rem += offset;
while (rem < 0) {
rem += SECS_PER_DAY;
--days;
}
while (rem >= SECS_PER_DAY) {
rem -= SECS_PER_DAY;
++days;
}
result->tm_hour = rem / SECS_PER_HOUR;
rem %= SECS_PER_HOUR;
result->tm_min = rem / 60;
result->tm_sec = rem % 60;
/* January 1, 1970 was a Thursday. */
result->tm_wday = (4 + days) % 7;
if (result->tm_wday < 0)
result->tm_wday += 7;
y = 1970;
while (days < 0 || days >= (__isleap(y) ? 366 : 365)) {
/* Guess a corrected year, assuming 365 days per year. */
long yg = y + math_div(days, 365);
/* Adjust DAYS and Y to match the guessed year. */
days -= (yg - y) * 365 + leaps_between(y, yg);
y = yg;
}
result->tm_year = y - 1900;
result->tm_yday = days;
ip = __mon_yday[__isleap(y)];
for (y = 11; days < ip[y]; y--)
continue;
days -= ip[y];
result->tm_mon = y;
result->tm_mday = days + 1;
}
EXPORT_SYMBOL_GPL(time_to_tm);
/* source: kernel/time/timeconv.c*/
+131
View File
@@ -0,0 +1,131 @@
/*
* Copyright 2009 Hauke Mehrtens <hauke@hauke-m.de>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 2.6.33.
*/
#include <linux/compat.h>
#if defined(CONFIG_PCCARD) || defined(CONFIG_PCCARD_MODULE)
/**
* pccard_loop_tuple() - loop over tuples in the CIS
* @s: the struct pcmcia_socket where the card is inserted
* @function: the device function we loop for
* @code: which CIS code shall we look for?
* @parse: buffer where the tuple shall be parsed (or NULL, if no parse)
* @priv_data: private data to be passed to the loop_tuple function.
* @loop_tuple: function to call for each CIS entry of type @function. IT
* gets passed the raw tuple, the paresed tuple (if @parse is
* set) and @priv_data.
*
* pccard_loop_tuple() loops over all CIS entries of type @function, and
* calls the @loop_tuple function for each entry. If the call to @loop_tuple
* returns 0, the loop exits. Returns 0 on success or errorcode otherwise.
*/
int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function,
cisdata_t code, cisparse_t *parse, void *priv_data,
int (*loop_tuple) (tuple_t *tuple,
cisparse_t *parse,
void *priv_data))
{
tuple_t tuple;
cisdata_t *buf;
int ret;
buf = kzalloc(256, GFP_KERNEL);
if (buf == NULL) {
dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n");
return -ENOMEM;
}
tuple.TupleData = buf;
tuple.TupleDataMax = 255;
tuple.TupleOffset = 0;
tuple.DesiredTuple = code;
tuple.Attributes = 0;
ret = pccard_get_first_tuple(s, function, &tuple);
while (!ret) {
if (pccard_get_tuple_data(s, &tuple))
goto next_entry;
if (parse)
if (pcmcia_parse_tuple(&tuple, parse))
goto next_entry;
ret = loop_tuple(&tuple, parse, priv_data);
if (!ret)
break;
next_entry:
ret = pccard_get_next_tuple(s, function, &tuple);
}
kfree(buf);
return ret;
}
EXPORT_SYMBOL_GPL(pccard_loop_tuple);
/* Source: drivers/pcmcia/cistpl.c */
#if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE)
struct pcmcia_loop_mem {
struct pcmcia_device *p_dev;
void *priv_data;
int (*loop_tuple) (struct pcmcia_device *p_dev,
tuple_t *tuple,
void *priv_data);
};
/**
* pcmcia_do_loop_tuple() - internal helper for pcmcia_loop_config()
*
* pcmcia_do_loop_tuple() is the internal callback for the call from
* pcmcia_loop_tuple() to pccard_loop_tuple(). Data is transferred
* by a struct pcmcia_cfg_mem.
*/
static int pcmcia_do_loop_tuple(tuple_t *tuple, cisparse_t *parse, void *priv)
{
struct pcmcia_loop_mem *loop = priv;
return loop->loop_tuple(loop->p_dev, tuple, loop->priv_data);
};
/**
* pcmcia_loop_tuple() - loop over tuples in the CIS
* @p_dev: the struct pcmcia_device which we need to loop for.
* @code: which CIS code shall we look for?
* @priv_data: private data to be passed to the loop_tuple function.
* @loop_tuple: function to call for each CIS entry of type @function. IT
* gets passed the raw tuple and @priv_data.
*
* pcmcia_loop_tuple() loops over all CIS entries of type @function, and
* calls the @loop_tuple function for each entry. If the call to @loop_tuple
* returns 0, the loop exits. Returns 0 on success or errorcode otherwise.
*/
int pcmcia_loop_tuple(struct pcmcia_device *p_dev, cisdata_t code,
int (*loop_tuple) (struct pcmcia_device *p_dev,
tuple_t *tuple,
void *priv_data),
void *priv_data)
{
struct pcmcia_loop_mem loop = {
.p_dev = p_dev,
.loop_tuple = loop_tuple,
.priv_data = priv_data};
return pccard_loop_tuple(p_dev->socket, p_dev->func, code, NULL,
&loop, pcmcia_do_loop_tuple);
}
EXPORT_SYMBOL_GPL(pcmcia_loop_tuple);
/* Source: drivers/pcmcia/pcmcia_resource.c */
#endif /* CONFIG_PCMCIA */
#endif /* CONFIG_PCCARD */
+48
View File
@@ -0,0 +1,48 @@
/*
* Copyright 2010 Kshitij Kulshreshtha <kkhere.geo@gmail.com>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 2.6.35.
*/
#include <linux/compat.h>
#include <linux/ctype.h>
/**
* hex_to_bin - convert a hex digit to its real value
* @ch: ascii character represents hex digit
*
* hex_to_bin() converts one hex digit to its actual value or -1 in case of bad
* input.
*/
int compat_hex_to_bin(char ch)
{
if ((ch >= '0') && (ch <= '9'))
return ch - '0';
ch = tolower(ch);
if ((ch >= 'a') && (ch <= 'f'))
return ch - 'a' + 10;
return -1;
}
EXPORT_SYMBOL_GPL(compat_hex_to_bin);
/**
* noop_llseek - No Operation Performed llseek implementation
* @file: file structure to seek on
* @offset: file offset to seek to
* @origin: type of seek
*
* This is an implementation of ->llseek useable for the rare special case when
* userspace expects the seek to succeed but the (device) file is actually not
* able to perform the seek. In this case you use noop_llseek() instead of
* falling back to the default implementation of ->llseek.
*/
loff_t noop_llseek(struct file *file, loff_t offset, int origin)
{
return file->f_pos;
}
EXPORT_SYMBOL_GPL(noop_llseek);
+186
View File
@@ -0,0 +1,186 @@
/*
* Copyright 2010 Hauke Mehrtens <hauke@hauke-m.de>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 2.6.36.
*/
#include <linux/compat.h>
#include <linux/usb.h>
#ifdef CONFIG_COMPAT_USB_URB_THREAD_FIX
/* Callers must hold anchor->lock */
static void __usb_unanchor_urb(struct urb *urb, struct usb_anchor *anchor)
{
urb->anchor = NULL;
list_del(&urb->anchor_list);
usb_put_urb(urb);
if (list_empty(&anchor->urb_list))
wake_up(&anchor->wait);
}
/**
* usb_unlink_anchored_urbs - asynchronously cancel transfer requests en masse
* @anchor: anchor the requests are bound to
*
* this allows all outstanding URBs to be unlinked starting
* from the back of the queue. This function is asynchronous.
* The unlinking is just tiggered. It may happen after this
* function has returned.
*
* This routine should not be called by a driver after its disconnect
* method has returned.
*/
void compat_usb_unlink_anchored_urbs(struct usb_anchor *anchor)
{
struct urb *victim;
while ((victim = usb_get_from_anchor(anchor)) != NULL) {
usb_unlink_urb(victim);
usb_put_urb(victim);
}
}
EXPORT_SYMBOL_GPL(compat_usb_unlink_anchored_urbs);
/**
* usb_get_from_anchor - get an anchor's oldest urb
* @anchor: the anchor whose urb you want
*
* this will take the oldest urb from an anchor,
* unanchor and return it
*/
struct urb *compat_usb_get_from_anchor(struct usb_anchor *anchor)
{
struct urb *victim;
unsigned long flags;
spin_lock_irqsave(&anchor->lock, flags);
if (!list_empty(&anchor->urb_list)) {
victim = list_entry(anchor->urb_list.next, struct urb,
anchor_list);
usb_get_urb(victim);
__usb_unanchor_urb(victim, anchor);
} else {
victim = NULL;
}
spin_unlock_irqrestore(&anchor->lock, flags);
return victim;
}
EXPORT_SYMBOL_GPL(compat_usb_get_from_anchor);
/**
* usb_scuttle_anchored_urbs - unanchor all an anchor's urbs
* @anchor: the anchor whose urbs you want to unanchor
*
* use this to get rid of all an anchor's urbs
*/
void compat_usb_scuttle_anchored_urbs(struct usb_anchor *anchor)
{
struct urb *victim;
unsigned long flags;
spin_lock_irqsave(&anchor->lock, flags);
while (!list_empty(&anchor->urb_list)) {
victim = list_entry(anchor->urb_list.prev, struct urb,
anchor_list);
__usb_unanchor_urb(victim, anchor);
}
spin_unlock_irqrestore(&anchor->lock, flags);
}
EXPORT_SYMBOL_GPL(compat_usb_scuttle_anchored_urbs);
#endif /* CONFIG_COMPAT_USB_URB_THREAD_FIX */
struct workqueue_struct *system_wq __read_mostly;
struct workqueue_struct *system_long_wq __read_mostly;
struct workqueue_struct *system_nrt_wq __read_mostly;
EXPORT_SYMBOL_GPL(system_wq);
EXPORT_SYMBOL_GPL(system_long_wq);
EXPORT_SYMBOL_GPL(system_nrt_wq);
int compat_schedule_work(struct work_struct *work)
{
return queue_work(system_wq, work);
}
EXPORT_SYMBOL_GPL(compat_schedule_work);
int compat_schedule_work_on(int cpu, struct work_struct *work)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27))
return queue_work_on(cpu, system_wq, work);
#else
return queue_work(system_wq, work);
#endif
}
EXPORT_SYMBOL_GPL(compat_schedule_work_on);
int compat_schedule_delayed_work(struct delayed_work *dwork,
unsigned long delay)
{
return queue_delayed_work(system_wq, dwork, delay);
}
EXPORT_SYMBOL_GPL(compat_schedule_delayed_work);
int compat_schedule_delayed_work_on(int cpu,
struct delayed_work *dwork,
unsigned long delay)
{
return queue_delayed_work_on(cpu, system_wq, dwork, delay);
}
EXPORT_SYMBOL_GPL(compat_schedule_delayed_work_on);
#undef flush_scheduled_work
void compat_flush_scheduled_work(void)
{
/*
* It is debatable which one we should prioritize first, lets
* go with the old kernel's one first for now (keventd_wq) and
* if think its reasonable later we can flip this around.
*/
flush_workqueue(system_wq);
flush_scheduled_work();
}
EXPORT_SYMBOL_GPL(compat_flush_scheduled_work);
/**
* work_busy - test whether a work is currently pending or running
* @work: the work to be tested
*
* Test whether @work is currently pending or running. There is no
* synchronization around this function and the test result is
* unreliable and only useful as advisory hints or for debugging.
* Especially for reentrant wqs, the pending state might hide the
* running state.
*
* RETURNS:
* OR'd bitmask of WORK_BUSY_* bits.
*/
unsigned int work_busy(struct work_struct *work)
{
unsigned int ret = 0;
if (work_pending(work))
ret |= WORK_BUSY_PENDING;
return ret;
}
EXPORT_SYMBOL_GPL(work_busy);
void compat_system_workqueue_create()
{
system_wq = alloc_workqueue("events", 0, 0);
system_long_wq = alloc_workqueue("events_long", 0, 0);
system_nrt_wq = create_singlethread_workqueue("events_nrt");
BUG_ON(!system_wq || !system_long_wq || !system_nrt_wq);
}
void compat_system_workqueue_destroy()
{
destroy_workqueue(system_wq);
destroy_workqueue(system_long_wq);
destroy_workqueue(system_nrt_wq);
}
+358
View File
@@ -0,0 +1,358 @@
/*
* Copyright 2010 Hauke Mehrtens <hauke@hauke-m.de>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 2.6.37.
*/
#include <linux/compat.h>
#include <linux/netdevice.h>
#include <net/sock.h>
#include <linux/nsproxy.h>
#include <linux/vmalloc.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
static const void *net_current_ns(void)
{
return current->nsproxy->net_ns;
}
static const void *net_initial_ns(void)
{
return &init_net;
}
static const void *net_netlink_ns(struct sock *sk)
{
return sock_net(sk);
}
struct kobj_ns_type_operations net_ns_type_operations = {
.type = KOBJ_NS_TYPE_NET,
.current_ns = net_current_ns,
.netlink_ns = net_netlink_ns,
.initial_ns = net_initial_ns,
};
EXPORT_SYMBOL_GPL(net_ns_type_operations);
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)*/
#undef genl_info
#undef genl_unregister_family
static LIST_HEAD(compat_nl_fam);
static struct genl_ops *genl_get_cmd(u8 cmd, struct genl_family *family)
{
struct genl_ops *ops;
list_for_each_entry(ops, &family->family.ops_list, ops.ops_list)
if (ops->cmd == cmd)
return ops;
return NULL;
}
static int nl_doit_wrapper(struct sk_buff *skb, struct genl_info *info)
{
struct compat_genl_info compat_info;
struct genl_family *family;
struct genl_ops *ops;
int err;
list_for_each_entry(family, &compat_nl_fam, list) {
if (family->id == info->nlhdr->nlmsg_type)
goto found;
}
return -ENOENT;
found:
ops = genl_get_cmd(info->genlhdr->cmd, family);
if (!ops)
return -ENOENT;
memset(&compat_info.user_ptr, 0, sizeof(compat_info.user_ptr));
compat_info.info = info;
#define __copy(_field) compat_info._field = info->_field
__copy(snd_seq);
__copy(snd_pid);
__copy(genlhdr);
__copy(attrs);
#undef __copy
if (family->pre_doit) {
err = family->pre_doit(ops, skb, &compat_info);
if (err)
return err;
}
err = ops->doit(skb, &compat_info);
if (family->post_doit)
family->post_doit(ops, skb, &compat_info);
return err;
}
int compat_genl_register_family_with_ops(struct genl_family *family,
struct genl_ops *ops, size_t n_ops)
{
int i, ret;
#define __copy(_field) family->family._field = family->_field
__copy(id);
__copy(hdrsize);
__copy(version);
__copy(maxattr);
strncpy(family->family.name, family->name, sizeof(family->family.name));
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))
__copy(netnsok);
#endif
#undef __copy
ret = genl_register_family(&family->family);
if (ret < 0)
return ret;
family->attrbuf = family->family.attrbuf;
family->id = family->family.id;
for (i = 0; i < n_ops; i++) {
#define __copy(_field) ops[i].ops._field = ops[i]._field
__copy(cmd);
__copy(flags);
__copy(policy);
__copy(dumpit);
__copy(done);
#undef __copy
if (ops[i].doit)
ops[i].ops.doit = nl_doit_wrapper;
ret = genl_register_ops(&family->family, &ops[i].ops);
if (ret < 0)
goto error_ops;
}
list_add(&family->list, &compat_nl_fam);
return ret;
error_ops:
compat_genl_unregister_family(family);
return ret;
}
EXPORT_SYMBOL_GPL(compat_genl_register_family_with_ops);
int compat_genl_unregister_family(struct genl_family *family)
{
int err;
err = genl_unregister_family(&family->family);
list_del(&family->list);
return err;
}
EXPORT_SYMBOL_GPL(compat_genl_unregister_family);
#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
#undef led_brightness_set
#undef led_classdev_unregister
static DEFINE_SPINLOCK(led_lock);
static LIST_HEAD(led_timers);
struct led_timer {
struct list_head list;
struct led_classdev *cdev;
struct timer_list blink_timer;
unsigned long blink_delay_on;
unsigned long blink_delay_off;
int blink_brightness;
};
static void led_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
led_cdev->brightness = brightness;
led_cdev->brightness_set(led_cdev, brightness);
}
static struct led_timer *led_get_timer(struct led_classdev *led_cdev)
{
struct led_timer *p;
unsigned long flags;
spin_lock_irqsave(&led_lock, flags);
list_for_each_entry(p, &led_timers, list) {
if (p->cdev == led_cdev)
goto found;
}
p = NULL;
found:
spin_unlock_irqrestore(&led_lock, flags);
return p;
}
static void led_stop_software_blink(struct led_timer *led)
{
del_timer_sync(&led->blink_timer);
led->blink_delay_on = 0;
led->blink_delay_off = 0;
}
static void led_timer_function(unsigned long data)
{
struct led_timer *led = (struct led_timer *)data;
unsigned long brightness;
unsigned long delay;
if (!led->blink_delay_on || !led->blink_delay_off) {
led->cdev->brightness_set(led->cdev, LED_OFF);
return;
}
brightness = led->cdev->brightness;
if (!brightness) {
/* Time to switch the LED on. */
brightness = led->blink_brightness;
delay = led->blink_delay_on;
} else {
/* Store the current brightness value to be able
* to restore it when the delay_off period is over.
*/
led->blink_brightness = brightness;
brightness = LED_OFF;
delay = led->blink_delay_off;
}
led_brightness_set(led->cdev, brightness);
mod_timer(&led->blink_timer, jiffies + msecs_to_jiffies(delay));
}
static struct led_timer *led_new_timer(struct led_classdev *led_cdev)
{
struct led_timer *led;
unsigned long flags;
led = kzalloc(sizeof(struct led_timer), GFP_ATOMIC);
if (!led)
return NULL;
led->cdev = led_cdev;
init_timer(&led->blink_timer);
led->blink_timer.function = led_timer_function;
led->blink_timer.data = (unsigned long) led;
spin_lock_irqsave(&led_lock, flags);
list_add(&led->list, &led_timers);
spin_unlock_irqrestore(&led_lock, flags);
return led;
}
void led_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off)
{
struct led_timer *led;
int current_brightness;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25))
if (led_cdev->blink_set &&
!led_cdev->blink_set(led_cdev, delay_on, delay_off))
return;
#endif
led = led_get_timer(led_cdev);
if (!led) {
led = led_new_timer(led_cdev);
if (!led)
return;
}
/* blink with 1 Hz as default if nothing specified */
if (!*delay_on && !*delay_off)
*delay_on = *delay_off = 500;
if (led->blink_delay_on == *delay_on &&
led->blink_delay_off == *delay_off)
return;
current_brightness = led_cdev->brightness;
if (current_brightness)
led->blink_brightness = current_brightness;
if (!led->blink_brightness)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
led->blink_brightness = led_cdev->max_brightness;
#else
led->blink_brightness = LED_FULL;
#endif
led_stop_software_blink(led);
led->blink_delay_on = *delay_on;
led->blink_delay_off = *delay_off;
/* never on - don't blink */
if (!*delay_on)
return;
/* never off - just set to brightness */
if (!*delay_off) {
led_brightness_set(led_cdev, led->blink_brightness);
return;
}
mod_timer(&led->blink_timer, jiffies + 1);
}
EXPORT_SYMBOL_GPL(led_blink_set);
void compat_led_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct led_timer *led = led_get_timer(led_cdev);
if (led)
led_stop_software_blink(led);
return led_cdev->brightness_set(led_cdev, brightness);
}
EXPORT_SYMBOL_GPL(compat_led_brightness_set);
void compat_led_classdev_unregister(struct led_classdev *led_cdev)
{
struct led_timer *led = led_get_timer(led_cdev);
unsigned long flags;
if (led) {
del_timer_sync(&led->blink_timer);
spin_lock_irqsave(&led_lock, flags);
list_del(&led->list);
spin_unlock_irqrestore(&led_lock, flags);
kfree(led);
}
led_classdev_unregister(led_cdev);
}
EXPORT_SYMBOL_GPL(compat_led_classdev_unregister);
/**
* vzalloc - allocate virtually contiguous memory with zero fill
* @size: allocation size
* Allocate enough pages to cover @size from the page level
* allocator and map them into contiguous kernel virtual space.
* The memory allocated is set to zero.
*
* For tight control over page level allocator and protection flags
* use __vmalloc() instead.
*/
void *compat_vzalloc(unsigned long size)
{
void *buf;
buf = vmalloc(size);
if (buf)
memset(buf, 0, size);
return buf;
}
EXPORT_SYMBOL_GPL(compat_vzalloc);
#endif
+50
View File
@@ -0,0 +1,50 @@
/*
* Copyright 2010 Hauke Mehrtens <hauke@hauke-m.de>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 2.6.38.
*/
#include <linux/compat.h>
#include <linux/module.h>
#include <linux/bug.h>
/**
* ewma_init() - Initialize EWMA parameters
* @avg: Average structure
* @factor: Factor to use for the scaled up internal value. The maximum value
* of averages can be ULONG_MAX/(factor*weight).
* @weight: Exponential weight, or decay rate. This defines how fast the
* influence of older values decreases. Has to be bigger than 1.
*
* Initialize the EWMA parameters for a given struct ewma @avg.
*/
void compat_ewma_init(struct ewma *avg, unsigned long factor, unsigned long weight)
{
WARN_ON(weight <= 1 || factor == 0);
avg->internal = 0;
avg->weight = weight;
avg->factor = factor;
}
EXPORT_SYMBOL_GPL(compat_ewma_init);
/**
* ewma_add() - Exponentially weighted moving average (EWMA)
* @avg: Average structure
* @val: Current value
*
* Add a sample to the average.
*/
struct ewma *compat_ewma_add(struct ewma *avg, unsigned long val)
{
avg->internal = avg->internal ?
(((avg->internal * (avg->weight - 1)) +
(val * avg->factor)) / avg->weight) :
(val * avg->factor);
return avg;
}
EXPORT_SYMBOL_GPL(compat_ewma_add);
+114
View File
@@ -0,0 +1,114 @@
/*
* Copyright 2011 Hauke Mehrtens <hauke@hauke-m.de>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 2.6.39.
*/
#include <linux/compat.h>
#include <linux/tty.h>
#include <linux/sched.h>
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27))
/*
* Termios Helper Methods
*/
static void unset_locked_termios(struct ktermios *termios,
struct ktermios *old,
struct ktermios *locked)
{
int i;
#define NOSET_MASK(x, y, z) (x = ((x) & ~(z)) | ((y) & (z)))
if (!locked) {
printk(KERN_WARNING "Warning?!? termios_locked is NULL.\n");
return;
}
NOSET_MASK(termios->c_iflag, old->c_iflag, locked->c_iflag);
NOSET_MASK(termios->c_oflag, old->c_oflag, locked->c_oflag);
NOSET_MASK(termios->c_cflag, old->c_cflag, locked->c_cflag);
NOSET_MASK(termios->c_lflag, old->c_lflag, locked->c_lflag);
termios->c_line = locked->c_line ? old->c_line : termios->c_line;
for (i = 0; i < NCCS; i++)
termios->c_cc[i] = locked->c_cc[i] ?
old->c_cc[i] : termios->c_cc[i];
/* FIXME: What should we do for i/ospeed */
}
/**
* tty_set_termios - update termios values
* @tty: tty to update
* @new_termios: desired new value
*
* Perform updates to the termios values set on this terminal. There
* is a bit of layering violation here with n_tty in terms of the
* internal knowledge of this function.
*
* Locking: termios_mutex
*/
int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
{
struct ktermios old_termios;
struct tty_ldisc *ld;
unsigned long flags;
/*
* Perform the actual termios internal changes under lock.
*/
/* FIXME: we need to decide on some locking/ordering semantics
for the set_termios notification eventually */
mutex_lock(&tty->termios_mutex);
old_termios = *tty->termios;
*tty->termios = *new_termios;
unset_locked_termios(tty->termios, &old_termios, tty->termios_locked);
/* See if packet mode change of state. */
if (tty->link && tty->link->packet) {
int extproc = (old_termios.c_lflag & EXTPROC) |
(tty->termios->c_lflag & EXTPROC);
int old_flow = ((old_termios.c_iflag & IXON) &&
(old_termios.c_cc[VSTOP] == '\023') &&
(old_termios.c_cc[VSTART] == '\021'));
int new_flow = (I_IXON(tty) &&
STOP_CHAR(tty) == '\023' &&
START_CHAR(tty) == '\021');
if ((old_flow != new_flow) || extproc) {
spin_lock_irqsave(&tty->ctrl_lock, flags);
if (old_flow != new_flow) {
tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
if (new_flow)
tty->ctrl_status |= TIOCPKT_DOSTOP;
else
tty->ctrl_status |= TIOCPKT_NOSTOP;
}
if (extproc)
tty->ctrl_status |= TIOCPKT_IOCTL;
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
wake_up_interruptible(&tty->link->read_wait);
}
}
if (tty->ops->set_termios)
(*tty->ops->set_termios)(tty, &old_termios);
else
tty_termios_copy_hw(tty->termios, &old_termios);
ld = tty_ldisc_ref(tty);
if (ld != NULL) {
if (ld->ops->set_termios)
(ld->ops->set_termios)(tty, &old_termios);
tty_ldisc_deref(ld);
}
mutex_unlock(&tty->termios_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(tty_set_termios);
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) */
+85
View File
@@ -0,0 +1,85 @@
/*
* Copyright 2011 Hauke Mehrtens <hauke@hauke-m.de>
* Copyright 2011 Alexey Dobriyan <adobriyan@gmail.com>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 3.0.
*/
#include <linux/compat.h>
#include <linux/if_ether.h>
/* This pulls-in a lot of non-exported symbol backports
* on kernels older than 2.6.32. There's no harm for not
* making this available on kernels < 2.6.32. */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))
#include <linux/pagemap.h>
/* This backports:
*
* commit d9d90e5eb70e09903dadff42099b6c948f814050
* Author: Hugh Dickins <hughd@google.com>
* Date: Mon Jun 27 16:18:04 2011 -0700
*
* tmpfs: add shmem_read_mapping_page_gfp
*/
struct page *shmem_read_mapping_page_gfp(struct address_space *mapping,
pgoff_t index, gfp_t gfp)
{
return read_cache_page_gfp(mapping, index, gfp);
}
EXPORT_SYMBOL_GPL(shmem_read_mapping_page_gfp);
#endif
int mac_pton(const char *s, u8 *mac)
{
int i;
/* XX:XX:XX:XX:XX:XX */
if (strlen(s) < 3 * ETH_ALEN - 1)
return 0;
/* Don't dirty result unless string is valid MAC. */
for (i = 0; i < ETH_ALEN; i++) {
if (!strchr("0123456789abcdefABCDEF", s[i * 3]))
return 0;
if (!strchr("0123456789abcdefABCDEF", s[i * 3 + 1]))
return 0;
if (i != ETH_ALEN - 1 && s[i * 3 + 2] != ':')
return 0;
}
for (i = 0; i < ETH_ALEN; i++) {
mac[i] = (hex_to_bin(s[i * 3]) << 4) | hex_to_bin(s[i * 3 + 1]);
}
return 1;
}
EXPORT_SYMBOL_GPL(mac_pton);
#define kstrto_from_user(f, g, type) \
int f(const char __user *s, size_t count, unsigned int base, type *res) \
{ \
/* sign, base 2 representation, newline, terminator */ \
char buf[1 + sizeof(type) * 8 + 1 + 1]; \
\
count = min(count, sizeof(buf) - 1); \
if (copy_from_user(buf, s, count)) \
return -EFAULT; \
buf[count] = '\0'; \
return g(buf, base, res); \
} \
EXPORT_SYMBOL_GPL(f)
kstrto_from_user(kstrtoull_from_user, kstrtoull, unsigned long long);
kstrto_from_user(kstrtoll_from_user, kstrtoll, long long);
kstrto_from_user(kstrtoul_from_user, kstrtoul, unsigned long);
kstrto_from_user(kstrtol_from_user, kstrtol, long);
kstrto_from_user(kstrtouint_from_user, kstrtouint, unsigned int);
kstrto_from_user(kstrtoint_from_user, kstrtoint, int);
kstrto_from_user(kstrtou16_from_user, kstrtou16, u16);
kstrto_from_user(kstrtos16_from_user, kstrtos16, s16);
kstrto_from_user(kstrtou8_from_user, kstrtou8, u8);
kstrto_from_user(kstrtos8_from_user, kstrtos8, s8);
+107
View File
@@ -0,0 +1,107 @@
/*
* Copyright 2012 Hauke Mehrtens <hauke@hauke-m.de>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 3.1.
*/
#include <linux/idr.h>
#include <linux/cpufreq.h>
/* This backports:
* commit 3d73710880afa3d61cf57b5d4eb192e812eb7e4f
* Author: Jesse Barnes <jbarnes@virtuousgeek.org>
* Date: Tue Jun 28 10:59:12 2011 -0700
*
* cpufreq: expose a cpufreq_quick_get_max routine
*/
unsigned int compat_cpufreq_quick_get_max(unsigned int cpu)
{
struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
unsigned int ret_freq = 0;
if (policy) {
ret_freq = policy->max;
cpufreq_cpu_put(policy);
}
return ret_freq;
}
EXPORT_SYMBOL(compat_cpufreq_quick_get_max);
static DEFINE_SPINLOCK(compat_simple_ida_lock);
/**
* ida_simple_get - get a new id.
* @ida: the (initialized) ida.
* @start: the minimum id (inclusive, < 0x8000000)
* @end: the maximum id (exclusive, < 0x8000000 or 0)
* @gfp_mask: memory allocation flags
*
* Allocates an id in the range start <= id < end, or returns -ENOSPC.
* On memory allocation failure, returns -ENOMEM.
*
* Use ida_simple_remove() to get rid of an id.
*/
int compat_ida_simple_get(struct ida *ida, unsigned int start, unsigned int end,
gfp_t gfp_mask)
{
int ret, id;
unsigned int max;
unsigned long flags;
BUG_ON((int)start < 0);
BUG_ON((int)end < 0);
if (end == 0)
max = 0x80000000;
else {
BUG_ON(end < start);
max = end - 1;
}
again:
if (!ida_pre_get(ida, gfp_mask))
return -ENOMEM;
spin_lock_irqsave(&compat_simple_ida_lock, flags);
ret = ida_get_new_above(ida, start, &id);
if (!ret) {
if (id > max) {
ida_remove(ida, id);
ret = -ENOSPC;
} else {
ret = id;
}
}
spin_unlock_irqrestore(&compat_simple_ida_lock, flags);
if (unlikely(ret == -EAGAIN))
goto again;
return ret;
}
EXPORT_SYMBOL(compat_ida_simple_get);
/**
* ida_simple_remove - remove an allocated id.
* @ida: the (initialized) ida.
* @id: the id returned by ida_simple_get.
*/
void compat_ida_simple_remove(struct ida *ida, unsigned int id)
{
unsigned long flags;
BUG_ON((int)id < 0);
spin_lock_irqsave(&compat_simple_ida_lock, flags);
ida_remove(ida, id);
spin_unlock_irqrestore(&compat_simple_ida_lock, flags);
}
EXPORT_SYMBOL(compat_ida_simple_remove);
/* source lib/idr.c */
+34
View File
@@ -0,0 +1,34 @@
/*
* Copyright 2012 Luis R. Rodriguez <mcgrof@frijolero.org>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 3.2.
*/
#include <linux/kernel.h>
#include <linux/device.h>
int __netdev_printk(const char *level, const struct net_device *dev,
struct va_format *vaf)
{
int r;
if (dev && dev->dev.parent)
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35))
r = dev_printk(level, dev->dev.parent, "%s: %pV",
netdev_name(dev), vaf);
#else
/* XXX: this could likely be done better but I'm lazy */
r = printk("%s%s: %pV", level, netdev_name(dev), vaf);
#endif
else if (dev)
r = printk("%s%s: %pV", level, netdev_name(dev), vaf);
else
r = printk("%s(NULL net_device): %pV", level, vaf);
return r;
}
EXPORT_SYMBOL_GPL(__netdev_printk);
+173
View File
@@ -0,0 +1,173 @@
/*
* Copyright 2012 Luis R. Rodriguez <mcgrof@frijolero.org>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 3.3.
*/
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/skbuff.h>
#include <linux/module.h>
#include <net/dst.h>
#include <net/xfrm.h>
static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
{
new->tstamp = old->tstamp;
new->dev = old->dev;
new->transport_header = old->transport_header;
new->network_header = old->network_header;
new->mac_header = old->mac_header;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
skb_dst_copy(new, old);
new->rxhash = old->rxhash;
#else
skb_dst_set(new, dst_clone(skb_dst(old)));
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0))
new->ooo_okay = old->ooo_okay;
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0))
new->l4_rxhash = old->l4_rxhash;
#endif
#ifdef CONFIG_XFRM
new->sp = secpath_get(old->sp);
#endif
memcpy(new->cb, old->cb, sizeof(old->cb));
new->csum = old->csum;
new->local_df = old->local_df;
new->pkt_type = old->pkt_type;
new->ip_summed = old->ip_summed;
skb_copy_queue_mapping(new, old);
new->priority = old->priority;
#if IS_ENABLED(CONFIG_IP_VS)
new->ipvs_property = old->ipvs_property;
#endif
new->protocol = old->protocol;
new->mark = old->mark;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33))
new->skb_iif = old->skb_iif;
#endif
__nf_copy(new, old);
#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE)
new->nf_trace = old->nf_trace;
#endif
#ifdef CONFIG_NET_SCHED
new->tc_index = old->tc_index;
#ifdef CONFIG_NET_CLS_ACT
new->tc_verd = old->tc_verd;
#endif
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27))
new->vlan_tci = old->vlan_tci;
#endif
skb_copy_secmark(new, old);
}
static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
{
#ifndef NET_SKBUFF_DATA_USES_OFFSET
/*
* Shift between the two data areas in bytes
*/
unsigned long offset = new->data - old->data;
#endif
__copy_skb_header(new, old);
#ifndef NET_SKBUFF_DATA_USES_OFFSET
/* {transport,network,mac}_header are relative to skb->head */
new->transport_header += offset;
new->network_header += offset;
if (skb_mac_header_was_set(new))
new->mac_header += offset;
#endif
skb_shinfo(new)->gso_size = skb_shinfo(old)->gso_size;
skb_shinfo(new)->gso_segs = skb_shinfo(old)->gso_segs;
skb_shinfo(new)->gso_type = skb_shinfo(old)->gso_type;
}
static void skb_clone_fraglist(struct sk_buff *skb)
{
struct sk_buff *list;
skb_walk_frags(skb, list)
skb_get(list);
}
/**
* __pskb_copy - create copy of an sk_buff with private head.
* @skb: buffer to copy
* @headroom: headroom of new skb
* @gfp_mask: allocation priority
*
* Make a copy of both an &sk_buff and part of its data, located
* in header. Fragmented data remain shared. This is used when
* the caller wishes to modify only header of &sk_buff and needs
* private copy of the header to alter. Returns %NULL on failure
* or the pointer to the buffer on success.
* The returned buffer has a reference count of 1.
*/
struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom, gfp_t gfp_mask)
{
unsigned int size = skb_headlen(skb) + headroom;
struct sk_buff *n = alloc_skb(size, gfp_mask);
if (!n)
goto out;
/* Set the data pointer */
skb_reserve(n, headroom);
/* Set the tail pointer and length */
skb_put(n, skb_headlen(skb));
/* Copy the bytes */
skb_copy_from_linear_data(skb, n->data, n->len);
n->truesize += skb->data_len;
n->data_len = skb->data_len;
n->len = skb->len;
if (skb_shinfo(skb)->nr_frags) {
int i;
/*
* SKBTX_DEV_ZEROCOPY was added on 3.1 as well but requires ubuf
* stuff added to the skb which we do not have
*/
#if 0
if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) {
if (skb_copy_ubufs(skb, gfp_mask)) {
kfree_skb(n);
n = NULL;
goto out;
}
}
#endif
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i];
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0))
skb_frag_ref(skb, i);
#else
get_page(skb_shinfo(skb)->frags[i].page);
#endif
}
skb_shinfo(n)->nr_frags = i;
}
if (skb_has_frag_list(skb)) {
skb_shinfo(n)->frag_list = skb_shinfo(skb)->frag_list;
skb_clone_fraglist(n);
}
copy_skb_header(n, skb);
out:
return n;
}
EXPORT_SYMBOL_GPL(__pskb_copy);
+491
View File
@@ -0,0 +1,491 @@
/*
* Copyright 2012 Luis R. Rodriguez <mcgrof@frijolero.org>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 3.4.
*/
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/wait.h>
/* __wake_up_common was declared as part of the wait.h until
* 2.6.31 in which they made it private to the scheduler. Prefix it with
* compat to avoid double declaration issues.
*/
static void compat_wake_up_common(wait_queue_head_t *q, unsigned int mode,
int nr_exclusive, int wake_flags, void *key)
{
wait_queue_t *curr, *next;
list_for_each_entry_safe(curr, next, &q->task_list, task_list) {
unsigned flags = curr->flags;
if (curr->func(curr, mode, wake_flags, key) &&
(flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
break;
}
}
/* The last 'nr' parameter was added to the __wake_up_locked() function
* in 3.4 kernel. Define a new one prefixed with compat_ for the new API.
*/
void compat_wake_up_locked(wait_queue_head_t *q, unsigned int mode, int nr)
{
compat_wake_up_common(q, mode, nr, 0, NULL);
}
EXPORT_SYMBOL_GPL(compat_wake_up_locked);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34))
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/delay.h>
#define setsda(adap, val) adap->setsda(adap->data, val)
#define setscl(adap, val) adap->setscl(adap->data, val)
#define getsda(adap) adap->getsda(adap->data)
#define getscl(adap) adap->getscl(adap->data)
#define bit_dbg(level, dev, format, args...) \
do {} while (0)
static inline void sdalo(struct i2c_algo_bit_data *adap)
{
setsda(adap, 0);
udelay((adap->udelay + 1) / 2);
}
static inline void sdahi(struct i2c_algo_bit_data *adap)
{
setsda(adap, 1);
udelay((adap->udelay + 1) / 2);
}
static inline void scllo(struct i2c_algo_bit_data *adap)
{
setscl(adap, 0);
udelay(adap->udelay / 2);
}
static int sclhi(struct i2c_algo_bit_data *adap)
{
unsigned long start;
setscl(adap, 1);
/* Not all adapters have scl sense line... */
if (!adap->getscl)
goto done;
start = jiffies;
while (!getscl(adap)) {
/* This hw knows how to read the clock line, so we wait
* until it actually gets high. This is safer as some
* chips may hold it low ("clock stretching") while they
* are processing data internally.
*/
if (time_after(jiffies, start + adap->timeout)) {
/* Test one last time, as we may have been preempted
* between last check and timeout test.
*/
if (getscl(adap))
break;
return -ETIMEDOUT;
}
cpu_relax();
}
#ifdef DEBUG
if (jiffies != start && i2c_debug >= 3)
pr_debug("i2c-algo-bit: needed %ld jiffies for SCL to go "
"high\n", jiffies - start);
#endif
done:
udelay(adap->udelay);
return 0;
}
static void i2c_start(struct i2c_algo_bit_data *adap)
{
/* assert: scl, sda are high */
setsda(adap, 0);
udelay(adap->udelay);
scllo(adap);
}
static void i2c_repstart(struct i2c_algo_bit_data *adap)
{
/* assert: scl is low */
sdahi(adap);
sclhi(adap);
setsda(adap, 0);
udelay(adap->udelay);
scllo(adap);
}
static void i2c_stop(struct i2c_algo_bit_data *adap)
{
/* assert: scl is low */
sdalo(adap);
sclhi(adap);
setsda(adap, 1);
udelay(adap->udelay);
}
static int i2c_outb(struct i2c_adapter *i2c_adap, unsigned char c)
{
int i;
int sb;
int ack;
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
/* assert: scl is low */
for (i = 7; i >= 0; i--) {
sb = (c >> i) & 1;
setsda(adap, sb);
udelay((adap->udelay + 1) / 2);
if (sclhi(adap) < 0) { /* timed out */
bit_dbg(1, &i2c_adap->dev, "i2c_outb: 0x%02x, "
"timeout at bit #%d\n", (int)c, i);
return -ETIMEDOUT;
}
/* FIXME do arbitration here:
* if (sb && !getsda(adap)) -> ouch! Get out of here.
*
* Report a unique code, so higher level code can retry
* the whole (combined) message and *NOT* issue STOP.
*/
scllo(adap);
}
sdahi(adap);
if (sclhi(adap) < 0) { /* timeout */
bit_dbg(1, &i2c_adap->dev, "i2c_outb: 0x%02x, "
"timeout at ack\n", (int)c);
return -ETIMEDOUT;
}
/* read ack: SDA should be pulled down by slave, or it may
* NAK (usually to report problems with the data we wrote).
*/
ack = !getsda(adap); /* ack: sda is pulled low -> success */
bit_dbg(2, &i2c_adap->dev, "i2c_outb: 0x%02x %s\n", (int)c,
ack ? "A" : "NA");
scllo(adap);
return ack;
/* assert: scl is low (sda undef) */
}
static int i2c_inb(struct i2c_adapter *i2c_adap)
{
/* read byte via i2c port, without start/stop sequence */
/* acknowledge is sent in i2c_read. */
int i;
unsigned char indata = 0;
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
/* assert: scl is low */
sdahi(adap);
for (i = 0; i < 8; i++) {
if (sclhi(adap) < 0) { /* timeout */
bit_dbg(1, &i2c_adap->dev, "i2c_inb: timeout at bit "
"#%d\n", 7 - i);
return -ETIMEDOUT;
}
indata *= 2;
if (getsda(adap))
indata |= 0x01;
setscl(adap, 0);
udelay(i == 7 ? adap->udelay / 2 : adap->udelay);
}
/* assert: scl is low */
return indata;
}
static int try_address(struct i2c_adapter *i2c_adap,
unsigned char addr, int retries)
{
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
int i, ret = 0;
for (i = 0; i <= retries; i++) {
ret = i2c_outb(i2c_adap, addr);
if (ret == 1 || i == retries)
break;
bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n");
i2c_stop(adap);
udelay(adap->udelay);
yield();
bit_dbg(3, &i2c_adap->dev, "emitting start condition\n");
i2c_start(adap);
}
if (i && ret)
bit_dbg(1, &i2c_adap->dev, "Used %d tries to %s client at "
"0x%02x: %s\n", i + 1,
addr & 1 ? "read from" : "write to", addr >> 1,
ret == 1 ? "success" : "failed, timeout?");
return ret;
}
static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
{
unsigned short flags = msg->flags;
unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK;
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
unsigned char addr;
int ret, retries;
retries = nak_ok ? 0 : i2c_adap->retries;
if (flags & I2C_M_TEN) {
/* a ten bit address */
addr = 0xf0 | ((msg->addr >> 7) & 0x06);
bit_dbg(2, &i2c_adap->dev, "addr0: %d\n", addr);
/* try extended address code...*/
ret = try_address(i2c_adap, addr, retries);
if ((ret != 1) && !nak_ok) {
dev_err(&i2c_adap->dev,
"died at extended address code\n");
return -ENXIO;
}
/* the remaining 8 bit address */
ret = i2c_outb(i2c_adap, msg->addr & 0xff);
if ((ret != 1) && !nak_ok) {
/* the chip did not ack / xmission error occurred */
dev_err(&i2c_adap->dev, "died at 2nd address code\n");
return -ENXIO;
}
if (flags & I2C_M_RD) {
bit_dbg(3, &i2c_adap->dev, "emitting repeated "
"start condition\n");
i2c_repstart(adap);
/* okay, now switch into reading mode */
addr |= 0x01;
ret = try_address(i2c_adap, addr, retries);
if ((ret != 1) && !nak_ok) {
dev_err(&i2c_adap->dev,
"died at repeated address code\n");
return -EIO;
}
}
} else { /* normal 7bit address */
addr = msg->addr << 1;
if (flags & I2C_M_RD)
addr |= 1;
if (flags & I2C_M_REV_DIR_ADDR)
addr ^= 1;
ret = try_address(i2c_adap, addr, retries);
if ((ret != 1) && !nak_ok)
return -ENXIO;
}
return 0;
}
static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
{
const unsigned char *temp = msg->buf;
int count = msg->len;
unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK;
int retval;
int wrcount = 0;
while (count > 0) {
retval = i2c_outb(i2c_adap, *temp);
/* OK/ACK; or ignored NAK */
if ((retval > 0) || (nak_ok && (retval == 0))) {
count--;
temp++;
wrcount++;
/* A slave NAKing the master means the slave didn't like
* something about the data it saw. For example, maybe
* the SMBus PEC was wrong.
*/
} else if (retval == 0) {
dev_err(&i2c_adap->dev, "sendbytes: NAK bailout.\n");
return -EIO;
/* Timeout; or (someday) lost arbitration
*
* FIXME Lost ARB implies retrying the transaction from
* the first message, after the "winning" master issues
* its STOP. As a rule, upper layer code has no reason
* to know or care about this ... it is *NOT* an error.
*/
} else {
dev_err(&i2c_adap->dev, "sendbytes: error %d\n",
retval);
return retval;
}
}
return wrcount;
}
static int acknak(struct i2c_adapter *i2c_adap, int is_ack)
{
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
/* assert: sda is high */
if (is_ack) /* send ack */
setsda(adap, 0);
udelay((adap->udelay + 1) / 2);
if (sclhi(adap) < 0) { /* timeout */
dev_err(&i2c_adap->dev, "readbytes: ack/nak timeout\n");
return -ETIMEDOUT;
}
scllo(adap);
return 0;
}
static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
{
int inval;
int rdcount = 0; /* counts bytes read */
unsigned char *temp = msg->buf;
int count = msg->len;
const unsigned flags = msg->flags;
while (count > 0) {
inval = i2c_inb(i2c_adap);
if (inval >= 0) {
*temp = inval;
rdcount++;
} else { /* read timed out */
break;
}
temp++;
count--;
/* Some SMBus transactions require that we receive the
transaction length as the first read byte. */
if (rdcount == 1 && (flags & I2C_M_RECV_LEN)) {
if (inval <= 0 || inval > I2C_SMBUS_BLOCK_MAX) {
if (!(flags & I2C_M_NO_RD_ACK))
acknak(i2c_adap, 0);
dev_err(&i2c_adap->dev, "readbytes: invalid "
"block length (%d)\n", inval);
return -EPROTO;
}
/* The original count value accounts for the extra
bytes, that is, either 1 for a regular transaction,
or 2 for a PEC transaction. */
count += inval;
msg->len += inval;
}
bit_dbg(2, &i2c_adap->dev, "readbytes: 0x%02x %s\n",
inval,
(flags & I2C_M_NO_RD_ACK)
? "(no ack/nak)"
: (count ? "A" : "NA"));
if (!(flags & I2C_M_NO_RD_ACK)) {
inval = acknak(i2c_adap, count);
if (inval < 0)
return inval;
}
}
return rdcount;
}
static u32 bit_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL |
I2C_FUNC_SMBUS_READ_BLOCK_DATA |
I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING;
}
static int bit_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg msgs[], int num)
{
struct i2c_msg *pmsg;
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
int i, ret;
unsigned short nak_ok;
if (adap->pre_xfer) {
ret = adap->pre_xfer(i2c_adap);
if (ret < 0)
return ret;
}
bit_dbg(3, &i2c_adap->dev, "emitting start condition\n");
i2c_start(adap);
for (i = 0; i < num; i++) {
pmsg = &msgs[i];
nak_ok = pmsg->flags & I2C_M_IGNORE_NAK;
if (!(pmsg->flags & I2C_M_NOSTART)) {
if (i) {
bit_dbg(3, &i2c_adap->dev, "emitting "
"repeated start condition\n");
i2c_repstart(adap);
}
ret = bit_doAddress(i2c_adap, pmsg);
if ((ret != 0) && !nak_ok) {
bit_dbg(1, &i2c_adap->dev, "NAK from "
"device addr 0x%02x msg #%d\n",
msgs[i].addr, i);
goto bailout;
}
}
if (pmsg->flags & I2C_M_RD) {
/* read bytes into buffer*/
ret = readbytes(i2c_adap, pmsg);
if (ret >= 1)
bit_dbg(2, &i2c_adap->dev, "read %d byte%s\n",
ret, ret == 1 ? "" : "s");
if (ret < pmsg->len) {
if (ret >= 0)
ret = -EIO;
goto bailout;
}
} else {
/* write bytes from buffer */
ret = sendbytes(i2c_adap, pmsg);
if (ret >= 1)
bit_dbg(2, &i2c_adap->dev, "wrote %d byte%s\n",
ret, ret == 1 ? "" : "s");
if (ret < pmsg->len) {
if (ret >= 0)
ret = -EIO;
goto bailout;
}
}
}
ret = i;
bailout:
bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n");
i2c_stop(adap);
if (adap->post_xfer)
adap->post_xfer(i2c_adap);
return ret;
}
const struct i2c_algorithm i2c_bit_algo = {
.master_xfer = bit_xfer,
.functionality = bit_func,
};
EXPORT_SYMBOL(i2c_bit_algo);
#endif
int simple_open(struct inode *inode, struct file *file)
{
if (inode->i_private)
file->private_data = inode->i_private;
return 0;
}
EXPORT_SYMBOL_GPL(simple_open);
+251
View File
@@ -0,0 +1,251 @@
/*
* Copyright 2012 Luis R. Rodriguez <mcgrof@do-not-panic.com>
*
* 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.
*
* Compatibility file for Linux wireless for kernels 3.7.
*/
#include <linux/workqueue.h>
#include <linux/export.h>
bool mod_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork,
unsigned long delay)
{
cancel_delayed_work(dwork);
queue_delayed_work(wq, dwork, delay);
return false;
}
EXPORT_SYMBOL_GPL(mod_delayed_work);
/*
* Kernels >= 3.7 get their PCI-E Capabilities Register cached
* via the pci_dev->pcie_flags_reg so for older kernels we have
* no other option but to read this every single time we need
* it accessed. If we really cared to improve the efficiency
* of this we could try to find an unused u16 varible on the
* pci_dev but if we found it we likely would remove it from
* the kernel anyway right? Bite me.
*/
static inline u16 pcie_flags_reg(struct pci_dev *dev)
{
int pos;
u16 reg16;
pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
if (!pos)
return 0;
pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &reg16);
return reg16;
}
static inline int pci_pcie_type(struct pci_dev *dev)
{
return (pcie_flags_reg(dev) & PCI_EXP_FLAGS_TYPE) >> 4;
}
static inline int pcie_cap_version(struct pci_dev *dev)
{
return pcie_flags_reg(dev) & PCI_EXP_FLAGS_VERS;
}
static inline bool pcie_cap_has_devctl(const struct pci_dev *dev)
{
return true;
}
static inline bool pcie_cap_has_lnkctl(struct pci_dev *dev)
{
int type = pci_pcie_type(dev);
return pcie_cap_version(dev) > 1 ||
type == PCI_EXP_TYPE_ROOT_PORT ||
type == PCI_EXP_TYPE_ENDPOINT ||
type == PCI_EXP_TYPE_LEG_END;
}
static inline bool pcie_cap_has_sltctl(struct pci_dev *dev)
{
int type = pci_pcie_type(dev);
return pcie_cap_version(dev) > 1 ||
type == PCI_EXP_TYPE_ROOT_PORT ||
(type == PCI_EXP_TYPE_DOWNSTREAM &&
pcie_flags_reg(dev) & PCI_EXP_FLAGS_SLOT);
}
static inline bool pcie_cap_has_rtctl(struct pci_dev *dev)
{
int type = pci_pcie_type(dev);
return pcie_cap_version(dev) > 1 ||
type == PCI_EXP_TYPE_ROOT_PORT ||
type == PCI_EXP_TYPE_RC_EC;
}
static bool pcie_capability_reg_implemented(struct pci_dev *dev, int pos)
{
if (!pci_is_pcie(dev))
return false;
switch (pos) {
case PCI_EXP_FLAGS_TYPE:
return true;
case PCI_EXP_DEVCAP:
case PCI_EXP_DEVCTL:
case PCI_EXP_DEVSTA:
return pcie_cap_has_devctl(dev);
case PCI_EXP_LNKCAP:
case PCI_EXP_LNKCTL:
case PCI_EXP_LNKSTA:
return pcie_cap_has_lnkctl(dev);
case PCI_EXP_SLTCAP:
case PCI_EXP_SLTCTL:
case PCI_EXP_SLTSTA:
return pcie_cap_has_sltctl(dev);
case PCI_EXP_RTCTL:
case PCI_EXP_RTCAP:
case PCI_EXP_RTSTA:
return pcie_cap_has_rtctl(dev);
case PCI_EXP_DEVCAP2:
case PCI_EXP_DEVCTL2:
case PCI_EXP_LNKCAP2:
case PCI_EXP_LNKCTL2:
case PCI_EXP_LNKSTA2:
return pcie_cap_version(dev) > 1;
default:
return false;
}
}
/*
* Note that these accessor functions are only for the "PCI Express
* Capability" (see PCIe spec r3.0, sec 7.8). They do not apply to the
* other "PCI Express Extended Capabilities" (AER, VC, ACS, MFVC, etc.)
*/
int pcie_capability_read_word(struct pci_dev *dev, int pos, u16 *val)
{
int ret;
*val = 0;
if (pos & 1)
return -EINVAL;
if (pcie_capability_reg_implemented(dev, pos)) {
ret = pci_read_config_word(dev, pci_pcie_cap(dev) + pos, val);
/*
* Reset *val to 0 if pci_read_config_word() fails, it may
* have been written as 0xFFFF if hardware error happens
* during pci_read_config_word().
*/
if (ret)
*val = 0;
return ret;
}
/*
* For Functions that do not implement the Slot Capabilities,
* Slot Status, and Slot Control registers, these spaces must
* be hardwired to 0b, with the exception of the Presence Detect
* State bit in the Slot Status register of Downstream Ports,
* which must be hardwired to 1b. (PCIe Base Spec 3.0, sec 7.8)
*/
if (pci_is_pcie(dev) && pos == PCI_EXP_SLTSTA &&
pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) {
*val = PCI_EXP_SLTSTA_PDS;
}
return 0;
}
EXPORT_SYMBOL(pcie_capability_read_word);
int pcie_capability_read_dword(struct pci_dev *dev, int pos, u32 *val)
{
int ret;
*val = 0;
if (pos & 3)
return -EINVAL;
if (pcie_capability_reg_implemented(dev, pos)) {
ret = pci_read_config_dword(dev, pci_pcie_cap(dev) + pos, val);
/*
* Reset *val to 0 if pci_read_config_dword() fails, it may
* have been written as 0xFFFFFFFF if hardware error happens
* during pci_read_config_dword().
*/
if (ret)
*val = 0;
return ret;
}
if (pci_is_pcie(dev) && pos == PCI_EXP_SLTCTL &&
pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) {
*val = PCI_EXP_SLTSTA_PDS;
}
return 0;
}
EXPORT_SYMBOL(pcie_capability_read_dword);
int pcie_capability_write_word(struct pci_dev *dev, int pos, u16 val)
{
if (pos & 1)
return -EINVAL;
if (!pcie_capability_reg_implemented(dev, pos))
return 0;
return pci_write_config_word(dev, pci_pcie_cap(dev) + pos, val);
}
EXPORT_SYMBOL(pcie_capability_write_word);
int pcie_capability_write_dword(struct pci_dev *dev, int pos, u32 val)
{
if (pos & 3)
return -EINVAL;
if (!pcie_capability_reg_implemented(dev, pos))
return 0;
return pci_write_config_dword(dev, pci_pcie_cap(dev) + pos, val);
}
EXPORT_SYMBOL(pcie_capability_write_dword);
int pcie_capability_clear_and_set_word(struct pci_dev *dev, int pos,
u16 clear, u16 set)
{
int ret;
u16 val;
ret = pcie_capability_read_word(dev, pos, &val);
if (!ret) {
val &= ~clear;
val |= set;
ret = pcie_capability_write_word(dev, pos, val);
}
return ret;
}
EXPORT_SYMBOL(pcie_capability_clear_and_set_word);
int pcie_capability_clear_and_set_dword(struct pci_dev *dev, int pos,
u32 clear, u32 set)
{
int ret;
u32 val;
ret = pcie_capability_read_dword(dev, pos, &val);
if (!ret) {
val &= ~clear;
val |= set;
ret = pcie_capability_write_dword(dev, pos, val);
}
return ret;
}
EXPORT_SYMBOL(pcie_capability_clear_and_set_dword);
+33
View File
@@ -0,0 +1,33 @@
#include <linux/spinlock.h>
#include <linux/module.h>
#if !((LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)) && (defined(CONFIG_UML) || defined(CONFIG_X86))) && !((LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33)) && defined(CONFIG_ARM) && !defined(CONFIG_GENERIC_ATOMIC64))
static DEFINE_SPINLOCK(lock);
long long atomic64_read(const atomic64_t *v)
{
unsigned long flags;
long long val;
spin_lock_irqsave(&lock, flags);
val = v->counter;
spin_unlock_irqrestore(&lock, flags);
return val;
}
EXPORT_SYMBOL_GPL(atomic64_read);
long long atomic64_add_return(long long a, atomic64_t *v)
{
unsigned long flags;
long long val;
spin_lock_irqsave(&lock, flags);
val = v->counter += a;
spin_unlock_irqrestore(&lock, flags);
return val;
}
EXPORT_SYMBOL_GPL(atomic64_add_return);
#endif
+759
View File
@@ -0,0 +1,759 @@
/*
* firmware_class.c - Multi purpose firmware loading support
*
* Copyright (c) 2003 Manuel Estrada Sainz
*
* Please see Documentation/firmware_class/ for more information.
*
*/
#include <linux/capability.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/vmalloc.h>
#include <linux/interrupt.h>
#include <linux/bitops.h>
#include <linux/mutex.h>
#include <linux/kthread.h>
#include <linux/highmem.h>
#include <linux/firmware.h>
#include <linux/slab.h>
#define compat_firmware_to_dev(obj) container_of(obj, struct device, kobj)
MODULE_AUTHOR("Manuel Estrada Sainz");
MODULE_DESCRIPTION("Multi purpose firmware loading support");
MODULE_LICENSE("GPL");
/* Builtin firmware support */
//#ifdef CONFIG_FW_LOADER
#if 0
extern struct builtin_fw __start_builtin_fw[];
extern struct builtin_fw __end_builtin_fw[];
static bool fw_get_builtin_firmware(struct firmware *fw, const char *name)
{
struct builtin_fw *b_fw;
for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) {
if (strcmp(name, b_fw->name) == 0) {
fw->size = b_fw->size;
fw->data = b_fw->data;
return true;
}
}
return false;
}
static bool fw_is_builtin_firmware(const struct firmware *fw)
{
struct builtin_fw *b_fw;
for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++)
if (fw->data == b_fw->data)
return true;
return false;
}
#else /* Module case - no builtin firmware support */
static inline bool fw_get_builtin_firmware(struct firmware *fw, const char *name)
{
return false;
}
static inline bool fw_is_builtin_firmware(const struct firmware *fw)
{
return false;
}
#endif
enum {
FW_STATUS_LOADING,
FW_STATUS_DONE,
FW_STATUS_ABORT,
};
static int loading_timeout = 60; /* In seconds */
/* fw_lock could be moved to 'struct firmware_priv' but since it is just
* guarding for corner cases a global lock should be OK */
static DEFINE_MUTEX(fw_lock);
struct firmware_priv {
struct completion completion;
struct firmware *fw;
unsigned long status;
struct page **pages;
int nr_pages;
int page_array_size;
struct timer_list timeout;
struct device dev;
bool nowait;
char fw_id[];
};
static struct firmware_priv *to_firmware_priv(struct device *dev)
{
return container_of(dev, struct firmware_priv, dev);
}
static void fw_load_abort(struct firmware_priv *fw_priv)
{
set_bit(FW_STATUS_ABORT, &fw_priv->status);
wmb();
complete(&fw_priv->completion);
}
static ssize_t firmware_timeout_show(struct class *class,
char *buf)
{
return sprintf(buf, "%d\n", loading_timeout);
}
/**
* firmware_timeout_store - set number of seconds to wait for firmware
* @class: device class pointer
* @buf: buffer to scan for timeout value
* @count: number of bytes in @buf
*
* Sets the number of seconds to wait for the firmware. Once
* this expires an error will be returned to the driver and no
* firmware will be provided.
*
* Note: zero means 'wait forever'.
**/
static ssize_t firmware_timeout_store(struct class *class,
const char *buf, size_t count)
{
loading_timeout = simple_strtol(buf, NULL, 10);
if (loading_timeout < 0)
loading_timeout = 0;
return count;
}
static struct class_attribute firmware_class_attrs[] = {
__ATTR(timeout, S_IWUSR | S_IRUGO,
firmware_timeout_show, firmware_timeout_store),
__ATTR_NULL
};
static void fw_dev_release(struct device *dev)
{
struct firmware_priv *fw_priv = to_firmware_priv(dev);
int i;
for (i = 0; i < fw_priv->nr_pages; i++)
__free_page(fw_priv->pages[i]);
kfree(fw_priv->pages);
kfree(fw_priv);
module_put(THIS_MODULE);
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24))
static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct firmware_priv *fw_priv = to_firmware_priv(dev);
if (add_uevent_var(env, "FIRMWARE=%s", fw_priv->fw_id))
return -ENOMEM;
if (add_uevent_var(env, "TIMEOUT=%i", loading_timeout))
return -ENOMEM;
if (add_uevent_var(env, "ASYNC=%d", fw_priv->nowait))
return -ENOMEM;
return 0;
}
#else
static int firmware_uevent(struct device *dev, char **envp,
int num_envp, char *buf, int size)
{
struct firmware_priv *fw_priv = to_firmware_priv(dev);
int error, len = 0, i = 0;
error = add_uevent_var(envp, num_envp, &i,
buf, size, &len,
"FIRMWARE=%s", fw_priv->fw_id);
if (error)
goto exit;
error = add_uevent_var(envp, num_envp, &i,
buf, size, &len,
"TIMEOUT=%i", loading_timeout);
if (error)
goto exit;
error = add_uevent_var(envp, num_envp, &i,
buf, size, &len,
"ASYNC=%i", fw_priv->nowait);
if (error)
goto exit;
return 0;
exit:
envp[i] = NULL;
return error;
}
#endif
static struct class firmware_class = {
.name = "compat_firmware",
.class_attrs = firmware_class_attrs,
.dev_uevent = firmware_uevent,
.dev_release = fw_dev_release,
};
static ssize_t firmware_loading_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct firmware_priv *fw_priv = to_firmware_priv(dev);
int loading = test_bit(FW_STATUS_LOADING, &fw_priv->status);
return sprintf(buf, "%d\n", loading);
}
static void firmware_free_data(const struct firmware *fw)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
int i;
vunmap(fw->data);
if (fw->pages) {
for (i = 0; i < PFN_UP(fw->size); i++)
__free_page(fw->pages[i]);
kfree(fw->pages);
}
#else
vunmap(fw->data);
#endif
}
/* Some architectures don't have PAGE_KERNEL_RO */
#ifndef PAGE_KERNEL_RO
#define PAGE_KERNEL_RO PAGE_KERNEL
#endif
/**
* firmware_loading_store - set value in the 'loading' control file
* @dev: device pointer
* @buf: buffer to scan for loading control value
* @count: number of bytes in @buf
*
* The relevant values are:
*
* 1: Start a load, discarding any previous partial load.
* 0: Conclude the load and hand the data to the driver code.
* -1: Conclude the load with an error and discard any written data.
**/
static ssize_t firmware_loading_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct firmware_priv *fw_priv = to_firmware_priv(dev);
int loading = simple_strtol(buf, NULL, 10);
int i;
switch (loading) {
case 1:
mutex_lock(&fw_lock);
if (!fw_priv->fw) {
mutex_unlock(&fw_lock);
break;
}
firmware_free_data(fw_priv->fw);
memset(fw_priv->fw, 0, sizeof(struct firmware));
/* If the pages are not owned by 'struct firmware' */
for (i = 0; i < fw_priv->nr_pages; i++)
__free_page(fw_priv->pages[i]);
kfree(fw_priv->pages);
fw_priv->pages = NULL;
fw_priv->page_array_size = 0;
fw_priv->nr_pages = 0;
set_bit(FW_STATUS_LOADING, &fw_priv->status);
mutex_unlock(&fw_lock);
break;
case 0:
if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) {
vunmap(fw_priv->fw->data);
fw_priv->fw->data = vmap(fw_priv->pages,
fw_priv->nr_pages,
0, PAGE_KERNEL_RO);
if (!fw_priv->fw->data) {
dev_err(dev, "%s: vmap() failed\n", __func__);
goto err;
}
/* Pages are now owned by 'struct firmware' */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
fw_priv->fw->pages = fw_priv->pages;
fw_priv->pages = NULL;
#endif
fw_priv->page_array_size = 0;
fw_priv->nr_pages = 0;
complete(&fw_priv->completion);
clear_bit(FW_STATUS_LOADING, &fw_priv->status);
break;
}
/* fallthrough */
default:
dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
/* fallthrough */
case -1:
err:
fw_load_abort(fw_priv);
break;
}
return count;
}
static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store);
#if defined(CONFIG_COMPAT_FIRMWARE_DATA_RW_NEEDS_FILP)
static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buffer, loff_t offset, size_t count)
#else
static ssize_t firmware_data_read(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buffer, loff_t offset, size_t count)
#endif
{
struct device *dev = compat_firmware_to_dev(kobj);
struct firmware_priv *fw_priv = to_firmware_priv(dev);
struct firmware *fw;
ssize_t ret_count;
mutex_lock(&fw_lock);
fw = fw_priv->fw;
if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) {
ret_count = -ENODEV;
goto out;
}
if (offset > fw->size) {
ret_count = 0;
goto out;
}
if (count > fw->size - offset)
count = fw->size - offset;
ret_count = count;
while (count) {
void *page_data;
int page_nr = offset >> PAGE_SHIFT;
int page_ofs = offset & (PAGE_SIZE-1);
int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
page_data = kmap(fw_priv->pages[page_nr]);
memcpy(buffer, page_data + page_ofs, page_cnt);
kunmap(fw_priv->pages[page_nr]);
buffer += page_cnt;
offset += page_cnt;
count -= page_cnt;
}
out:
mutex_unlock(&fw_lock);
return ret_count;
}
static int fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
{
int pages_needed = ALIGN(min_size, PAGE_SIZE) >> PAGE_SHIFT;
/* If the array of pages is too small, grow it... */
if (fw_priv->page_array_size < pages_needed) {
int new_array_size = max(pages_needed,
fw_priv->page_array_size * 2);
struct page **new_pages;
new_pages = kmalloc(new_array_size * sizeof(void *),
GFP_KERNEL);
if (!new_pages) {
fw_load_abort(fw_priv);
return -ENOMEM;
}
memcpy(new_pages, fw_priv->pages,
fw_priv->page_array_size * sizeof(void *));
memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) *
(new_array_size - fw_priv->page_array_size));
kfree(fw_priv->pages);
fw_priv->pages = new_pages;
fw_priv->page_array_size = new_array_size;
}
while (fw_priv->nr_pages < pages_needed) {
fw_priv->pages[fw_priv->nr_pages] =
alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
if (!fw_priv->pages[fw_priv->nr_pages]) {
fw_load_abort(fw_priv);
return -ENOMEM;
}
fw_priv->nr_pages++;
}
return 0;
}
/**
* firmware_data_write - write method for firmware
* @kobj: kobject for the device
* @bin_attr: bin_attr structure
* @buffer: buffer being written
* @offset: buffer offset for write in total data store area
* @count: buffer size
*
* Data written to the 'data' attribute will be later handed to
* the driver as a firmware image.
**/
#if defined(CONFIG_COMPAT_FIRMWARE_DATA_RW_NEEDS_FILP)
static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buffer, loff_t offset, size_t count)
#else
static ssize_t firmware_data_write(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buffer, loff_t offset, size_t count)
#endif
{
struct device *dev = compat_firmware_to_dev(kobj);
struct firmware_priv *fw_priv = to_firmware_priv(dev);
struct firmware *fw;
ssize_t retval;
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
mutex_lock(&fw_lock);
fw = fw_priv->fw;
if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) {
retval = -ENODEV;
goto out;
}
retval = fw_realloc_buffer(fw_priv, offset + count);
if (retval)
goto out;
retval = count;
while (count) {
void *page_data;
int page_nr = offset >> PAGE_SHIFT;
int page_ofs = offset & (PAGE_SIZE - 1);
int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
page_data = kmap(fw_priv->pages[page_nr]);
memcpy(page_data + page_ofs, buffer, page_cnt);
kunmap(fw_priv->pages[page_nr]);
buffer += page_cnt;
offset += page_cnt;
count -= page_cnt;
}
fw->size = max_t(size_t, offset, fw->size);
out:
mutex_unlock(&fw_lock);
return retval;
}
static struct bin_attribute firmware_attr_data = {
.attr = { .name = "data", .mode = 0644 },
.size = 0,
.read = firmware_data_read,
.write = firmware_data_write,
};
static void firmware_class_timeout(u_long data)
{
struct firmware_priv *fw_priv = (struct firmware_priv *) data;
fw_load_abort(fw_priv);
}
static struct firmware_priv *
fw_create_instance(struct firmware *firmware, const char *fw_name,
struct device *device, bool uevent, bool nowait)
{
struct firmware_priv *fw_priv;
struct device *f_dev;
int error;
fw_priv = kzalloc(sizeof(*fw_priv) + strlen(fw_name) + 1 , GFP_KERNEL);
if (!fw_priv) {
dev_err(device, "%s: kmalloc failed\n", __func__);
error = -ENOMEM;
goto err_out;
}
fw_priv->fw = firmware;
fw_priv->nowait = nowait;
strcpy(fw_priv->fw_id, fw_name);
init_completion(&fw_priv->completion);
setup_timer(&fw_priv->timeout,
firmware_class_timeout, (u_long) fw_priv);
f_dev = &fw_priv->dev;
device_initialize(f_dev);
dev_set_name(f_dev, "%s", dev_name(device));
f_dev->parent = device;
f_dev->class = &firmware_class;
dev_set_uevent_suppress(f_dev, true);
/* Need to pin this module until class device is destroyed */
__module_get(THIS_MODULE);
error = device_add(f_dev);
if (error) {
dev_err(device, "%s: device_register failed\n", __func__);
goto err_put_dev;
}
error = device_create_bin_file(f_dev, &firmware_attr_data);
if (error) {
dev_err(device, "%s: sysfs_create_bin_file failed\n", __func__);
goto err_del_dev;
}
error = device_create_file(f_dev, &dev_attr_loading);
if (error) {
dev_err(device, "%s: device_create_file failed\n", __func__);
goto err_del_bin_attr;
}
if (uevent)
dev_set_uevent_suppress(f_dev, false);
return fw_priv;
err_del_bin_attr:
device_remove_bin_file(f_dev, &firmware_attr_data);
err_del_dev:
device_del(f_dev);
err_put_dev:
put_device(f_dev);
err_out:
return ERR_PTR(error);
}
static void fw_destroy_instance(struct firmware_priv *fw_priv)
{
struct device *f_dev = &fw_priv->dev;
device_remove_file(f_dev, &dev_attr_loading);
device_remove_bin_file(f_dev, &firmware_attr_data);
device_unregister(f_dev);
}
static int _request_firmware(const struct firmware **firmware_p,
const char *name, struct device *device,
bool uevent, bool nowait)
{
struct firmware_priv *fw_priv;
struct firmware *firmware;
int retval = 0;
if (!firmware_p)
return -EINVAL;
*firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
if (!firmware) {
dev_err(device, "%s: kmalloc(struct firmware) failed\n",
__func__);
retval = -ENOMEM;
goto out;
}
if (fw_get_builtin_firmware(firmware, name)) {
dev_dbg(device, "firmware: using built-in firmware %s\n", name);
return 0;
}
if (uevent)
dev_dbg(device, "firmware: requesting %s\n", name);
fw_priv = fw_create_instance(firmware, name, device, uevent, nowait);
if (IS_ERR(fw_priv)) {
retval = PTR_ERR(fw_priv);
goto out;
}
if (uevent) {
if (loading_timeout > 0)
mod_timer(&fw_priv->timeout,
round_jiffies_up(jiffies +
loading_timeout * HZ));
kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD);
}
wait_for_completion(&fw_priv->completion);
set_bit(FW_STATUS_DONE, &fw_priv->status);
del_timer_sync(&fw_priv->timeout);
mutex_lock(&fw_lock);
if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status))
retval = -ENOENT;
fw_priv->fw = NULL;
mutex_unlock(&fw_lock);
fw_destroy_instance(fw_priv);
out:
if (retval) {
release_firmware(firmware);
*firmware_p = NULL;
}
return retval;
}
/**
* request_firmware: - send firmware request and wait for it
* @firmware_p: pointer to firmware image
* @name: name of firmware file
* @device: device for which firmware is being loaded
*
* @firmware_p will be used to return a firmware image by the name
* of @name for device @device.
*
* Should be called from user context where sleeping is allowed.
*
* @name will be used as $FIRMWARE in the uevent environment and
* should be distinctive enough not to be confused with any other
* firmware image for this or any other device.
**/
int
compat_request_firmware(const struct firmware **firmware_p, const char *name,
struct device *device)
{
int uevent = 1;
return _request_firmware(firmware_p, name, device, uevent, false);
}
/**
* release_firmware: - release the resource associated with a firmware image
* @fw: firmware resource to release
**/
void compat_release_firmware(const struct firmware *fw)
{
if (fw) {
if (!fw_is_builtin_firmware(fw))
firmware_free_data(fw);
kfree(fw);
}
}
/* Async support */
struct firmware_work {
struct work_struct work;
struct module *module;
const char *name;
struct device *device;
void *context;
void (*cont)(const struct firmware *fw, void *context);
int uevent;
};
static int request_firmware_work_func(void *arg)
{
struct firmware_work *fw_work = arg;
const struct firmware *fw;
int ret;
if (!arg) {
WARN_ON(1);
return 0;
}
ret = _request_firmware(&fw, fw_work->name, fw_work->device,
fw_work->uevent, true);
fw_work->cont(fw, fw_work->context);
module_put(fw_work->module);
kfree(fw_work);
return ret;
}
/**
* request_firmware_nowait - asynchronous version of request_firmware
* @module: module requesting the firmware
* @uevent: sends uevent to copy the firmware image if this flag
* is non-zero else the firmware copy must be done manually.
* @name: name of firmware file
* @device: device for which firmware is being loaded
* @gfp: allocation flags
* @context: will be passed over to @cont, and
* @fw may be %NULL if firmware request fails.
* @cont: function will be called asynchronously when the firmware
* request is over.
*
* Asynchronous variant of request_firmware() for user contexts where
* it is not possible to sleep for long time. It can't be called
* in atomic contexts.
**/
int
compat_request_firmware_nowait(
struct module *module, int uevent,
const char *name, struct device *device, gfp_t gfp, void *context,
void (*cont)(const struct firmware *fw, void *context))
{
struct task_struct *task;
struct firmware_work *fw_work;
fw_work = kzalloc(sizeof (struct firmware_work), gfp);
if (!fw_work)
return -ENOMEM;
fw_work->module = module;
fw_work->name = name;
fw_work->device = device;
fw_work->context = context;
fw_work->cont = cont;
fw_work->uevent = uevent;
if (!try_module_get(module)) {
kfree(fw_work);
return -EFAULT;
}
task = kthread_run(request_firmware_work_func, fw_work,
"firmware/%s", name);
if (IS_ERR(task)) {
fw_work->cont(NULL, fw_work->context);
module_put(fw_work->module);
kfree(fw_work);
return PTR_ERR(task);
}
return 0;
}
static int __init firmware_class_init(void)
{
return class_register(&firmware_class);
}
static void __exit firmware_class_exit(void)
{
class_unregister(&firmware_class);
}
fs_initcall(firmware_class_init);
module_exit(firmware_class_exit);
EXPORT_SYMBOL_GPL(release_firmware);
EXPORT_SYMBOL_GPL(request_firmware);
EXPORT_SYMBOL_GPL(request_firmware_nowait);
+101
View File
@@ -0,0 +1,101 @@
/*
* Copyright (c) 2011 Broadcom Corporation
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/module.h>
#include <linux/cordic.h>
#define CORDIC_ANGLE_GEN 39797
#define CORDIC_PRECISION_SHIFT 16
#define CORDIC_NUM_ITER (CORDIC_PRECISION_SHIFT + 2)
#define FIXED(X) ((s32)((X) << CORDIC_PRECISION_SHIFT))
#define FLOAT(X) (((X) >= 0) \
? ((((X) >> (CORDIC_PRECISION_SHIFT - 1)) + 1) >> 1) \
: -((((-(X)) >> (CORDIC_PRECISION_SHIFT - 1)) + 1) >> 1))
static const s32 arctan_table[] = {
2949120,
1740967,
919879,
466945,
234379,
117304,
58666,
29335,
14668,
7334,
3667,
1833,
917,
458,
229,
115,
57,
29
};
/*
* cordic_calc_iq() - calculates the i/q coordinate for given angle
*
* theta: angle in degrees for which i/q coordinate is to be calculated
* coord: function output parameter holding the i/q coordinate
*/
struct cordic_iq cordic_calc_iq(s32 theta)
{
struct cordic_iq coord;
s32 angle, valtmp;
unsigned iter;
int signx = 1;
int signtheta;
coord.i = CORDIC_ANGLE_GEN;
coord.q = 0;
angle = 0;
theta = FIXED(theta);
signtheta = (theta < 0) ? -1 : 1;
theta = ((theta + FIXED(180) * signtheta) % FIXED(360)) -
FIXED(180) * signtheta;
if (FLOAT(theta) > 90) {
theta -= FIXED(180);
signx = -1;
} else if (FLOAT(theta) < -90) {
theta += FIXED(180);
signx = -1;
}
for (iter = 0; iter < CORDIC_NUM_ITER; iter++) {
if (theta > angle) {
valtmp = coord.i - (coord.q >> iter);
coord.q += (coord.i >> iter);
angle += arctan_table[iter];
} else {
valtmp = coord.i + (coord.q >> iter);
coord.q -= (coord.i >> iter);
angle -= arctan_table[iter];
}
coord.i = valtmp;
}
coord.i *= signx;
coord.q *= signx;
return coord;
}
EXPORT_SYMBOL_GPL(cordic_calc_iq);
MODULE_DESCRIPTION("Cordic functions");
MODULE_AUTHOR("Broadcom Corporation");
MODULE_LICENSE("Dual BSD/GPL");
+87
View File
@@ -0,0 +1,87 @@
/*
* Copyright (c) 2011 Broadcom Corporation
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#undef pr_fmt
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/crc8.h>
#include <linux/printk.h>
/*
* crc8_populate_msb - fill crc table for given polynomial in reverse bit order.
*
* table: table to be filled.
* polynomial: polynomial for which table is to be filled.
*/
void crc8_populate_msb(u8 table[CRC8_TABLE_SIZE], u8 polynomial)
{
int i, j;
const u8 msbit = 0x80;
u8 t = msbit;
table[0] = 0;
for (i = 1; i < CRC8_TABLE_SIZE; i *= 2) {
t = (t << 1) ^ (t & msbit ? polynomial : 0);
for (j = 0; j < i; j++)
table[i+j] = table[j] ^ t;
}
}
EXPORT_SYMBOL_GPL(crc8_populate_msb);
/*
* crc8_populate_lsb - fill crc table for given polynomial in regular bit order.
*
* table: table to be filled.
* polynomial: polynomial for which table is to be filled.
*/
void crc8_populate_lsb(u8 table[CRC8_TABLE_SIZE], u8 polynomial)
{
int i, j;
u8 t = 1;
table[0] = 0;
for (i = (CRC8_TABLE_SIZE >> 1); i; i >>= 1) {
t = (t >> 1) ^ (t & 1 ? polynomial : 0);
for (j = 0; j < CRC8_TABLE_SIZE; j += 2*i)
table[i+j] = table[j] ^ t;
}
}
EXPORT_SYMBOL_GPL(crc8_populate_lsb);
/*
* crc8 - calculate a crc8 over the given input data.
*
* table: crc table used for calculation.
* pdata: pointer to data buffer.
* nbytes: number of bytes in data buffer.
* crc: previous returned crc8 value.
*/
u8 crc8(const u8 table[CRC8_TABLE_SIZE], u8 *pdata, size_t nbytes, u8 crc)
{
/* loop over the buffer data */
while (nbytes-- > 0)
crc = table[(crc ^ *pdata++) & 0xff];
return crc;
}
EXPORT_SYMBOL_GPL(crc8);
MODULE_DESCRIPTION("CRC8 (by Williams, Ross N.) function");
MODULE_AUTHOR("Broadcom Corporation");
MODULE_LICENSE("Dual BSD/GPL");
+143
View File
@@ -0,0 +1,143 @@
#include <linux/skbuff.h>
#include <linux/export.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/if_vlan.h>
#include <net/ip.h>
#include <linux/if_tunnel.h>
#include <linux/if_pppox.h>
#include <linux/ppp_defs.h>
#include <net/flow_keys.h>
/* copy saddr & daddr, possibly using 64bit load/store
* Equivalent to : flow->src = iph->saddr;
* flow->dst = iph->daddr;
*/
static void iph_to_flow_copy_addrs(struct flow_keys *flow, const struct iphdr *iph)
{
BUILD_BUG_ON(offsetof(typeof(*flow), dst) !=
offsetof(typeof(*flow), src) + sizeof(flow->src));
memcpy(&flow->src, &iph->saddr, sizeof(flow->src) + sizeof(flow->dst));
}
bool skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow)
{
int poff, nhoff = skb_network_offset(skb);
u8 ip_proto;
__be16 proto = skb->protocol;
memset(flow, 0, sizeof(*flow));
again:
switch (proto) {
case __constant_htons(ETH_P_IP): {
const struct iphdr *iph;
struct iphdr _iph;
ip:
iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph);
if (!iph)
return false;
if (ip_is_fragment(iph))
ip_proto = 0;
else
ip_proto = iph->protocol;
iph_to_flow_copy_addrs(flow, iph);
nhoff += iph->ihl * 4;
break;
}
case __constant_htons(ETH_P_IPV6): {
const struct ipv6hdr *iph;
struct ipv6hdr _iph;
ipv6:
iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph);
if (!iph)
return false;
ip_proto = iph->nexthdr;
flow->src = iph->saddr.s6_addr32[3];
flow->dst = iph->daddr.s6_addr32[3];
nhoff += sizeof(struct ipv6hdr);
break;
}
case __constant_htons(ETH_P_8021Q): {
const struct vlan_hdr *vlan;
struct vlan_hdr _vlan;
vlan = skb_header_pointer(skb, nhoff, sizeof(_vlan), &_vlan);
if (!vlan)
return false;
proto = vlan->h_vlan_encapsulated_proto;
nhoff += sizeof(*vlan);
goto again;
}
case __constant_htons(ETH_P_PPP_SES): {
struct {
struct pppoe_hdr hdr;
__be16 proto;
} *hdr, _hdr;
hdr = skb_header_pointer(skb, nhoff, sizeof(_hdr), &_hdr);
if (!hdr)
return false;
proto = hdr->proto;
nhoff += PPPOE_SES_HLEN;
switch (proto) {
case __constant_htons(PPP_IP):
goto ip;
case __constant_htons(PPP_IPV6):
goto ipv6;
default:
return false;
}
}
default:
return false;
}
switch (ip_proto) {
case IPPROTO_GRE: {
struct gre_hdr {
__be16 flags;
__be16 proto;
} *hdr, _hdr;
hdr = skb_header_pointer(skb, nhoff, sizeof(_hdr), &_hdr);
if (!hdr)
return false;
/*
* Only look inside GRE if version zero and no
* routing
*/
if (!(hdr->flags & (GRE_VERSION|GRE_ROUTING))) {
proto = hdr->proto;
nhoff += 4;
if (hdr->flags & GRE_CSUM)
nhoff += 4;
if (hdr->flags & GRE_KEY)
nhoff += 4;
if (hdr->flags & GRE_SEQ)
nhoff += 4;
goto again;
}
break;
}
case IPPROTO_IPIP:
goto again;
default:
break;
}
flow->ip_proto = ip_proto;
poff = proto_ports_offset(ip_proto);
if (poff >= 0) {
__be32 *ports, _ports;
nhoff += poff;
ports = skb_header_pointer(skb, nhoff, sizeof(_ports), &_ports);
if (ports)
flow->ports = *ports;
}
return true;
}
+608
View File
@@ -0,0 +1,608 @@
/*
* A generic kernel FIFO implementation
*
* Copyright (C) 2009/2010 Stefani Seibold <stefani@seibold.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/log2.h>
#include <linux/uaccess.h>
#include <linux/kfifo.h>
/*
* internal helper to calculate the unused elements in a fifo
*/
static inline unsigned int kfifo_unused(struct __kfifo *fifo)
{
return (fifo->mask + 1) - (fifo->in - fifo->out);
}
int __kfifo_alloc(struct __kfifo *fifo, unsigned int size,
size_t esize, gfp_t gfp_mask)
{
/*
* round down to the next power of 2, since our 'let the indices
* wrap' technique works only in this case.
*/
if (!is_power_of_2(size))
size = rounddown_pow_of_two(size);
fifo->in = 0;
fifo->out = 0;
fifo->esize = esize;
if (size < 2) {
fifo->data = NULL;
fifo->mask = 0;
return -EINVAL;
}
fifo->data = kmalloc(size * esize, gfp_mask);
if (!fifo->data) {
fifo->mask = 0;
return -ENOMEM;
}
fifo->mask = size - 1;
return 0;
}
EXPORT_SYMBOL_GPL(__kfifo_alloc);
void __kfifo_free(struct __kfifo *fifo)
{
kfree(fifo->data);
fifo->in = 0;
fifo->out = 0;
fifo->esize = 0;
fifo->data = NULL;
fifo->mask = 0;
}
EXPORT_SYMBOL_GPL(__kfifo_free);
int __kfifo_init(struct __kfifo *fifo, void *buffer,
unsigned int size, size_t esize)
{
size /= esize;
if (!is_power_of_2(size))
size = rounddown_pow_of_two(size);
fifo->in = 0;
fifo->out = 0;
fifo->esize = esize;
fifo->data = buffer;
if (size < 2) {
fifo->mask = 0;
return -EINVAL;
}
fifo->mask = size - 1;
return 0;
}
EXPORT_SYMBOL_GPL(__kfifo_init);
static void kfifo_copy_in(struct __kfifo *fifo, const void *src,
unsigned int len, unsigned int off)
{
unsigned int size = fifo->mask + 1;
unsigned int esize = fifo->esize;
unsigned int l;
off &= fifo->mask;
if (esize != 1) {
off *= esize;
size *= esize;
len *= esize;
}
l = min(len, size - off);
memcpy(fifo->data + off, src, l);
memcpy(fifo->data, src + l, len - l);
/*
* make sure that the data in the fifo is up to date before
* incrementing the fifo->in index counter
*/
smp_wmb();
}
unsigned int __kfifo_in(struct __kfifo *fifo,
const void *buf, unsigned int len)
{
unsigned int l;
l = kfifo_unused(fifo);
if (len > l)
len = l;
kfifo_copy_in(fifo, buf, len, fifo->in);
fifo->in += len;
return len;
}
EXPORT_SYMBOL_GPL(__kfifo_in);
static void kfifo_copy_out(struct __kfifo *fifo, void *dst,
unsigned int len, unsigned int off)
{
unsigned int size = fifo->mask + 1;
unsigned int esize = fifo->esize;
unsigned int l;
off &= fifo->mask;
if (esize != 1) {
off *= esize;
size *= esize;
len *= esize;
}
l = min(len, size - off);
memcpy(dst, fifo->data + off, l);
memcpy(dst + l, fifo->data, len - l);
/*
* make sure that the data is copied before
* incrementing the fifo->out index counter
*/
smp_wmb();
}
unsigned int __kfifo_out_peek(struct __kfifo *fifo,
void *buf, unsigned int len)
{
unsigned int l;
l = fifo->in - fifo->out;
if (len > l)
len = l;
kfifo_copy_out(fifo, buf, len, fifo->out);
return len;
}
EXPORT_SYMBOL_GPL(__kfifo_out_peek);
unsigned int __kfifo_out(struct __kfifo *fifo,
void *buf, unsigned int len)
{
len = __kfifo_out_peek(fifo, buf, len);
fifo->out += len;
return len;
}
EXPORT_SYMBOL_GPL(__kfifo_out);
static unsigned long kfifo_copy_from_user(struct __kfifo *fifo,
const void __user *from, unsigned int len, unsigned int off,
unsigned int *copied)
{
unsigned int size = fifo->mask + 1;
unsigned int esize = fifo->esize;
unsigned int l;
unsigned long ret;
off &= fifo->mask;
if (esize != 1) {
off *= esize;
size *= esize;
len *= esize;
}
l = min(len, size - off);
ret = copy_from_user(fifo->data + off, from, l);
if (unlikely(ret))
ret = DIV_ROUND_UP(ret + len - l, esize);
else {
ret = copy_from_user(fifo->data, from + l, len - l);
if (unlikely(ret))
ret = DIV_ROUND_UP(ret, esize);
}
/*
* make sure that the data in the fifo is up to date before
* incrementing the fifo->in index counter
*/
smp_wmb();
*copied = len - ret;
/* return the number of elements which are not copied */
return ret;
}
int __kfifo_from_user(struct __kfifo *fifo, const void __user *from,
unsigned long len, unsigned int *copied)
{
unsigned int l;
unsigned long ret;
unsigned int esize = fifo->esize;
int err;
if (esize != 1)
len /= esize;
l = kfifo_unused(fifo);
if (len > l)
len = l;
ret = kfifo_copy_from_user(fifo, from, len, fifo->in, copied);
if (unlikely(ret)) {
len -= ret;
err = -EFAULT;
} else
err = 0;
fifo->in += len;
return err;
}
EXPORT_SYMBOL_GPL(__kfifo_from_user);
static unsigned long kfifo_copy_to_user(struct __kfifo *fifo, void __user *to,
unsigned int len, unsigned int off, unsigned int *copied)
{
unsigned int l;
unsigned long ret;
unsigned int size = fifo->mask + 1;
unsigned int esize = fifo->esize;
off &= fifo->mask;
if (esize != 1) {
off *= esize;
size *= esize;
len *= esize;
}
l = min(len, size - off);
ret = copy_to_user(to, fifo->data + off, l);
if (unlikely(ret))
ret = DIV_ROUND_UP(ret + len - l, esize);
else {
ret = copy_to_user(to + l, fifo->data, len - l);
if (unlikely(ret))
ret = DIV_ROUND_UP(ret, esize);
}
/*
* make sure that the data is copied before
* incrementing the fifo->out index counter
*/
smp_wmb();
*copied = len - ret;
/* return the number of elements which are not copied */
return ret;
}
int __kfifo_to_user(struct __kfifo *fifo, void __user *to,
unsigned long len, unsigned int *copied)
{
unsigned int l;
unsigned long ret;
unsigned int esize = fifo->esize;
int err;
if (esize != 1)
len /= esize;
l = fifo->in - fifo->out;
if (len > l)
len = l;
ret = kfifo_copy_to_user(fifo, to, len, fifo->out, copied);
if (unlikely(ret)) {
len -= ret;
err = -EFAULT;
} else
err = 0;
fifo->out += len;
return err;
}
EXPORT_SYMBOL_GPL(__kfifo_to_user);
static int setup_sgl_buf(struct scatterlist *sgl, void *buf,
int nents, unsigned int len)
{
int n;
unsigned int l;
unsigned int off;
struct page *page;
if (!nents)
return 0;
if (!len)
return 0;
n = 0;
page = virt_to_page(buf);
off = offset_in_page(buf);
l = 0;
while (len >= l + PAGE_SIZE - off) {
struct page *npage;
l += PAGE_SIZE;
buf += PAGE_SIZE;
npage = virt_to_page(buf);
if (page_to_phys(page) != page_to_phys(npage) - l) {
sg_set_page(sgl, page, l - off, off);
sgl = sg_next(sgl);
if (++n == nents || sgl == NULL)
return n;
page = npage;
len -= l - off;
l = off = 0;
}
}
sg_set_page(sgl, page, len, off);
return n + 1;
}
static unsigned int setup_sgl(struct __kfifo *fifo, struct scatterlist *sgl,
int nents, unsigned int len, unsigned int off)
{
unsigned int size = fifo->mask + 1;
unsigned int esize = fifo->esize;
unsigned int l;
unsigned int n;
off &= fifo->mask;
if (esize != 1) {
off *= esize;
size *= esize;
len *= esize;
}
l = min(len, size - off);
n = setup_sgl_buf(sgl, fifo->data + off, nents, l);
n += setup_sgl_buf(sgl + n, fifo->data, nents - n, len - l);
return n;
}
unsigned int __kfifo_dma_in_prepare(struct __kfifo *fifo,
struct scatterlist *sgl, int nents, unsigned int len)
{
unsigned int l;
l = kfifo_unused(fifo);
if (len > l)
len = l;
return setup_sgl(fifo, sgl, nents, len, fifo->in);
}
EXPORT_SYMBOL_GPL(__kfifo_dma_in_prepare);
unsigned int __kfifo_dma_out_prepare(struct __kfifo *fifo,
struct scatterlist *sgl, int nents, unsigned int len)
{
unsigned int l;
l = fifo->in - fifo->out;
if (len > l)
len = l;
return setup_sgl(fifo, sgl, nents, len, fifo->out);
}
EXPORT_SYMBOL_GPL(__kfifo_dma_out_prepare);
unsigned int __kfifo_max_r(unsigned int len, size_t recsize)
{
unsigned int max = (1 << (recsize << 3)) - 1;
if (len > max)
return max;
return len;
}
#define __KFIFO_PEEK(data, out, mask) \
((data)[(out) & (mask)])
/*
* __kfifo_peek_n internal helper function for determinate the length of
* the next record in the fifo
*/
static unsigned int __kfifo_peek_n(struct __kfifo *fifo, size_t recsize)
{
unsigned int l;
unsigned int mask = fifo->mask;
unsigned char *data = fifo->data;
l = __KFIFO_PEEK(data, fifo->out, mask);
if (--recsize)
l |= __KFIFO_PEEK(data, fifo->out + 1, mask) << 8;
return l;
}
#define __KFIFO_POKE(data, in, mask, val) \
( \
(data)[(in) & (mask)] = (unsigned char)(val) \
)
/*
* __kfifo_poke_n internal helper function for storeing the length of
* the record into the fifo
*/
static void __kfifo_poke_n(struct __kfifo *fifo, unsigned int n, size_t recsize)
{
unsigned int mask = fifo->mask;
unsigned char *data = fifo->data;
__KFIFO_POKE(data, fifo->in, mask, n);
if (recsize > 1)
__KFIFO_POKE(data, fifo->in + 1, mask, n >> 8);
}
unsigned int __kfifo_len_r(struct __kfifo *fifo, size_t recsize)
{
return __kfifo_peek_n(fifo, recsize);
}
EXPORT_SYMBOL_GPL(__kfifo_len_r);
unsigned int __kfifo_in_r(struct __kfifo *fifo, const void *buf,
unsigned int len, size_t recsize)
{
if (len + recsize > kfifo_unused(fifo))
return 0;
__kfifo_poke_n(fifo, len, recsize);
kfifo_copy_in(fifo, buf, len, fifo->in + recsize);
fifo->in += len + recsize;
return len;
}
EXPORT_SYMBOL_GPL(__kfifo_in_r);
static unsigned int kfifo_out_copy_r(struct __kfifo *fifo,
void *buf, unsigned int len, size_t recsize, unsigned int *n)
{
*n = __kfifo_peek_n(fifo, recsize);
if (len > *n)
len = *n;
kfifo_copy_out(fifo, buf, len, fifo->out + recsize);
return len;
}
unsigned int __kfifo_out_peek_r(struct __kfifo *fifo, void *buf,
unsigned int len, size_t recsize)
{
unsigned int n;
if (fifo->in == fifo->out)
return 0;
return kfifo_out_copy_r(fifo, buf, len, recsize, &n);
}
EXPORT_SYMBOL_GPL(__kfifo_out_peek_r);
unsigned int __kfifo_out_r(struct __kfifo *fifo, void *buf,
unsigned int len, size_t recsize)
{
unsigned int n;
if (fifo->in == fifo->out)
return 0;
len = kfifo_out_copy_r(fifo, buf, len, recsize, &n);
fifo->out += n + recsize;
return len;
}
EXPORT_SYMBOL_GPL(__kfifo_out_r);
void __kfifo_skip_r(struct __kfifo *fifo, size_t recsize)
{
unsigned int n;
n = __kfifo_peek_n(fifo, recsize);
fifo->out += n + recsize;
}
EXPORT_SYMBOL_GPL(__kfifo_skip_r);
int __kfifo_from_user_r(struct __kfifo *fifo, const void __user *from,
unsigned long len, unsigned int *copied, size_t recsize)
{
unsigned long ret;
len = __kfifo_max_r(len, recsize);
if (len + recsize > kfifo_unused(fifo)) {
*copied = 0;
return 0;
}
__kfifo_poke_n(fifo, len, recsize);
ret = kfifo_copy_from_user(fifo, from, len, fifo->in + recsize, copied);
if (unlikely(ret)) {
*copied = 0;
return -EFAULT;
}
fifo->in += len + recsize;
return 0;
}
EXPORT_SYMBOL_GPL(__kfifo_from_user_r);
int __kfifo_to_user_r(struct __kfifo *fifo, void __user *to,
unsigned long len, unsigned int *copied, size_t recsize)
{
unsigned long ret;
unsigned int n;
if (fifo->in == fifo->out) {
*copied = 0;
return 0;
}
n = __kfifo_peek_n(fifo, recsize);
if (len > n)
len = n;
ret = kfifo_copy_to_user(fifo, to, len, fifo->out + recsize, copied);
if (unlikely(ret)) {
*copied = 0;
return -EFAULT;
}
fifo->out += n + recsize;
return 0;
}
EXPORT_SYMBOL_GPL(__kfifo_to_user_r);
unsigned int __kfifo_dma_in_prepare_r(struct __kfifo *fifo,
struct scatterlist *sgl, int nents, unsigned int len, size_t recsize)
{
if (!nents)
BUG();
len = __kfifo_max_r(len, recsize);
if (len + recsize > kfifo_unused(fifo))
return 0;
return setup_sgl(fifo, sgl, nents, len, fifo->in + recsize);
}
EXPORT_SYMBOL_GPL(__kfifo_dma_in_prepare_r);
void __kfifo_dma_in_finish_r(struct __kfifo *fifo,
unsigned int len, size_t recsize)
{
len = __kfifo_max_r(len, recsize);
__kfifo_poke_n(fifo, len, recsize);
fifo->in += len + recsize;
}
EXPORT_SYMBOL_GPL(__kfifo_dma_in_finish_r);
unsigned int __kfifo_dma_out_prepare_r(struct __kfifo *fifo,
struct scatterlist *sgl, int nents, unsigned int len, size_t recsize)
{
if (!nents)
BUG();
len = __kfifo_max_r(len, recsize);
if (len + recsize > fifo->in - fifo->out)
return 0;
return setup_sgl(fifo, sgl, nents, len, fifo->out + recsize);
}
EXPORT_SYMBOL_GPL(__kfifo_dma_out_prepare_r);
void __kfifo_dma_out_finish_r(struct __kfifo *fifo, size_t recsize)
{
unsigned int len;
len = __kfifo_peek_n(fifo, recsize);
fifo->out += len + recsize;
}
EXPORT_SYMBOL_GPL(__kfifo_dma_out_finish_r);
+236
View File
@@ -0,0 +1,236 @@
/*
* Convert integer string representation to an integer.
* If an integer doesn't fit into specified type, -E is returned.
*
* Integer starts with optional sign.
* kstrtou*() functions do not accept sign "-".
*
* Radix 0 means autodetection: leading "0x" implies radix 16,
* leading "0" implies radix 8, otherwise radix is 10.
* Autodetection hints work after optional sign, but not before.
*
* If -E is returned, result is not touched.
*/
#include <linux/kernel.h>
/*
* kstrto* was included in kernel 2.6.38.4 and causes conflicts with the
* version included in compat-drivers. We use strict_strtol to check if
* kstrto* is already available.
*/
#ifndef strict_strtol
#include <linux/ctype.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/types.h>
static inline char _tolower(const char c)
{
return c | 0x20;
}
static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res)
{
unsigned long long acc;
int ok;
if (base == 0) {
if (s[0] == '0') {
if (_tolower(s[1]) == 'x' && isxdigit(s[2]))
base = 16;
else
base = 8;
} else
base = 10;
}
if (base == 16 && s[0] == '0' && _tolower(s[1]) == 'x')
s += 2;
acc = 0;
ok = 0;
while (*s) {
unsigned int val;
if ('0' <= *s && *s <= '9')
val = *s - '0';
else if ('a' <= _tolower(*s) && _tolower(*s) <= 'f')
val = _tolower(*s) - 'a' + 10;
else if (*s == '\n') {
if (*(s + 1) == '\0')
break;
else
return -EINVAL;
} else
return -EINVAL;
if (val >= base)
return -EINVAL;
if (acc > div_u64(ULLONG_MAX - val, base))
return -ERANGE;
acc = acc * base + val;
ok = 1;
s++;
}
if (!ok)
return -EINVAL;
*res = acc;
return 0;
}
int kstrtoull(const char *s, unsigned int base, unsigned long long *res)
{
if (s[0] == '+')
s++;
return _kstrtoull(s, base, res);
}
EXPORT_SYMBOL_GPL(kstrtoull);
int kstrtoll(const char *s, unsigned int base, long long *res)
{
unsigned long long tmp;
int rv;
if (s[0] == '-') {
rv = _kstrtoull(s + 1, base, &tmp);
if (rv < 0)
return rv;
if ((long long)(-tmp) >= 0)
return -ERANGE;
*res = -tmp;
} else {
rv = kstrtoull(s, base, &tmp);
if (rv < 0)
return rv;
if ((long long)tmp < 0)
return -ERANGE;
*res = tmp;
}
return 0;
}
EXPORT_SYMBOL_GPL(kstrtoll);
/* Internal, do not use. */
int _kstrtoul(const char *s, unsigned int base, unsigned long *res)
{
unsigned long long tmp;
int rv;
rv = kstrtoull(s, base, &tmp);
if (rv < 0)
return rv;
if (tmp != (unsigned long long)(unsigned long)tmp)
return -ERANGE;
*res = tmp;
return 0;
}
EXPORT_SYMBOL_GPL(_kstrtoul);
/* Internal, do not use. */
int _kstrtol(const char *s, unsigned int base, long *res)
{
long long tmp;
int rv;
rv = kstrtoll(s, base, &tmp);
if (rv < 0)
return rv;
if (tmp != (long long)(long)tmp)
return -ERANGE;
*res = tmp;
return 0;
}
EXPORT_SYMBOL_GPL(_kstrtol);
int kstrtouint(const char *s, unsigned int base, unsigned int *res)
{
unsigned long long tmp;
int rv;
rv = kstrtoull(s, base, &tmp);
if (rv < 0)
return rv;
if (tmp != (unsigned long long)(unsigned int)tmp)
return -ERANGE;
*res = tmp;
return 0;
}
EXPORT_SYMBOL_GPL(kstrtouint);
int kstrtoint(const char *s, unsigned int base, int *res)
{
long long tmp;
int rv;
rv = kstrtoll(s, base, &tmp);
if (rv < 0)
return rv;
if (tmp != (long long)(int)tmp)
return -ERANGE;
*res = tmp;
return 0;
}
EXPORT_SYMBOL_GPL(kstrtoint);
int kstrtou16(const char *s, unsigned int base, u16 *res)
{
unsigned long long tmp;
int rv;
rv = kstrtoull(s, base, &tmp);
if (rv < 0)
return rv;
if (tmp != (unsigned long long)(u16)tmp)
return -ERANGE;
*res = tmp;
return 0;
}
EXPORT_SYMBOL_GPL(kstrtou16);
int kstrtos16(const char *s, unsigned int base, s16 *res)
{
long long tmp;
int rv;
rv = kstrtoll(s, base, &tmp);
if (rv < 0)
return rv;
if (tmp != (long long)(s16)tmp)
return -ERANGE;
*res = tmp;
return 0;
}
EXPORT_SYMBOL_GPL(kstrtos16);
int kstrtou8(const char *s, unsigned int base, u8 *res)
{
unsigned long long tmp;
int rv;
rv = kstrtoull(s, base, &tmp);
if (rv < 0)
return rv;
if (tmp != (unsigned long long)(u8)tmp)
return -ERANGE;
*res = tmp;
return 0;
}
EXPORT_SYMBOL_GPL(kstrtou8);
int kstrtos8(const char *s, unsigned int base, s8 *res)
{
long long tmp;
int rv;
rv = kstrtoll(s, base, &tmp);
if (rv < 0)
return rv;
if (tmp != (long long)(s8)tmp)
return -ERANGE;
*res = tmp;
return 0;
}
EXPORT_SYMBOL_GPL(kstrtos8);
#endif /* #ifndef strict_strtol */
+78
View File
@@ -0,0 +1,78 @@
#include <linux/module.h>
MODULE_AUTHOR("Luis R. Rodriguez");
MODULE_DESCRIPTION("Kernel compatibility module");
MODULE_LICENSE("GPL");
#ifndef COMPAT_BASE
#error "You need a COMPAT_BASE"
#endif
#ifndef COMPAT_BASE_TREE
#error "You need a COMPAT_BASE_TREE"
#endif
#ifndef COMPAT_BASE_TREE_VERSION
#error "You need a COMPAT_BASE_TREE_VERSION"
#endif
#ifndef COMPAT_VERSION
#error "You need a COMPAT_VERSION"
#endif
static char *compat_base = COMPAT_BASE;
static char *compat_base_tree = COMPAT_BASE_TREE;
static char *compat_base_tree_version = COMPAT_BASE_TREE_VERSION;
static char *compat_version = COMPAT_VERSION;
module_param(compat_base, charp, 0400);
MODULE_PARM_DESC(compat_base_tree,
"The upstream verion of compat.git used");
module_param(compat_base_tree, charp, 0400);
MODULE_PARM_DESC(compat_base_tree,
"The upstream tree used as base for this backport");
module_param(compat_base_tree_version, charp, 0400);
MODULE_PARM_DESC(compat_base_tree_version,
"The git-describe of the upstream base tree");
module_param(compat_version, charp, 0400);
MODULE_PARM_DESC(compat_version,
"Version of the kernel compat backport work");
void compat_dependency_symbol(void)
{
}
EXPORT_SYMBOL_GPL(compat_dependency_symbol);
static int __init compat_init(void)
{
compat_pm_qos_power_init();
compat_system_workqueue_create();
init_compat_mmc_pm_flags();
printk(KERN_INFO
COMPAT_PROJECT " backport release: "
COMPAT_VERSION
"\n");
printk(KERN_INFO "Backport based on "
COMPAT_BASE_TREE " " COMPAT_BASE_TREE_VERSION
"\n");
printk(KERN_INFO "compat.git: "
COMPAT_BASE_TREE "\n");
return 0;
}
module_init(compat_init);
static void __exit compat_exit(void)
{
compat_pm_qos_power_deinit();
compat_system_workqueue_destroy();
return;
}
module_exit(compat_exit);
+477
View File
@@ -0,0 +1,477 @@
#include <net/compat.h>
/* This is the backport of pm-qos params for kernels <= 2.6.25 */
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25))
/*
* This module exposes the interface to kernel space for specifying
* QoS dependencies. It provides infrastructure for registration of:
*
* Dependents on a QoS value : register requirements
* Watchers of QoS value : get notified when target QoS value changes
*
* This QoS design is best effort based. Dependents register their QoS needs.
* Watchers register to keep track of the current QoS needs of the system.
*
* There are 3 basic classes of QoS parameter: latency, timeout, throughput
* each have defined units:
* latency: usec
* timeout: usec <-- currently not used.
* throughput: kbs (kilo byte / sec)
*
* There are lists of pm_qos_objects each one wrapping requirements, notifiers
*
* User mode requirements on a QOS parameter register themselves to the
* subsystem by opening the device node /dev/... and writing there request to
* the node. As long as the process holds a file handle open to the node the
* client continues to be accounted for. Upon file release the usermode
* requirement is removed and a new qos target is computed. This way when the
* requirement that the application has is cleaned up when closes the file
* pointer or exits the pm_qos_object will get an opportunity to clean up.
*
* Mark Gross <mgross@linux.intel.com>
*/
#include <linux/pm_qos_params.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/uaccess.h>
/*
* locking rule: all changes to requirements or notifiers lists
* or pm_qos_object list and pm_qos_objects need to happen with pm_qos_lock
* held, taken with _irqsave. One lock to rule them all
*/
struct requirement_list {
struct list_head list;
union {
s32 value;
s32 usec;
s32 kbps;
};
char *name;
};
static s32 max_compare(s32 v1, s32 v2);
static s32 min_compare(s32 v1, s32 v2);
struct pm_qos_object {
struct requirement_list requirements;
struct blocking_notifier_head *notifiers;
struct miscdevice pm_qos_power_miscdev;
char *name;
s32 default_value;
atomic_t target_value;
s32 (*comparitor)(s32, s32);
};
static struct pm_qos_object null_pm_qos;
static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier);
static struct pm_qos_object cpu_dma_pm_qos = {
.requirements = {LIST_HEAD_INIT(cpu_dma_pm_qos.requirements.list)},
.notifiers = &cpu_dma_lat_notifier,
.name = "cpu_dma_latency",
.default_value = 2000 * USEC_PER_SEC,
.target_value = ATOMIC_INIT(2000 * USEC_PER_SEC),
.comparitor = min_compare
};
static BLOCKING_NOTIFIER_HEAD(network_lat_notifier);
static struct pm_qos_object network_lat_pm_qos = {
.requirements = {LIST_HEAD_INIT(network_lat_pm_qos.requirements.list)},
.notifiers = &network_lat_notifier,
.name = "network_latency",
.default_value = 2000 * USEC_PER_SEC,
.target_value = ATOMIC_INIT(2000 * USEC_PER_SEC),
.comparitor = min_compare
};
static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier);
static struct pm_qos_object network_throughput_pm_qos = {
.requirements =
{LIST_HEAD_INIT(network_throughput_pm_qos.requirements.list)},
.notifiers = &network_throughput_notifier,
.name = "network_throughput",
.default_value = 0,
.target_value = ATOMIC_INIT(0),
.comparitor = max_compare
};
static BLOCKING_NOTIFIER_HEAD(system_bus_freq_notifier);
static struct pm_qos_object system_bus_freq_pm_qos = {
.requirements =
{LIST_HEAD_INIT(system_bus_freq_pm_qos.requirements.list)},
.notifiers = &system_bus_freq_notifier,
.name = "system_bus_freq",
.default_value = 0,
.target_value = ATOMIC_INIT(0),
.comparitor = max_compare
};
static struct pm_qos_object *pm_qos_array[PM_QOS_NUM_CLASSES] = {
[PM_QOS_RESERVED] = &null_pm_qos,
[PM_QOS_CPU_DMA_LATENCY] = &cpu_dma_pm_qos,
[PM_QOS_NETWORK_LATENCY] = &network_lat_pm_qos,
[PM_QOS_NETWORK_THROUGHPUT] = &network_throughput_pm_qos,
[PM_QOS_SYSTEM_BUS_FREQ] = &system_bus_freq_pm_qos,
};
static DEFINE_SPINLOCK(pm_qos_lock);
static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
size_t count, loff_t *f_pos);
static int pm_qos_power_open(struct inode *inode, struct file *filp);
static int pm_qos_power_release(struct inode *inode, struct file *filp);
static const struct file_operations pm_qos_power_fops = {
.write = pm_qos_power_write,
.open = pm_qos_power_open,
.release = pm_qos_power_release,
};
/* static helper functions */
static s32 max_compare(s32 v1, s32 v2)
{
return max(v1, v2);
}
static s32 min_compare(s32 v1, s32 v2)
{
return min(v1, v2);
}
static void update_target(int target)
{
s32 extreme_value;
struct requirement_list *node;
unsigned long flags;
int call_notifier = 0;
spin_lock_irqsave(&pm_qos_lock, flags);
extreme_value = pm_qos_array[target]->default_value;
list_for_each_entry(node,
&pm_qos_array[target]->requirements.list, list) {
extreme_value = pm_qos_array[target]->comparitor(
extreme_value, node->value);
}
if (atomic_read(&pm_qos_array[target]->target_value) != extreme_value) {
call_notifier = 1;
atomic_set(&pm_qos_array[target]->target_value, extreme_value);
pr_debug(KERN_ERR "new target for qos %d is %d\n", target,
atomic_read(&pm_qos_array[target]->target_value));
}
spin_unlock_irqrestore(&pm_qos_lock, flags);
if (call_notifier)
blocking_notifier_call_chain(pm_qos_array[target]->notifiers,
(unsigned long) extreme_value, NULL);
}
static int register_pm_qos_misc(struct pm_qos_object *qos)
{
qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR;
qos->pm_qos_power_miscdev.name = qos->name;
qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops;
return misc_register(&qos->pm_qos_power_miscdev);
}
static int find_pm_qos_object_by_minor(int minor)
{
int pm_qos_class;
for (pm_qos_class = 0;
pm_qos_class < PM_QOS_NUM_CLASSES; pm_qos_class++) {
if (minor ==
pm_qos_array[pm_qos_class]->pm_qos_power_miscdev.minor)
return pm_qos_class;
}
return -1;
}
/**
* pm_qos_requirement - returns current system wide qos expectation
* @pm_qos_class: identification of which qos value is requested
*
* This function returns the current target value in an atomic manner.
*/
int pm_qos_requirement(int pm_qos_class)
{
return atomic_read(&pm_qos_array[pm_qos_class]->target_value);
}
EXPORT_SYMBOL_GPL(pm_qos_requirement);
/**
* pm_qos_add_requirement - inserts new qos request into the list
* @pm_qos_class: identifies which list of qos request to us
* @name: identifies the request
* @value: defines the qos request
*
* This function inserts a new entry in the pm_qos_class list of requested qos
* performance characteristics. It recomputes the aggregate QoS expectations
* for the pm_qos_class of parameters.
*/
int pm_qos_add_requirement(int pm_qos_class, char *name, s32 value)
{
struct requirement_list *dep;
unsigned long flags;
dep = kzalloc(sizeof(struct requirement_list), GFP_KERNEL);
if (dep) {
if (value == PM_QOS_DEFAULT_VALUE)
dep->value = pm_qos_array[pm_qos_class]->default_value;
else
dep->value = value;
dep->name = kstrdup(name, GFP_KERNEL);
if (!dep->name)
goto cleanup;
spin_lock_irqsave(&pm_qos_lock, flags);
list_add(&dep->list,
&pm_qos_array[pm_qos_class]->requirements.list);
spin_unlock_irqrestore(&pm_qos_lock, flags);
update_target(pm_qos_class);
return 0;
}
cleanup:
kfree(dep);
return -ENOMEM;
}
EXPORT_SYMBOL_GPL(pm_qos_add_requirement);
/**
* pm_qos_update_requirement - modifies an existing qos request
* @pm_qos_class: identifies which list of qos request to us
* @name: identifies the request
* @value: defines the qos request
*
* Updates an existing qos requirement for the pm_qos_class of parameters along
* with updating the target pm_qos_class value.
*
* If the named request isn't in the list then no change is made.
*/
int pm_qos_update_requirement(int pm_qos_class, char *name, s32 new_value)
{
unsigned long flags;
struct requirement_list *node;
int pending_update = 0;
spin_lock_irqsave(&pm_qos_lock, flags);
list_for_each_entry(node,
&pm_qos_array[pm_qos_class]->requirements.list, list) {
if (strcmp(node->name, name) == 0) {
if (new_value == PM_QOS_DEFAULT_VALUE)
node->value =
pm_qos_array[pm_qos_class]->default_value;
else
node->value = new_value;
pending_update = 1;
break;
}
}
spin_unlock_irqrestore(&pm_qos_lock, flags);
if (pending_update)
update_target(pm_qos_class);
return 0;
}
EXPORT_SYMBOL_GPL(pm_qos_update_requirement);
/**
* pm_qos_remove_requirement - modifies an existing qos request
* @pm_qos_class: identifies which list of qos request to us
* @name: identifies the request
*
* Will remove named qos request from pm_qos_class list of parameters and
* recompute the current target value for the pm_qos_class.
*/
void pm_qos_remove_requirement(int pm_qos_class, char *name)
{
unsigned long flags;
struct requirement_list *node;
int pending_update = 0;
spin_lock_irqsave(&pm_qos_lock, flags);
list_for_each_entry(node,
&pm_qos_array[pm_qos_class]->requirements.list, list) {
if (strcmp(node->name, name) == 0) {
kfree(node->name);
list_del(&node->list);
kfree(node);
pending_update = 1;
break;
}
}
spin_unlock_irqrestore(&pm_qos_lock, flags);
if (pending_update)
update_target(pm_qos_class);
}
EXPORT_SYMBOL_GPL(pm_qos_remove_requirement);
/**
* pm_qos_add_notifier - sets notification entry for changes to target value
* @pm_qos_class: identifies which qos target changes should be notified.
* @notifier: notifier block managed by caller.
*
* will register the notifier into a notification chain that gets called
* upon changes to the pm_qos_class target value.
*/
int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier)
{
int retval;
retval = blocking_notifier_chain_register(
pm_qos_array[pm_qos_class]->notifiers, notifier);
return retval;
}
EXPORT_SYMBOL_GPL(pm_qos_add_notifier);
/**
* pm_qos_remove_notifier - deletes notification entry from chain.
* @pm_qos_class: identifies which qos target changes are notified.
* @notifier: notifier block to be removed.
*
* will remove the notifier from the notification chain that gets called
* upon changes to the pm_qos_class target value.
*/
int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier)
{
int retval;
retval = blocking_notifier_chain_unregister(
pm_qos_array[pm_qos_class]->notifiers, notifier);
return retval;
}
EXPORT_SYMBOL_GPL(pm_qos_remove_notifier);
#define PID_NAME_LEN 32
static int pm_qos_power_open(struct inode *inode, struct file *filp)
{
int ret;
long pm_qos_class;
char name[PID_NAME_LEN];
pm_qos_class = find_pm_qos_object_by_minor(iminor(inode));
if (pm_qos_class >= 0) {
filp->private_data = (void *)pm_qos_class;
snprintf(name, PID_NAME_LEN, "process_%d", current->pid);
ret = pm_qos_add_requirement(pm_qos_class, name,
PM_QOS_DEFAULT_VALUE);
if (ret >= 0)
return 0;
}
return -EPERM;
}
static int pm_qos_power_release(struct inode *inode, struct file *filp)
{
int pm_qos_class;
char name[PID_NAME_LEN];
pm_qos_class = (long)filp->private_data;
snprintf(name, PID_NAME_LEN, "process_%d", current->pid);
pm_qos_remove_requirement(pm_qos_class, name);
return 0;
}
static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
size_t count, loff_t *f_pos)
{
s32 value;
int pm_qos_class;
char name[PID_NAME_LEN];
pm_qos_class = (long)filp->private_data;
if (count != sizeof(s32))
return -EINVAL;
if (copy_from_user(&value, buf, sizeof(s32)))
return -EFAULT;
snprintf(name, PID_NAME_LEN, "process_%d", current->pid);
pm_qos_update_requirement(pm_qos_class, name, value);
return sizeof(s32);
}
/*
* This initializes pm-qos for older kernels.
*/
int compat_pm_qos_power_init(void)
{
int ret = 0;
ret = register_pm_qos_misc(&cpu_dma_pm_qos);
if (ret < 0) {
printk(KERN_ERR "pm_qos_param: cpu_dma_latency setup failed\n");
return ret;
}
ret = register_pm_qos_misc(&network_lat_pm_qos);
if (ret < 0) {
printk(KERN_ERR "pm_qos_param: network_latency setup failed\n");
return ret;
}
ret = register_pm_qos_misc(&network_throughput_pm_qos);
if (ret < 0) {
printk(KERN_ERR
"pm_qos_param: network_throughput setup failed\n");
return ret;
}
ret = register_pm_qos_misc(&system_bus_freq_pm_qos);
if (ret < 0)
printk(KERN_ERR
"pm_qos_param: system_bus_freq setup failed\n");
return ret;
}
int compat_pm_qos_power_deinit(void)
{
int ret = 0;
ret = misc_deregister(&cpu_dma_pm_qos.pm_qos_power_miscdev);
if (ret < 0) {
printk(KERN_ERR "pm_qos_param: cpu_dma_latency deinit failed\n");
return ret;
}
ret = misc_deregister(&network_lat_pm_qos.pm_qos_power_miscdev);
if (ret < 0) {
printk(KERN_ERR "pm_qos_param: network_latency deinit failed\n");
return ret;
}
ret = misc_deregister(&network_throughput_pm_qos.pm_qos_power_miscdev);
if (ret < 0) {
printk(KERN_ERR
"pm_qos_param: network_throughput deinit failed\n");
return ret;
}
ret = misc_deregister(&system_bus_freq_pm_qos.pm_qos_power_miscdev);
if (ret < 0) {
printk(KERN_ERR
"pm_qos_param: system_bus_freq deinit failed\n");
return ret;
}
return ret;
}
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) */
+306
View File
@@ -0,0 +1,306 @@
/*
* Codel - The Controlled-Delay Active Queue Management algorithm
*
* Copyright (C) 2011-2012 Kathleen Nichols <nichols@pollere.com>
* Copyright (C) 2011-2012 Van Jacobson <van@pollere.net>
*
* Implemented on linux by :
* Copyright (C) 2012 Michael D. Taht <dave.taht@bufferbloat.net>
* Copyright (C) 2012 Eric Dumazet <edumazet@google.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The names of the authors may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* Alternatively, provided that this notice is retained in full, this
* software may be distributed under the terms of the GNU General
* Public License ("GPL") version 2, in which case the provisions of the
* GPL apply INSTEAD OF those given above.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/skbuff.h>
#include <linux/prefetch.h>
#include <net/pkt_sched.h>
#include <net/codel.h>
#define DEFAULT_CODEL_LIMIT 1000
struct codel_sched_data {
struct codel_params params;
struct codel_vars vars;
struct codel_stats stats;
u32 drop_overlimit;
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39))
u32 limit;
#endif
};
/* This is the specific function called from codel_dequeue()
* to dequeue a packet from queue. Note: backlog is handled in
* codel, we dont need to reduce it here.
*/
static struct sk_buff *dequeue(struct codel_vars *vars, struct Qdisc *sch)
{
struct sk_buff *skb = __skb_dequeue(&sch->q);
prefetch(&skb->end); /* we'll need skb_shinfo() */
return skb;
}
static struct sk_buff *codel_qdisc_dequeue(struct Qdisc *sch)
{
struct codel_sched_data *q = qdisc_priv(sch);
struct sk_buff *skb;
skb = codel_dequeue(sch, &q->params, &q->vars, &q->stats, dequeue);
/* We cant call qdisc_tree_decrease_qlen() if our qlen is 0,
* or HTB crashes. Defer it for next round.
*/
if (q->stats.drop_count && sch->q.qlen) {
qdisc_tree_decrease_qlen(sch, q->stats.drop_count);
q->stats.drop_count = 0;
}
if (skb)
qdisc_bstats_update(sch, skb);
return skb;
}
static int codel_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch)
{
struct codel_sched_data *q;
q = qdisc_priv(sch);
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39))
if (likely(qdisc_qlen(sch) < q->limit)) {
#else
if (likely(qdisc_qlen(sch) < sch->limit)) {
#endif
codel_set_enqueue_time(skb);
return qdisc_enqueue_tail(skb, sch);
}
q->drop_overlimit++;
return qdisc_drop(skb, sch);
}
static const struct nla_policy codel_policy[TCA_CODEL_MAX + 1] = {
[TCA_CODEL_TARGET] = { .type = NLA_U32 },
[TCA_CODEL_LIMIT] = { .type = NLA_U32 },
[TCA_CODEL_INTERVAL] = { .type = NLA_U32 },
[TCA_CODEL_ECN] = { .type = NLA_U32 },
};
static int codel_change(struct Qdisc *sch, struct nlattr *opt)
{
struct codel_sched_data *q = qdisc_priv(sch);
struct nlattr *tb[TCA_CODEL_MAX + 1];
unsigned int qlen;
int err;
if (!opt)
return -EINVAL;
err = nla_parse_nested(tb, TCA_CODEL_MAX, opt, codel_policy);
if (err < 0)
return err;
sch_tree_lock(sch);
if (tb[TCA_CODEL_TARGET]) {
u32 target = nla_get_u32(tb[TCA_CODEL_TARGET]);
q->params.target = ((u64)target * NSEC_PER_USEC) >> CODEL_SHIFT;
}
if (tb[TCA_CODEL_INTERVAL]) {
u32 interval = nla_get_u32(tb[TCA_CODEL_INTERVAL]);
q->params.interval = ((u64)interval * NSEC_PER_USEC) >> CODEL_SHIFT;
}
if (tb[TCA_CODEL_LIMIT])
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39))
q->limit = nla_get_u32(tb[TCA_CODEL_LIMIT]);
#else
sch->limit = nla_get_u32(tb[TCA_CODEL_LIMIT]);
#endif
if (tb[TCA_CODEL_ECN])
q->params.ecn = !!nla_get_u32(tb[TCA_CODEL_ECN]);
qlen = sch->q.qlen;
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39))
while (sch->q.qlen > q->limit) {
#else
while (sch->q.qlen > sch->limit) {
#endif
struct sk_buff *skb = __skb_dequeue(&sch->q);
sch->qstats.backlog -= qdisc_pkt_len(skb);
qdisc_drop(skb, sch);
}
qdisc_tree_decrease_qlen(sch, qlen - sch->q.qlen);
sch_tree_unlock(sch);
return 0;
}
static int codel_init(struct Qdisc *sch, struct nlattr *opt)
{
struct codel_sched_data *q = qdisc_priv(sch);
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39))
q->limit = DEFAULT_CODEL_LIMIT;
#else
sch->limit = DEFAULT_CODEL_LIMIT;
#endif
codel_params_init(&q->params);
codel_vars_init(&q->vars);
codel_stats_init(&q->stats);
if (opt) {
int err = codel_change(sch, opt);
if (err)
return err;
}
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39))
if (q->limit >= 1)
#else
if (sch->limit >= 1)
#endif
sch->flags |= TCQ_F_CAN_BYPASS;
else
sch->flags &= ~TCQ_F_CAN_BYPASS;
return 0;
}
static int codel_dump(struct Qdisc *sch, struct sk_buff *skb)
{
struct codel_sched_data *q = qdisc_priv(sch);
struct nlattr *opts;
opts = nla_nest_start(skb, TCA_OPTIONS);
if (opts == NULL)
goto nla_put_failure;
if (nla_put_u32(skb, TCA_CODEL_TARGET,
codel_time_to_us(q->params.target)) ||
nla_put_u32(skb, TCA_CODEL_LIMIT,
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39))
q->limit) ||
#else
sch->limit) ||
#endif
nla_put_u32(skb, TCA_CODEL_INTERVAL,
codel_time_to_us(q->params.interval)) ||
nla_put_u32(skb, TCA_CODEL_ECN,
q->params.ecn))
goto nla_put_failure;
return nla_nest_end(skb, opts);
nla_put_failure:
nla_nest_cancel(skb, opts);
return -1;
}
static int codel_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
{
const struct codel_sched_data *q = qdisc_priv(sch);
struct tc_codel_xstats st = {
.maxpacket = q->stats.maxpacket,
.count = q->vars.count,
.lastcount = q->vars.lastcount,
.drop_overlimit = q->drop_overlimit,
.ldelay = codel_time_to_us(q->vars.ldelay),
.dropping = q->vars.dropping,
.ecn_mark = q->stats.ecn_mark,
};
if (q->vars.dropping) {
codel_tdiff_t delta = q->vars.drop_next - codel_get_time();
if (delta >= 0)
st.drop_next = codel_time_to_us(delta);
else
st.drop_next = -codel_time_to_us(-delta);
}
return gnet_stats_copy_app(d, &st, sizeof(st));
}
static void codel_reset(struct Qdisc *sch)
{
struct codel_sched_data *q = qdisc_priv(sch);
qdisc_reset_queue(sch);
codel_vars_init(&q->vars);
}
static struct Qdisc_ops codel_qdisc_ops __read_mostly = {
.id = "codel",
.priv_size = sizeof(struct codel_sched_data),
.enqueue = codel_qdisc_enqueue,
.dequeue = codel_qdisc_dequeue,
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28))
.peek = qdisc_peek_dequeued,
#endif
.init = codel_init,
.reset = codel_reset,
.change = codel_change,
.dump = codel_dump,
.dump_stats = codel_dump_stats,
.owner = THIS_MODULE,
};
static int __init codel_module_init(void)
{
return register_qdisc(&codel_qdisc_ops);
}
static void __exit codel_module_exit(void)
{
unregister_qdisc(&codel_qdisc_ops);
}
module_init(codel_module_init)
module_exit(codel_module_exit)
MODULE_DESCRIPTION("Controlled Delay queue discipline");
MODULE_AUTHOR("Dave Taht");
MODULE_AUTHOR("Eric Dumazet");
MODULE_LICENSE("Dual BSD/GPL");
+659
View File
@@ -0,0 +1,659 @@
/*
* Fair Queue CoDel discipline
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Copyright (C) 2012 Eric Dumazet <edumazet@google.com>
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/jiffies.h>
#include <linux/string.h>
#include <linux/in.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/jhash.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <net/netlink.h>
#include <net/pkt_sched.h>
#include <net/flow_keys.h>
#include <net/codel.h>
/* Fair Queue CoDel.
*
* Principles :
* Packets are classified (internal classifier or external) on flows.
* This is a Stochastic model (as we use a hash, several flows
* might be hashed on same slot)
* Each flow has a CoDel managed queue.
* Flows are linked onto two (Round Robin) lists,
* so that new flows have priority on old ones.
*
* For a given flow, packets are not reordered (CoDel uses a FIFO)
* head drops only.
* ECN capability is on by default.
* Low memory footprint (64 bytes per flow)
*/
struct fq_codel_flow {
struct sk_buff *head;
struct sk_buff *tail;
struct list_head flowchain;
int deficit;
u32 dropped; /* number of drops (or ECN marks) on this flow */
struct codel_vars cvars;
}; /* please try to keep this structure <= 64 bytes */
struct fq_codel_sched_data {
struct tcf_proto *filter_list; /* optional external classifier */
struct fq_codel_flow *flows; /* Flows table [flows_cnt] */
u32 *backlogs; /* backlog table [flows_cnt] */
u32 flows_cnt; /* number of flows */
u32 perturbation; /* hash perturbation */
u32 quantum; /* psched_mtu(qdisc_dev(sch)); */
struct codel_params cparams;
struct codel_stats cstats;
u32 drop_overlimit;
u32 new_flow_count;
struct list_head new_flows; /* list of new flows */
struct list_head old_flows; /* list of old flows */
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39))
u32 limit;
#endif
};
static unsigned int fq_codel_hash(const struct fq_codel_sched_data *q,
const struct sk_buff *skb)
{
struct flow_keys keys;
unsigned int hash;
skb_flow_dissect(skb, &keys);
hash = jhash_3words((__force u32)keys.dst,
(__force u32)keys.src ^ keys.ip_proto,
(__force u32)keys.ports, q->perturbation);
return ((u64)hash * q->flows_cnt) >> 32;
}
static unsigned int fq_codel_classify(struct sk_buff *skb, struct Qdisc *sch,
int *qerr)
{
struct fq_codel_sched_data *q = qdisc_priv(sch);
struct tcf_result res;
int result;
if (TC_H_MAJ(skb->priority) == sch->handle &&
TC_H_MIN(skb->priority) > 0 &&
TC_H_MIN(skb->priority) <= q->flows_cnt)
return TC_H_MIN(skb->priority);
if (!q->filter_list)
return fq_codel_hash(q, skb) + 1;
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
result = tc_classify(skb, q->filter_list, &res);
if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT
switch (result) {
case TC_ACT_STOLEN:
case TC_ACT_QUEUED:
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
case TC_ACT_SHOT:
return 0;
}
#endif
if (TC_H_MIN(res.classid) <= q->flows_cnt)
return TC_H_MIN(res.classid);
}
return 0;
}
/* helper functions : might be changed when/if skb use a standard list_head */
/* remove one skb from head of slot queue */
static inline struct sk_buff *dequeue_head(struct fq_codel_flow *flow)
{
struct sk_buff *skb = flow->head;
flow->head = skb->next;
skb->next = NULL;
return skb;
}
/* add skb to flow queue (tail add) */
static inline void flow_queue_add(struct fq_codel_flow *flow,
struct sk_buff *skb)
{
if (flow->head == NULL)
flow->head = skb;
else
flow->tail->next = skb;
flow->tail = skb;
skb->next = NULL;
}
static unsigned int fq_codel_drop(struct Qdisc *sch)
{
struct fq_codel_sched_data *q = qdisc_priv(sch);
struct sk_buff *skb;
unsigned int maxbacklog = 0, idx = 0, i, len;
struct fq_codel_flow *flow;
/* Queue is full! Find the fat flow and drop packet from it.
* This might sound expensive, but with 1024 flows, we scan
* 4KB of memory, and we dont need to handle a complex tree
* in fast path (packet queue/enqueue) with many cache misses.
*/
for (i = 0; i < q->flows_cnt; i++) {
if (q->backlogs[i] > maxbacklog) {
maxbacklog = q->backlogs[i];
idx = i;
}
}
flow = &q->flows[idx];
skb = dequeue_head(flow);
len = qdisc_pkt_len(skb);
q->backlogs[idx] -= len;
kfree_skb(skb);
sch->q.qlen--;
sch->qstats.drops++;
sch->qstats.backlog -= len;
flow->dropped++;
return idx;
}
static int fq_codel_enqueue(struct sk_buff *skb, struct Qdisc *sch)
{
struct fq_codel_sched_data *q = qdisc_priv(sch);
unsigned int idx;
struct fq_codel_flow *flow;
int uninitialized_var(ret);
idx = fq_codel_classify(skb, sch, &ret);
if (idx == 0) {
if (ret & __NET_XMIT_BYPASS)
sch->qstats.drops++;
kfree_skb(skb);
return ret;
}
idx--;
codel_set_enqueue_time(skb);
flow = &q->flows[idx];
flow_queue_add(flow, skb);
q->backlogs[idx] += qdisc_pkt_len(skb);
sch->qstats.backlog += qdisc_pkt_len(skb);
if (list_empty(&flow->flowchain)) {
list_add_tail(&flow->flowchain, &q->new_flows);
codel_vars_init(&flow->cvars);
q->new_flow_count++;
flow->deficit = q->quantum;
flow->dropped = 0;
}
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39))
if (++sch->q.qlen < q->limit)
#else
if (++sch->q.qlen < sch->limit)
#endif
return NET_XMIT_SUCCESS;
q->drop_overlimit++;
/* Return Congestion Notification only if we dropped a packet
* from this flow.
*/
if (fq_codel_drop(sch) == idx)
return NET_XMIT_CN;
/* As we dropped a packet, better let upper stack know this */
qdisc_tree_decrease_qlen(sch, 1);
return NET_XMIT_SUCCESS;
}
/* This is the specific function called from codel_dequeue()
* to dequeue a packet from queue. Note: backlog is handled in
* codel, we dont need to reduce it here.
*/
static struct sk_buff *dequeue(struct codel_vars *vars, struct Qdisc *sch)
{
struct fq_codel_sched_data *q = qdisc_priv(sch);
struct fq_codel_flow *flow;
struct sk_buff *skb = NULL;
flow = container_of(vars, struct fq_codel_flow, cvars);
if (flow->head) {
skb = dequeue_head(flow);
q->backlogs[flow - q->flows] -= qdisc_pkt_len(skb);
sch->q.qlen--;
}
return skb;
}
static struct sk_buff *fq_codel_dequeue(struct Qdisc *sch)
{
struct fq_codel_sched_data *q = qdisc_priv(sch);
struct sk_buff *skb;
struct fq_codel_flow *flow;
struct list_head *head;
u32 prev_drop_count, prev_ecn_mark;
begin:
head = &q->new_flows;
if (list_empty(head)) {
head = &q->old_flows;
if (list_empty(head))
return NULL;
}
flow = list_first_entry(head, struct fq_codel_flow, flowchain);
if (flow->deficit <= 0) {
flow->deficit += q->quantum;
list_move_tail(&flow->flowchain, &q->old_flows);
goto begin;
}
prev_drop_count = q->cstats.drop_count;
prev_ecn_mark = q->cstats.ecn_mark;
skb = codel_dequeue(sch, &q->cparams, &flow->cvars, &q->cstats,
dequeue);
flow->dropped += q->cstats.drop_count - prev_drop_count;
flow->dropped += q->cstats.ecn_mark - prev_ecn_mark;
if (!skb) {
/* force a pass through old_flows to prevent starvation */
if ((head == &q->new_flows) && !list_empty(&q->old_flows))
list_move_tail(&flow->flowchain, &q->old_flows);
else
list_del_init(&flow->flowchain);
goto begin;
}
qdisc_bstats_update(sch, skb);
flow->deficit -= qdisc_pkt_len(skb);
/* We cant call qdisc_tree_decrease_qlen() if our qlen is 0,
* or HTB crashes. Defer it for next round.
*/
if (q->cstats.drop_count && sch->q.qlen) {
qdisc_tree_decrease_qlen(sch, q->cstats.drop_count);
q->cstats.drop_count = 0;
}
return skb;
}
static void fq_codel_reset(struct Qdisc *sch)
{
struct sk_buff *skb;
while ((skb = fq_codel_dequeue(sch)) != NULL)
kfree_skb(skb);
}
static const struct nla_policy fq_codel_policy[TCA_FQ_CODEL_MAX + 1] = {
[TCA_FQ_CODEL_TARGET] = { .type = NLA_U32 },
[TCA_FQ_CODEL_LIMIT] = { .type = NLA_U32 },
[TCA_FQ_CODEL_INTERVAL] = { .type = NLA_U32 },
[TCA_FQ_CODEL_ECN] = { .type = NLA_U32 },
[TCA_FQ_CODEL_FLOWS] = { .type = NLA_U32 },
[TCA_FQ_CODEL_QUANTUM] = { .type = NLA_U32 },
};
static int fq_codel_change(struct Qdisc *sch, struct nlattr *opt)
{
struct fq_codel_sched_data *q = qdisc_priv(sch);
struct nlattr *tb[TCA_FQ_CODEL_MAX + 1];
int err;
if (!opt)
return -EINVAL;
err = nla_parse_nested(tb, TCA_FQ_CODEL_MAX, opt, fq_codel_policy);
if (err < 0)
return err;
if (tb[TCA_FQ_CODEL_FLOWS]) {
if (q->flows)
return -EINVAL;
q->flows_cnt = nla_get_u32(tb[TCA_FQ_CODEL_FLOWS]);
if (!q->flows_cnt ||
q->flows_cnt > 65536)
return -EINVAL;
}
sch_tree_lock(sch);
if (tb[TCA_FQ_CODEL_TARGET]) {
u64 target = nla_get_u32(tb[TCA_FQ_CODEL_TARGET]);
q->cparams.target = (target * NSEC_PER_USEC) >> CODEL_SHIFT;
}
if (tb[TCA_FQ_CODEL_INTERVAL]) {
u64 interval = nla_get_u32(tb[TCA_FQ_CODEL_INTERVAL]);
q->cparams.interval = (interval * NSEC_PER_USEC) >> CODEL_SHIFT;
}
if (tb[TCA_FQ_CODEL_LIMIT])
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39))
q->limit = nla_get_u32(tb[TCA_FQ_CODEL_LIMIT]);
#else
sch->limit = nla_get_u32(tb[TCA_FQ_CODEL_LIMIT]);
#endif
if (tb[TCA_FQ_CODEL_ECN])
q->cparams.ecn = !!nla_get_u32(tb[TCA_FQ_CODEL_ECN]);
if (tb[TCA_FQ_CODEL_QUANTUM])
q->quantum = max(256U, nla_get_u32(tb[TCA_FQ_CODEL_QUANTUM]));
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39))
while (sch->q.qlen > q->limit) {
#else
while (sch->q.qlen > sch->limit) {
#endif
struct sk_buff *skb = fq_codel_dequeue(sch);
kfree_skb(skb);
q->cstats.drop_count++;
}
qdisc_tree_decrease_qlen(sch, q->cstats.drop_count);
q->cstats.drop_count = 0;
sch_tree_unlock(sch);
return 0;
}
static void *fq_codel_zalloc(size_t sz)
{
void *ptr = kzalloc(sz, GFP_KERNEL | __GFP_NOWARN);
if (!ptr)
ptr = vzalloc(sz);
return ptr;
}
static void fq_codel_free(void *addr)
{
if (addr) {
if (is_vmalloc_addr(addr))
vfree(addr);
else
kfree(addr);
}
}
static void fq_codel_destroy(struct Qdisc *sch)
{
struct fq_codel_sched_data *q = qdisc_priv(sch);
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25))
tcf_destroy_chain(&q->filter_list);
#else
tcf_destroy_chain(q->filter_list);
#endif
fq_codel_free(q->backlogs);
fq_codel_free(q->flows);
}
static int fq_codel_init(struct Qdisc *sch, struct nlattr *opt)
{
struct fq_codel_sched_data *q = qdisc_priv(sch);
int i;
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39))
q->limit = 10*1024;
#else
sch->limit = 10*1024;
#endif
q->flows_cnt = 1024;
q->quantum = psched_mtu(qdisc_dev(sch));
q->perturbation = net_random();
INIT_LIST_HEAD(&q->new_flows);
INIT_LIST_HEAD(&q->old_flows);
codel_params_init(&q->cparams);
codel_stats_init(&q->cstats);
q->cparams.ecn = true;
if (opt) {
int err = fq_codel_change(sch, opt);
if (err)
return err;
}
if (!q->flows) {
q->flows = fq_codel_zalloc(q->flows_cnt *
sizeof(struct fq_codel_flow));
if (!q->flows)
return -ENOMEM;
q->backlogs = fq_codel_zalloc(q->flows_cnt * sizeof(u32));
if (!q->backlogs) {
fq_codel_free(q->flows);
return -ENOMEM;
}
for (i = 0; i < q->flows_cnt; i++) {
struct fq_codel_flow *flow = q->flows + i;
INIT_LIST_HEAD(&flow->flowchain);
}
}
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39))
if (q->limit >= 1)
#else
if (sch->limit >= 1)
#endif
sch->flags |= TCQ_F_CAN_BYPASS;
else
sch->flags &= ~TCQ_F_CAN_BYPASS;
return 0;
}
static int fq_codel_dump(struct Qdisc *sch, struct sk_buff *skb)
{
struct fq_codel_sched_data *q = qdisc_priv(sch);
struct nlattr *opts;
opts = nla_nest_start(skb, TCA_OPTIONS);
if (opts == NULL)
goto nla_put_failure;
if (nla_put_u32(skb, TCA_FQ_CODEL_TARGET,
codel_time_to_us(q->cparams.target)) ||
nla_put_u32(skb, TCA_FQ_CODEL_LIMIT,
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39))
q->limit) ||
#else
sch->limit) ||
#endif
nla_put_u32(skb, TCA_FQ_CODEL_INTERVAL,
codel_time_to_us(q->cparams.interval)) ||
nla_put_u32(skb, TCA_FQ_CODEL_ECN,
q->cparams.ecn) ||
nla_put_u32(skb, TCA_FQ_CODEL_QUANTUM,
q->quantum) ||
nla_put_u32(skb, TCA_FQ_CODEL_FLOWS,
q->flows_cnt))
goto nla_put_failure;
nla_nest_end(skb, opts);
return skb->len;
nla_put_failure:
return -1;
}
static int fq_codel_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
{
struct fq_codel_sched_data *q = qdisc_priv(sch);
struct tc_fq_codel_xstats st = {
.type = TCA_FQ_CODEL_XSTATS_QDISC,
};
struct list_head *pos;
st.qdisc_stats.maxpacket = q->cstats.maxpacket;
st.qdisc_stats.drop_overlimit = q->drop_overlimit;
st.qdisc_stats.ecn_mark = q->cstats.ecn_mark;
st.qdisc_stats.new_flow_count = q->new_flow_count;
list_for_each(pos, &q->new_flows)
st.qdisc_stats.new_flows_len++;
list_for_each(pos, &q->old_flows)
st.qdisc_stats.old_flows_len++;
return gnet_stats_copy_app(d, &st, sizeof(st));
}
static struct Qdisc *fq_codel_leaf(struct Qdisc *sch, unsigned long arg)
{
return NULL;
}
static unsigned long fq_codel_get(struct Qdisc *sch, u32 classid)
{
return 0;
}
static unsigned long fq_codel_bind(struct Qdisc *sch, unsigned long parent,
u32 classid)
{
/* we cannot bypass queue discipline anymore */
sch->flags &= ~TCQ_F_CAN_BYPASS;
return 0;
}
static void fq_codel_put(struct Qdisc *q, unsigned long cl)
{
}
static struct tcf_proto **fq_codel_find_tcf(struct Qdisc *sch, unsigned long cl)
{
struct fq_codel_sched_data *q = qdisc_priv(sch);
if (cl)
return NULL;
return &q->filter_list;
}
static int fq_codel_dump_class(struct Qdisc *sch, unsigned long cl,
struct sk_buff *skb, struct tcmsg *tcm)
{
tcm->tcm_handle |= TC_H_MIN(cl);
return 0;
}
static int fq_codel_dump_class_stats(struct Qdisc *sch, unsigned long cl,
struct gnet_dump *d)
{
struct fq_codel_sched_data *q = qdisc_priv(sch);
u32 idx = cl - 1;
struct gnet_stats_queue qs = { 0 };
struct tc_fq_codel_xstats xstats;
if (idx < q->flows_cnt) {
const struct fq_codel_flow *flow = &q->flows[idx];
const struct sk_buff *skb = flow->head;
memset(&xstats, 0, sizeof(xstats));
xstats.type = TCA_FQ_CODEL_XSTATS_CLASS;
xstats.class_stats.deficit = flow->deficit;
xstats.class_stats.ldelay =
codel_time_to_us(flow->cvars.ldelay);
xstats.class_stats.count = flow->cvars.count;
xstats.class_stats.lastcount = flow->cvars.lastcount;
xstats.class_stats.dropping = flow->cvars.dropping;
if (flow->cvars.dropping) {
codel_tdiff_t delta = flow->cvars.drop_next -
codel_get_time();
xstats.class_stats.drop_next = (delta >= 0) ?
codel_time_to_us(delta) :
-codel_time_to_us(-delta);
}
while (skb) {
qs.qlen++;
skb = skb->next;
}
qs.backlog = q->backlogs[idx];
qs.drops = flow->dropped;
}
if (gnet_stats_copy_queue(d, &qs) < 0)
return -1;
if (idx < q->flows_cnt)
return gnet_stats_copy_app(d, &xstats, sizeof(xstats));
return 0;
}
static void fq_codel_walk(struct Qdisc *sch, struct qdisc_walker *arg)
{
struct fq_codel_sched_data *q = qdisc_priv(sch);
unsigned int i;
if (arg->stop)
return;
for (i = 0; i < q->flows_cnt; i++) {
if (list_empty(&q->flows[i].flowchain) ||
arg->count < arg->skip) {
arg->count++;
continue;
}
if (arg->fn(sch, i + 1, arg) < 0) {
arg->stop = 1;
break;
}
arg->count++;
}
}
static const struct Qdisc_class_ops fq_codel_class_ops = {
.leaf = fq_codel_leaf,
.get = fq_codel_get,
.put = fq_codel_put,
.tcf_chain = fq_codel_find_tcf,
.bind_tcf = fq_codel_bind,
.unbind_tcf = fq_codel_put,
.dump = fq_codel_dump_class,
.dump_stats = fq_codel_dump_class_stats,
.walk = fq_codel_walk,
};
static struct Qdisc_ops fq_codel_qdisc_ops __read_mostly = {
.cl_ops = &fq_codel_class_ops,
.id = "fq_codel",
.priv_size = sizeof(struct fq_codel_sched_data),
.enqueue = fq_codel_enqueue,
.dequeue = fq_codel_dequeue,
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28))
.peek = qdisc_peek_dequeued,
#endif
.drop = fq_codel_drop,
.init = fq_codel_init,
.reset = fq_codel_reset,
.destroy = fq_codel_destroy,
.change = fq_codel_change,
.dump = fq_codel_dump,
.dump_stats = fq_codel_dump_stats,
.owner = THIS_MODULE,
};
static int __init fq_codel_module_init(void)
{
return register_qdisc(&fq_codel_qdisc_ops);
}
static void __exit fq_codel_module_exit(void)
{
unregister_qdisc(&fq_codel_qdisc_ops);
}
module_init(fq_codel_module_init)
module_exit(fq_codel_module_exit)
MODULE_AUTHOR("Eric Dumazet");
MODULE_LICENSE("GPL");
+21
View File
@@ -0,0 +1,21 @@
#!/bin/sh
if [ -f /usr/bin/lsb_release ]; then
LSB_RED_ID=$(/usr/bin/lsb_release -i -s)
else
LSB_RED_ID="Unknown"
fi
case $LSB_RED_ID in
"Ubuntu")
mkdir -p /lib/udev/ /lib/udev/rules.d/
cp udev/ubuntu/compat_firmware.sh /lib/udev/
cp udev/50-compat_firmware.rules /lib/udev/rules.d/
;;
*)
mkdir -p /lib/udev/ /lib/udev/rules.d/
cp udev/compat_firmware.sh /lib/udev/
cp udev/50-compat_firmware.rules /lib/udev/rules.d/
;;
esac
+105
View File
@@ -0,0 +1,105 @@
#!/bin/bash
#
# Copyright 2012 Luis R. Rodriguez <mcgrof@frijolero.org>
# Copyright 2011 Hauke Mehrtens <hauke@hauke-m.de>
# Copyright 2011 John W. Linville <linville@tuxdriver.com>
#
# Use this to parse a small .config equivalent looking file to generate
# our own autoconf.h. This file has defines for each config option
# just like the kernels include/linux/autoconf.h
#
# XXX: consider using scripts/kconfig/confdata.c instead.
# On the downside this would require the user to have libc though.
# This indicates which is the oldest kernel we support
# Update this if you are adding support for older kernels.
OLDEST_KERNEL_SUPPORTED="2.6.24"
if [ $# -ne 1 ]; then
echo "Usage $0 config-file"
exit
fi
COMPAT_CONFIG="$1"
if [ ! -f $COMPAT_CONFIG ]; then
echo "File $1 is not a file"
exit
fi
# Defines a CONFIG_ option if not defined yet, this helps respect
# linux/autoconf.h
function define_config {
VAR=$1
VALUE=$2
case $VALUE in
n) # Try to undefine it
echo "#undef $VAR"
;;
y)
echo "#ifndef $VAR"
echo "#define $VAR 1"
echo "#endif /* $VAR */"
;;
m)
echo "#ifndef $VAR"
echo "#define $VAR 1"
echo "#endif /* $VAR */"
;;
*) # Assume string
# XXX: add better checks to make sure what was on
# the right was indeed a string
echo "#ifndef $VAR"
echo "#define $VAR \"$VALUE\""
echo "#endif /* $VAR */"
;;
esac
}
function kernel_version_req {
VERSION=$(echo $1 | sed -e 's/\./,/g')
echo "#if (LINUX_VERSION_CODE < KERNEL_VERSION($VERSION))"
echo "#error compat requirement: Linux >= $VERSION"
echo "#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION($VERSION) */"
}
cat <<EOF
#ifndef COMPAT_AUTOCONF_INCLUDED
#define COMPAT_AUTOCONF_INCLUDED
/*
* Automatically generated C config: don't edit
*/
EOF
# Checks user is compiling against a kernel we support
kernel_version_req $OLDEST_KERNEL_SUPPORTED
# For each CONFIG_FOO=x option
for i in $(egrep '^CONFIG_|^ifdef CONFIG_|^ifndef CONFIG_|^endif #CONFIG_|^else #CONFIG_' $COMPAT_CONFIG | sed 's/ /+/'); do
case $i in
'ifdef+CONFIG_'* )
echo "#$i" | sed -e 's/+/ /' -e 's/\(ifdef CONFIG_COMPAT_KERNEL_3_\)\([0-9]*\)/if (LINUX_VERSION_CODE < KERNEL_VERSION(3,\2,0))/' -e 's/\(ifdef CONFIG_COMPAT_KERNEL_2_6_\)\([0-9]*\)/if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,\2))/' -e 's/\(ifdef CONFIG_COMPAT_RHEL_\)\([0-9]*\)_\([0-9]*\)/if (defined(RHEL_MAJOR) \&\& RHEL_MAJOR == \2 \&\& RHEL_MINOR >= \3)/' -e 's/\(#ifdef \)\(CONFIG_[^:space:]*\)/#if defined(\2) || defined(\2_MODULE)/'
continue
;;
'ifndef+CONFIG_'* )
echo "#$i" | sed -e 's/+/ /' -e 's/\(ifndef CONFIG_COMPAT_KERNEL_3_\)\([0-9]*\)/if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,\2,0))/' -e 's/\(ifndef CONFIG_COMPAT_KERNEL_2_6_\)\([0-9]*\)/if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,\2))/' -e 's/\(ifndef CONFIG_COMPAT_RHEL_\)\([0-9]*\)_\([0-9]*\)/if (!defined(RHEL_MAJOR) || RHEL_MAJOR != \2 || RHEL_MINOR < \3)/' -e 's/\(#ifndef \)\(CONFIG_[^:space:]*\)/#if !defined(\2) \&\& !defined(\2_MODULE)/'
continue
;;
'else+#CONFIG_'* | 'endif+#CONFIG_'* )
echo "#$i */" |sed -e 's/+#/ \/* /g'
continue
;;
CONFIG_* )
# Get the element on the left of the "="
VAR=$(echo $i | cut -d"=" -f 1)
# Get the element on the right of the "="
VALUE=$(echo $i | cut -d"=" -f 2)
# Any other module which can *definitely* be built as a module goes here
define_config $VAR $VALUE
continue
;;
esac
done
echo "#endif /* COMPAT_AUTOCONF_INCLUDED */"
+81
View File
@@ -0,0 +1,81 @@
#!/bin/bash
# Copyright 2012 Luis R. Rodriguez <mcgrof@frijolero.org>
# Copyright 2012 Hauke Mehrtens <hauke@hauke-m.de>
#
# This generates a bunch of CONFIG_COMPAT_KERNEL_2_6_22
# CONFIG_COMPAT_KERNEL_3_0 .. etc for each kernel release you need an object
# for.
#
# Note: this is part of the compat.git project, not compat-drivers,
# send patches against compat.git.
if [[ ! -f ${KLIB_BUILD}/Makefile ]]; then
exit
fi
# Actual kernel version
KERNEL_VERSION=$(${MAKE} -C ${KLIB_BUILD} kernelversion | sed -n 's/^\([0-9]\)\..*/\1/p')
# 3.0 kernel stuff
COMPAT_LATEST_VERSION="7"
KERNEL_SUBLEVEL="-1"
# Note that this script will export all variables explicitly,
# trying to export all with a blanket "export" statement at
# the top of the generated file causes the build to slow down
# by an order of magnitude.
if [[ ${KERNEL_VERSION} -eq "3" ]]; then
KERNEL_SUBLEVEL=$(${MAKE} -C ${KLIB_BUILD} kernelversion | sed -n 's/^3\.\([0-9]\+\).*/\1/p')
else
COMPAT_26LATEST_VERSION="39"
KERNEL_26SUBLEVEL=$(${MAKE} -C ${KLIB_BUILD} kernelversion | sed -n 's/^2\.6\.\([0-9]\+\).*/\1/p')
let KERNEL_26SUBLEVEL=${KERNEL_26SUBLEVEL}+1
for i in $(seq ${KERNEL_26SUBLEVEL} ${COMPAT_26LATEST_VERSION}); do
eval CONFIG_COMPAT_KERNEL_2_6_${i}=y
echo "export CONFIG_COMPAT_KERNEL_2_6_${i}=y"
done
fi
let KERNEL_SUBLEVEL=${KERNEL_SUBLEVEL}+1
for i in $(seq ${KERNEL_SUBLEVEL} ${COMPAT_LATEST_VERSION}); do
eval CONFIG_COMPAT_KERNEL_3_${i}=y
echo "export CONFIG_COMPAT_KERNEL_3_${i}=y"
done
# The purpose of these seem to be the inverse of the above other varibales.
# The RHEL checks seem to annotate the existance of RHEL minor versions.
RHEL_MAJOR=$(grep ^RHEL_MAJOR ${KLIB_BUILD}/Makefile | sed -n 's/.*= *\(.*\)/\1/p')
if [[ ! -z ${RHEL_MAJOR} ]]; then
RHEL_MINOR=$(grep ^RHEL_MINOR ${KLIB_BUILD}/Makefile | sed -n 's/.*= *\(.*\)/\1/p')
for i in $(seq 0 ${RHEL_MINOR}); do
eval CONFIG_COMPAT_RHEL_${RHEL_MAJOR}_${i}=y
echo "export CONFIG_COMPAT_RHEL_${RHEL_MAJOR}_${i}=y"
done
fi
if [[ ${CONFIG_COMPAT_KERNEL_2_6_33} = "y" ]]; then
if [[ ! ${CONFIG_COMPAT_RHEL_6_0} = "y" ]]; then
echo "export CONFIG_COMPAT_FIRMWARE_CLASS=m"
fi
fi
if [[ ${CONFIG_COMPAT_KERNEL_2_6_36} = "y" ]]; then
if [[ ! ${CONFIG_COMPAT_RHEL_6_1} = "y" ]]; then
echo "export CONFIG_COMPAT_KFIFO=y"
fi
fi
if [[ ${CONFIG_COMPAT_KERNEL_3_5} = "y" ]]; then
# We don't have 2.6.24 backport support yet for Codel / FQ CoDel
# For those who want to try this is what is required that I can tell
# so far:
# * struct Qdisc_ops
# - init and change callback ops use a different argument dataype
# - you need to parse data received from userspace differently
if [[ ${CONFIG_COMPAT_KERNEL_2_6_25} != "y" ]]; then
echo "export CONFIG_COMPAT_NET_SCH_CODEL=m"
echo "export CONFIG_COMPAT_NET_SCH_FQ_CODEL=m"
fi
fi