📄 dell_rbu.c
字号:
* Re-initialize the rbu_data variables after a free */ rbu_data.image_update_ordernum = -1; rbu_data.image_update_buffer = NULL; rbu_data.image_update_buffer_size = 0; rbu_data.bios_image_size = 0; rbu_data.dma_alloc = 0;}/* * img_update_realloc: This function allocates the contiguous pages to * accommodate the requested size of data. The memory address and size * values are stored globally and on every call to this function the new * size is checked to see if more data is required than the existing size. * If true the previous memory is freed and new allocation is done to * accommodate the new size. If the incoming size is less then than the * already allocated size, then that memory is reused. This function is * called with lock held and returns with lock held. */static int img_update_realloc(unsigned long size){ unsigned char *image_update_buffer = NULL; unsigned long rc; unsigned long img_buf_phys_addr; int ordernum; int dma_alloc = 0; /* * check if the buffer of sufficient size has been * already allocated */ if (rbu_data.image_update_buffer_size >= size) { /* * check for corruption */ if ((size != 0) && (rbu_data.image_update_buffer == NULL)) { printk(KERN_ERR "dell_rbu:%s: corruption " "check failed\n", __FUNCTION__); return -EINVAL; } /* * we have a valid pre-allocated buffer with * sufficient size */ return 0; } /* * free any previously allocated buffer */ img_update_free(); spin_unlock(&rbu_data.lock); ordernum = get_order(size); image_update_buffer = (unsigned char *) __get_free_pages(GFP_KERNEL, ordernum); img_buf_phys_addr = (unsigned long) virt_to_phys(image_update_buffer); if (img_buf_phys_addr > BIOS_SCAN_LIMIT) { free_pages((unsigned long) image_update_buffer, ordernum); ordernum = -1; image_update_buffer = dma_alloc_coherent(NULL, size, &dell_rbu_dmaaddr, GFP_KERNEL); dma_alloc = 1; } spin_lock(&rbu_data.lock); if (image_update_buffer != NULL) { rbu_data.image_update_buffer = image_update_buffer; rbu_data.image_update_buffer_size = size; rbu_data.bios_image_size = rbu_data.image_update_buffer_size; rbu_data.image_update_ordernum = ordernum; rbu_data.dma_alloc = dma_alloc; rc = 0; } else { pr_debug("Not enough memory for image update:" "size = %ld\n", size); rc = -ENOMEM; } return rc;}static ssize_t read_packet_data(char *buffer, loff_t pos, size_t count){ int retval; size_t bytes_left; size_t data_length; char *ptempBuf = buffer; /* check to see if we have something to return */ if (rbu_data.num_packets == 0) { pr_debug("read_packet_data: no packets written\n"); retval = -ENOMEM; goto read_rbu_data_exit; } if (pos > rbu_data.imagesize) { retval = 0; printk(KERN_WARNING "dell_rbu:read_packet_data: " "data underrun\n"); goto read_rbu_data_exit; } bytes_left = rbu_data.imagesize - pos; data_length = min(bytes_left, count); if ((retval = packet_read_list(ptempBuf, &data_length)) < 0) goto read_rbu_data_exit; if ((pos + count) > rbu_data.imagesize) { rbu_data.packet_read_count = 0; /* this was the last copy */ retval = bytes_left; } else retval = count; read_rbu_data_exit: return retval;}static ssize_t read_rbu_mono_data(char *buffer, loff_t pos, size_t count){ unsigned char *ptemp = NULL; size_t bytes_left = 0; size_t data_length = 0; ssize_t ret_count = 0; /* check to see if we have something to return */ if ((rbu_data.image_update_buffer == NULL) || (rbu_data.bios_image_size == 0)) { pr_debug("read_rbu_data_mono: image_update_buffer %p ," "bios_image_size %lu\n", rbu_data.image_update_buffer, rbu_data.bios_image_size); ret_count = -ENOMEM; goto read_rbu_data_exit; } if (pos > rbu_data.bios_image_size) { ret_count = 0; goto read_rbu_data_exit; } bytes_left = rbu_data.bios_image_size - pos; data_length = min(bytes_left, count); ptemp = rbu_data.image_update_buffer; memcpy(buffer, (ptemp + pos), data_length); if ((pos + count) > rbu_data.bios_image_size) /* this was the last copy */ ret_count = bytes_left; else ret_count = count; read_rbu_data_exit: return ret_count;}static ssize_t read_rbu_data(struct kobject *kobj, char *buffer, loff_t pos, size_t count){ ssize_t ret_count = 0; spin_lock(&rbu_data.lock); if (!strcmp(image_type, "mono")) ret_count = read_rbu_mono_data(buffer, pos, count); else if (!strcmp(image_type, "packet")) ret_count = read_packet_data(buffer, pos, count); else pr_debug("read_rbu_data: invalid image type specified\n"); spin_unlock(&rbu_data.lock); return ret_count;}static void callbackfn_rbu(const struct firmware *fw, void *context){ int rc = 0; if (!fw || !fw->size) { rbu_data.entry_created = 0; return; } spin_lock(&rbu_data.lock); if (!strcmp(image_type, "mono")) { if (!img_update_realloc(fw->size)) memcpy(rbu_data.image_update_buffer, fw->data, fw->size); } else if (!strcmp(image_type, "packet")) { /* * we need to free previous packets if a * new hunk of packets needs to be downloaded */ packet_empty_list(); if (packetize_data(fw->data, fw->size)) /* Incase something goes wrong when we are * in middle of packetizing the data, we * need to free up whatever packets might * have been created before we quit. */ packet_empty_list(); } else pr_debug("invalid image type specified.\n"); spin_unlock(&rbu_data.lock); rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, "dell_rbu", &rbu_device->dev, &context, callbackfn_rbu); if (rc) printk(KERN_ERR "dell_rbu:%s request_firmware_nowait failed" " %d\n", __FUNCTION__, rc); else rbu_data.entry_created = 1;}static ssize_t read_rbu_image_type(struct kobject *kobj, char *buffer, loff_t pos, size_t count){ int size = 0; if (!pos) size = sprintf(buffer, "%s\n", image_type); return size;}static ssize_t write_rbu_image_type(struct kobject *kobj, char *buffer, loff_t pos, size_t count){ int rc = count; int req_firm_rc = 0; int i; spin_lock(&rbu_data.lock); /* * Find the first newline or space */ for (i = 0; i < count; ++i) if (buffer[i] == '\n' || buffer[i] == ' ') { buffer[i] = '\0'; break; } if (i == count) buffer[count] = '\0'; if (strstr(buffer, "mono")) strcpy(image_type, "mono"); else if (strstr(buffer, "packet")) strcpy(image_type, "packet"); else if (strstr(buffer, "init")) { /* * If due to the user error the driver gets in a bad * state where even though it is loaded , the * /sys/class/firmware/dell_rbu entries are missing. * to cover this situation the user can recreate entries * by writing init to image_type. */ if (!rbu_data.entry_created) { spin_unlock(&rbu_data.lock); req_firm_rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, "dell_rbu", &rbu_device->dev, &context, callbackfn_rbu); if (req_firm_rc) { printk(KERN_ERR "dell_rbu:%s request_firmware_nowait" " failed %d\n", __FUNCTION__, rc); rc = -EIO; } else rbu_data.entry_created = 1; spin_lock(&rbu_data.lock); } } else { printk(KERN_WARNING "dell_rbu: image_type is invalid\n"); spin_unlock(&rbu_data.lock); return -EINVAL; } /* we must free all previous allocations */ packet_empty_list(); img_update_free(); spin_unlock(&rbu_data.lock); return rc;}static ssize_t read_rbu_packet_size(struct kobject *kobj, char *buffer, loff_t pos, size_t count){ int size = 0; if (!pos) { spin_lock(&rbu_data.lock); size = sprintf(buffer, "%lu\n", rbu_data.packetsize); spin_unlock(&rbu_data.lock); } return size;}static ssize_t write_rbu_packet_size(struct kobject *kobj, char *buffer, loff_t pos, size_t count){ unsigned long temp; spin_lock(&rbu_data.lock); packet_empty_list(); sscanf(buffer, "%lu", &temp); if (temp < 0xffffffff) rbu_data.packetsize = temp; spin_unlock(&rbu_data.lock); return count;}static struct bin_attribute rbu_data_attr = { .attr = {.name = "data",.owner = THIS_MODULE,.mode = 0444}, .read = read_rbu_data,};static struct bin_attribute rbu_image_type_attr = { .attr = {.name = "image_type",.owner = THIS_MODULE,.mode = 0644}, .read = read_rbu_image_type, .write = write_rbu_image_type,};static struct bin_attribute rbu_packet_size_attr = { .attr = {.name = "packet_size",.owner = THIS_MODULE,.mode = 0644}, .read = read_rbu_packet_size, .write = write_rbu_packet_size,};static int __init dcdrbu_init(void){ int rc = 0; spin_lock_init(&rbu_data.lock); init_packet_head(); rbu_device = platform_device_register_simple("dell_rbu", -1, NULL, 0); if (!rbu_device) { printk(KERN_ERR "dell_rbu:%s:platform_device_register_simple " "failed\n", __FUNCTION__); return -EIO; } sysfs_create_bin_file(&rbu_device->dev.kobj, &rbu_data_attr); sysfs_create_bin_file(&rbu_device->dev.kobj, &rbu_image_type_attr); sysfs_create_bin_file(&rbu_device->dev.kobj, &rbu_packet_size_attr); rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, "dell_rbu", &rbu_device->dev, &context, callbackfn_rbu); if (rc) printk(KERN_ERR "dell_rbu:%s:request_firmware_nowait" " failed %d\n", __FUNCTION__, rc); else rbu_data.entry_created = 1; return rc;}static __exit void dcdrbu_exit(void){ spin_lock(&rbu_data.lock); packet_empty_list(); img_update_free(); spin_unlock(&rbu_data.lock); platform_device_unregister(rbu_device);}module_exit(dcdrbu_exit);module_init(dcdrbu_init);/* vim:noet:ts=8:sw=8*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -