719 lines · 17.2 KB · C
RawDownload
/* * 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 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 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,				     struct class_attribute *attr,				     char *buf){	return sprintf(buf, "%d\n", loading_timeout);} /** * firmware_timeout_store - set number of seconds to wait for firmware * @class: device class pointer * @attr: device attribute 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,				      struct class_attribute *attr,				      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);} 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;} static struct class firmware_class = {	.name		= "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){	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);	}} /* 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 * @attr: device attribute 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' */			fw_priv->fw->pages = fw_priv->pages;			fw_priv->pages = NULL; 			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); 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){	struct device *dev = 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 * @filp: open sysfs file * @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. **/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){	struct device *dev = 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:

Lines 1–500 of 719.