Newer
Older
/*
* firmware_class.c - Multi purpose firmware loading support
*
* Copyright (c) 2003 Manuel Estrada Sainz
*
* Please see Documentation/firmware_class/ for more information.
*
*/
#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/workqueue.h>
#include <linux/slab.h>
#include <linux/sched.h>

Linus Torvalds
committed
#include <linux/file.h>
#include <linux/list.h>
#include <linux/async.h>
#include <linux/pm.h>
#include <linux/suspend.h>
#include <linux/syscore_ops.h>

Linus Torvalds
committed
#include <generated/utsrelease.h>
MODULE_AUTHOR("Manuel Estrada Sainz");
MODULE_DESCRIPTION("Multi purpose firmware loading support");
MODULE_LICENSE("GPL");

Linus Torvalds
committed
static const char *fw_path[] = {
"/lib/firmware/updates/" UTS_RELEASE,
"/lib/firmware/updates",
"/lib/firmware/" UTS_RELEASE,
"/lib/firmware"
};
/* Don't inline this: 'struct kstat' is biggish */
static noinline long fw_file_size(struct file *file)
{
struct kstat st;
if (vfs_getattr(file->f_path.mnt, file->f_path.dentry, &st))
return -1;
if (!S_ISREG(st.mode))
return -1;
if (st.size != (long)st.size)
return -1;
return st.size;
}
static bool fw_read_file_contents(struct file *file, struct firmware *fw)
{
long size;
char *buf;
size = fw_file_size(file);
if (size < 0)
return false;
buf = vmalloc(size);
if (!buf)
return false;
if (kernel_read(file, 0, buf, size) != size) {

Linus Torvalds
committed
vfree(buf);
return false;
}
fw->data = buf;
fw->size = size;
return true;
}
static bool fw_get_filesystem_firmware(struct firmware *fw, const char *name)
{
int i;
bool success = false;
char *path = __getname();
for (i = 0; i < ARRAY_SIZE(fw_path); i++) {
struct file *file;
snprintf(path, PATH_MAX, "%s/%s", fw_path[i], name);
file = filp_open(path, O_RDONLY, 0);
if (IS_ERR(file))
continue;
success = fw_read_file_contents(file, fw);
fput(file);
if (success)
break;
}
__putname(path);
return success;
}
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/* Builtin firmware support */
#ifdef CONFIG_FW_LOADER
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 */
static inline long firmware_loading_timeout(void)
{
return loading_timeout > 0 ? loading_timeout * HZ : MAX_SCHEDULE_TIMEOUT;
}
struct firmware_cache {
/* firmware_buf instance will be added into the below list */
spinlock_t lock;
struct list_head head;
/*
* Names of firmware images which have been cached successfully
* will be added into the below list so that device uncache
* helper can trace which firmware images have been cached
* before.
*/
spinlock_t name_lock;
struct list_head fw_names;
wait_queue_head_t wait_queue;
int cnt;
struct delayed_work work;
struct notifier_block pm_notify;
struct kref ref;
struct list_head list;
struct firmware_cache *fwc;
void *data;
size_t size;
struct page **pages;
int nr_pages;
int page_array_size;
struct fw_cache_entry {
struct list_head list;
char name[];
Loading
Loading full blame...