[patch 2.6.12-rc3] dell_rbu: Resubmitting patch for new Dell BIOS update driver

From: Abhay Salunke (Abhay_Salunke_at_dell.com)
Date: 06/02/05

  • Next message: Martin J. Bligh: "Re: Avoiding external fragmentation with a placement policy Version 12"
    Date:	Thu, 2 Jun 2005 13:36:48 -0500
    To: linux-kernel@vger.kernel.org, Andrew Morton <akpm@osdl.org>
    
    

    This is a resubmit of the patch after incorporating all the inputs from revieweres.
    This has the hotplug firmware interface as suggested by many.
    Currently it does not suport reading back the data; I am workingon it and will add
    that feature as new patch.

    By making a contribution to this project, I certify that:
    The contribution was created in whole or in part by me and I have the
    right to submit it under the open source license indicated in the file.

    Signed-off-by: Abhay Salunke <Abhay_Salunke@dell.com>

    Thanks,
    Abhay Salunke
    Software Engineer.
    DELL Inc

    diff -uprN linux-2.6.11.8.ORIG/Documentation/DELL_RBU.txt linux-2.6.11.8/Documentation/DELL_RBU.txt
    --- linux-2.6.11.8.ORIG/Documentation/DELL_RBU.txt 1969-12-31 18:00:00.000000000 -0600
    +++ linux-2.6.11.8/Documentation/DELL_RBU.txt 2005-06-02 13:23:36.358562176 -0500
    @@ -0,0 +1,70 @@
    +Purpose:
    +Demonstrate the usage of the DELL_RBU (DELL Remote BIOS Update) driver
    +for updating BIOS images on Dell hardware.
    +
    +Scope:
    +This document discusses the functionality of the DELL_RBU driver.
    +This driver is required by BIOS update applications shipped by DELL for updating
    +BIOS on DELL servers and client systems.
    +
    +Overview:
    +The rbu driver is designed to be running on 2.6 kernel.
    +This driver is one single dell_rbu.c file (approx 800 lines total).
    +This driver utilizes the hotplug interface for downloading the BIOS update image
    +in the contiguous or packetized memory depending upon the update type.
    +The BIOS then scans the memory to find the image and will then update itself.
    +There are basically two different mechanisms for writing the BIOS image in to
    +contiguous memory
    +1> By writing the image to one monolithic chunk of contiguous physical memory.
    +2> By writing image in to smaller packet chunks of contiguous physical memory.
    +The update mechanism is determined by the update application based on the
    +particular system type.
    +
    +Update mechanism using single physical chunk of memory:
    +The rbu driver on its load time created the following entries in sysfs
    +/sys/firmware/dell_rbu/monolithic/mono_name
    +/sys/firmware/dell_rbu/monolithic/mono_size
    +/sys/firmware/dell_rbu/packetized/packet_name
    +/sys/firmware/dell_rbu/packetized/packet_size
    +
    +Steps to update the BIOS image:
    +
    +1> Copy the image file in to /lib/firmware
    +2> echo the image name in to /sys/firmware/dell_rbu/xxxx/xxxx_name
    +
    +This will generate a hotplug event and the data form the image file is
    +transferred to the memory.
    +Here xxxx stands for the type of BIOS update mechanism chosen by choosing
    +monolitich the image is copied to contiguous physical pages and by choosing
    +the mechanism as packetized the image is treated as one single packet and the
    +packet size is set to the first packet image size. If a new image packet of
    +different size form the previous is copied then all the previous packets are
    +freed and this packet's size is treated as new packet size.
    +
    +On a driver unload all the allocated memory is freed.
    +The user should not unload the driver after downloading the new BIOS image
    +if it wants to update BIOS with that image.
    +The user can overwrite the previously loaded monolithich image by echoing a
    +new file name string to /sys/firmware/dell_rbu/monolithic/mono_name. Make sure
    +the file is present in /lib/firmware. If the image size is more than
    +previous image then the previous image is freed and the new alocation is made.
    +
    +The user can know of a successful allocation by readind the size files.
    +cat /sys/firmware/dell_rbu/monolithic/mono_size
    +
    +Update using smaller chunks (packets) of contiguous memory:
    +The disadvantage of contiguous allocation is that it may not be always possible
    +to get that size of contiuguous chunk of avaliable physical pages as in most
    +Linux systems the memory gets fragmented immideately after a reboot.
    +The update using smaller chunks fixes this issue; it also requires the BIOS on
    +the system to support this feature; the update application needs to query this
    +with the BIOS on the system before using this technique.
    +
    +
    +NOTE:
    +Afte updating the BIOS image the appplication needs to communicate with the BIOS
    +for enabling the update on the next reboot. The application can then choose to
    +reboot the system imideately or not reboot the system and leave up to the user
    +to do a reboot.
    +
    +
    diff -uprN linux-2.6.11.8.ORIG/drivers/firmware/dell_rbu.c linux-2.6.11.8/drivers/firmware/dell_rbu.c
    --- linux-2.6.11.8.ORIG/drivers/firmware/dell_rbu.c 1969-12-31 18:00:00.000000000 -0600
    +++ linux-2.6.11.8/drivers/firmware/dell_rbu.c 2005-06-02 13:28:48.821060656 -0500
    @@ -0,0 +1,700 @@
    +/*
    + * dell_rbu.c
    + * Bios Update driver for Dell systems
    + * Author: Dell Inc
    + * Abhay Salunke <abhay_salunke@dell.com>
    + *
    + * Copyright (C) 2005 Dell Inc.
    + *
    + * Remote BIOS Update (rbu) driver is used for updating DELL BIOS by creating
    + * entries in the /sys file systems on Linux 2.6 and higher kernels.
    + * It uses the hotplug interface for getting the image in to memory.
    + * The driver supports two mechanism to update the BIOS namely contiguous and
    + * packetized. Both these methods still require to have some application to set
    + * the CMOS bit indicating the BIOS to update itself after a reboot.
    + * In both the methods the image file name needs to be specified for hotplugging
    + * the image file.
    + *
    + * Contiguous method:
    + * This driver writes the incmoing data in a monolithic image by allocating
    + * contiguos physical pages large enough to accomodate the incoming BIOS image
    + * size.
    + *
    + * Packetized method:
    + * The driver writes the incoming packet image by allocating a new packet on
    + * every time the packet image name is written.
    + * This driver requires an application to break the BIOS image in to fixed sized
    + * packet chunks and each packet is written as a hotplug image.
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License v2.0 as published by
    + * the Free Software Foundation
    + *
    + * 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.
    + */
    +#include <linux/version.h>
    +#include <linux/config.h>
    +#include <linux/init.h>
    +#include <linux/module.h>
    +#include <linux/string.h>
    +#include <linux/errno.h>
    +#include <linux/blkdev.h>
    +#include <linux/device.h>
    +#include <linux/spinlock.h>
    +#include <linux/moduleparam.h>
    +#include "linux/firmware.h"
    +#define BIOS_SCAN_LIMIT 0xffffffff
    +MODULE_AUTHOR("Abhay Salunke <abhay_salunke@dell.com>");
    +MODULE_DESCRIPTION("Driver for updating BIOS image on DELL systems");
    +MODULE_LICENSE("GPL");
    +MODULE_VERSION("1.0");
    +
    +#define MONOLITHIC (1)
    +#define PACKETIZED (10)
    +static struct _rbu_data {
    + void *image_update_buffer;
    + unsigned long image_update_buffer_size;
    + unsigned long bios_image_size;
    + unsigned long image_update_order_number;
    + spinlock_t lock;
    + unsigned long packet_read_count;
    + unsigned long packet_write_count;
    + unsigned long num_packets;
    + unsigned long packetsize;
    + char mono_image[256];
    + char packet_image[256];
    +} rbu_data;
    +
    +struct packet_data{
    + struct list_head list;
    + size_t length;
    + void *data;
    + int ordernum;
    +};
    +
    +static struct packet_data packet_data_head;
    +
    +
    +static void init_packet_head(void)
    +{
    + INIT_LIST_HEAD(&packet_data_head.list);
    + rbu_data.packet_write_count = 0;
    + rbu_data.packet_read_count = 0;
    + rbu_data.num_packets = 0;
    + rbu_data.packetsize = 0;
    +}
    +
    +struct rbu_download_device {
    + int type;
    + struct kobject kobj;
    +};
    +
    +static struct rbu_download_device *rbu_download_mono;
    +
    +static struct rbu_download_device *rbu_download_packet;
    +
    +static struct device rbu_device;
    +
    +static int fill_last_packet(void *data, size_t length)
    +{
    + struct list_head *ptemp_list;
    + struct packet_data *ppacket = NULL;
    + int packet_count = 0;
    +
    + pr_debug("fill_last_packet: entry \n");
    +
    + /* check if we have any packets */
    + if (0 == rbu_data.num_packets) {
    + pr_debug("fill_last_packet: num_packets=0\n");
    + return -ENOMEM;
    + }
    +
    + packet_count = rbu_data.num_packets;
    +
    + ptemp_list = (&packet_data_head.list)->next;
    +
    + while(--packet_count) {
    + ptemp_list = ptemp_list->next;
    + }
    +
    + ppacket = list_entry(ptemp_list,struct packet_data, list);
    +
    + if ((rbu_data.packet_write_count + length) > rbu_data.packetsize) {
    + printk(KERN_WARNING "fill_last_packet: packet size data "
    + "overrun\n");
    + return -ENOMEM;
    + }
    +
    + pr_debug("fill_last_packet : buffer = %p\n", ppacket->data);
    +
    + /* copy the incoming data in to the new buffer */
    + memcpy((ppacket->data + rbu_data.packet_write_count),
    + data, length);
    +
    + if ((rbu_data.packet_write_count + length) == rbu_data.packetsize) {
    + /*
    + this was the last data chunk in the packet
    + so reinitialize the packet data counter to zero
    + */
    + rbu_data.packet_write_count = 0;
    + } else {
    + rbu_data.packet_write_count += length;
    + }
    + pr_debug("fill_last_packet: exit \n");
    + return 0;
    +}
    +
    +/*
    + get_free_pages_limited:
    + This is a helper function which allocates free pages based on an upper limit.
    + On x86_64 or 64 bit arch the memory allocation goes above 4GB space which is
    + not addressable by the BIOS. This function tries to get allocation below the
    + limit (4GB) address. It first tries to allocate memory normally using the
    + GFP_KERNEL argument if the incoming limit is non-zero and if the returned
    + physical memory address exceeds the upper limit, the allocated pages are freed
    + and the memory is reallocated using the GFP_DMA argument.
    +*/
    +static void *get_free_pages_limited(unsigned long size,
    + int *ordernum,
    + unsigned long limit)
    +{
    + unsigned long img_buf_phys_addr;
    + void *pbuf = NULL;
    +
    + *ordernum = get_order(size);
    + /*
    + Check if we are not getting a very large file.
    + This can happen as a user error in entering the file size
    + */
    + if (*ordernum == BITS_PER_LONG) {
    + pr_debug("get_free_pages_limited: Incoming size is"
    + " very large\n");
    + return NULL;
    + }
    +
    + /* try allocating a new buffer to fit the request */
    + pbuf =(unsigned char *)__get_free_pages(GFP_KERNEL, *ordernum);
    +
    + if (pbuf != NULL) {
    + /* check if the image is with in limits */
    + img_buf_phys_addr = (unsigned long)virt_to_phys(pbuf);
    +
    + if ((limit != 0) && ((img_buf_phys_addr + size) > limit)) {
    + pr_debug("Got memory above 4GB range, free this "
    + "and try with DMA memory\n");
    + /* free this memory as we need it with in 4GB range */
    + free_pages ((unsigned long)pbuf, *ordernum);
    + /*
    + Try allocating a new buffer from the GFP_DMA range
    + as it is with in 16MB range.
    + */
    + pbuf =(unsigned char *)__get_free_pages(GFP_DMA,
    + *ordernum);
    + if (pbuf == NULL)
    + pr_debug("Failed to get memory of size %ld "
    + "using GFP_DMA\n", size);
    + }
    + }
    + return pbuf;
    +}
    +
    +static int create_packet(size_t length)
    +{
    + struct packet_data *newpacket;
    + int ordernum = 0;
    +
    + pr_debug("create_packet: entry \n");
    +
    + if (rbu_data.packetsize == 0 ) {
    + pr_debug("create_packet: packetsize not specified\n");
    + return -EINVAL;
    + }
    +
    + newpacket = kmalloc(sizeof(struct packet_data) ,GFP_KERNEL);
    + if(newpacket == NULL) {
    + printk(KERN_WARNING "create_packet: failed to allocate new "
    + "packet\n");
    + return -ENOMEM;
    + }
    +
    + /* there is no upper limit on memory address for packetized mechanism*/
    + newpacket->data = get_free_pages_limited(rbu_data.packetsize,
    + &ordernum, 0);
    + pr_debug("create_packet: newpacket %p\n", newpacket->data);
    +
    + if(newpacket->data == NULL) {
    + printk(KERN_WARNING "create_packet: failed to allocate new "
    + "packet\n");
    + kfree(newpacket);
    + return -ENOMEM;
    + }
    +
    + newpacket->ordernum = ordernum;
    + ++rbu_data.num_packets;
    + /* initialize the newly created packet headers */
    + INIT_LIST_HEAD(&newpacket->list);
    + list_add_tail(&newpacket->list, &packet_data_head.list);
    + /* packets have fixed size*/
    + newpacket->length = rbu_data.packetsize;
    +
    + pr_debug("create_packet: exit \n");
    +
    + return 0;
    +}
    +
    +static int packetize_data(void *data, size_t length)
    +{
    + int rc = 0;
    +
    + if (rbu_data.packet_write_count == 0) {
    + if ((rc = create_packet(length)) != 0 )
    + return rc;
    + }
    + /* fill data in to the packet */
    + if ((rc = fill_last_packet(data, length)) != 0)
    + return rc;
    +
    + return rc;
    +}
    +
    +/*
    + do_packet_read :
    + This is a helper function which reads the packet data of the
    + current list.
    + data: is the incoming buffer
    + ptemp_list: points to the incoming list item
    + length: is the length of the free space in the buffer.
    + bytes_read: is the total number of bytes read already from
    + the packet list
    + list_read_count: is the counter to keep track of the number
    + of bytes read out of each packet.
    +*/
    +int do_packet_read(char *data,
    + struct list_head *ptemp_list,
    + int length,
    + int bytes_read,
    + int *list_read_count)
    +{
    + void *ptemp_buf;
    + struct packet_data *newpacket = NULL;
    + int bytes_copied = 0;
    + int j = 0;
    +
    + newpacket = list_entry(ptemp_list,struct packet_data, list);
    + *list_read_count += newpacket->length;
    +
    + if (*list_read_count > bytes_read) {
    + /* point to the start of unread data */
    + j = newpacket->length - (*list_read_count - bytes_read);
    + /* point to the offset in the packet buffer*/
    + ptemp_buf = (u8 *)newpacket->data + j;
    + /* check if there is enough room in the incoming buffer*/
    + if (length > (*list_read_count - bytes_read))
    + /* copy what ever is there in this packet and move on*/
    + bytes_copied = (*list_read_count - bytes_read);
    + else
    + /* copy the remaining */
    + bytes_copied = length;
    + memcpy(data, ptemp_buf, bytes_copied);
    + }
    + return bytes_copied;
    +}
    +
    +static void packet_empty_list(void)
    +{
    + struct list_head *ptemp_list;
    + struct list_head *pnext_list;
    + struct packet_data *newpacket;
    +
    + ptemp_list = (&packet_data_head.list)->next;
    + while(!list_empty(ptemp_list)) {
    + newpacket = list_entry(ptemp_list, struct packet_data, list);
    + pnext_list = ptemp_list->next;
    + list_del(ptemp_list);
    + ptemp_list = pnext_list;
    + /*
    + zero out the RBU packet memory before freeing to make sure
    + there are no stale RBU packets left in memory
    + */
    + memset(newpacket->data, 0, rbu_data.packetsize);
    + free_pages((unsigned long)newpacket->data, newpacket->ordernum);
    + kfree(newpacket);
    + }
    + rbu_data.packet_write_count = 0;
    + rbu_data.packet_read_count = 0;
    + rbu_data.num_packets = 0;
    +}
    +
    +/*
    + img_update_free:
    + Frees the buffer allocated for storing BIOS image
    + Always called with lock held and returned with lock held
    +*/
    +static void img_update_free( void)
    +{
    + if (rbu_data.image_update_buffer == NULL)
    + return;
    +
    + /*
    + zero out this buffer before freeing it to get rid of any stale
    + BIOS image copied in memory.
    + */
    + memset(rbu_data.image_update_buffer, 0,
    + rbu_data.image_update_buffer_size);
    + free_pages((unsigned long)rbu_data.image_update_buffer,
    + rbu_data.image_update_order_number);
    + /* Re-initialize the rbu_data variables after a free */
    + rbu_data.image_update_buffer = NULL;
    + rbu_data.image_update_buffer_size = 0;
    + rbu_data.bios_image_size = 0;
    +}
    +
    +/*
    + img_update_realloc:
    + This function allocates the contiguous pages to accomodate 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 accomodate 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 returna with lock held.
    +*/
    +static int img_update_realloc(unsigned long size)
    +{
    + unsigned char *image_update_buffer = NULL;
    + unsigned long rc;
    + int ordernum =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 "img_update_realloc: corruption check "
    + "failed\n");
    + return -EINVAL;
    + }
    + /* we have a valid pre-allocated buffer with sufficient size */
    + return 0;
    + }
    +
    + /* free any previously allocated buffer */
    + img_update_free();
    +
    + /*
    + This has already been called as locked so we can now unlock
    + and proceed to calling get_free_pages_limited as this function
    + can sleep
    + */
    + spin_unlock(&rbu_data.lock);
    +
    + image_update_buffer = (unsigned char *)get_free_pages_limited(size,
    + &ordernum,
    + BIOS_SCAN_LIMIT);
    +
    + /* acquire the spinlock again */
    + spin_lock(&rbu_data.lock);
    +
    + if (image_update_buffer != NULL) {
    + rbu_data.image_update_buffer = image_update_buffer;
    + rbu_data.image_update_buffer_size = PAGE_SIZE << ordernum;
    + rbu_data.image_update_order_number = ordernum;
    + memset(rbu_data.image_update_buffer,0,
    + rbu_data.image_update_buffer_size);
    + rc = 0;
    + } else {
    + pr_debug("Not enough memory for image update:order number = %d"
    + ",size = %ld\n",ordernum, size);
    + rc = -ENOMEM;
    + }
    +
    + return rc;
    +} /* img_update_realloc */
    +
    +static ssize_t dummy_store (struct rbu_download_device *rbu_dev,
    + const char *buf, size_t count, int type)
    +{
    + /* do nothing */
    + return count;
    +}
    +
    +static ssize_t rbu_show_image_size (struct rbu_download_device *rbu_dev,
    + char *buf, int type)
    +{
    + unsigned int size = 0;
    + if ( type == MONOLITHIC )
    + size = sprintf(buf, "%lu\n", rbu_data.bios_image_size);
    + else
    + size = sprintf(buf, "%lu\n", rbu_data.packetsize);
    + return size;
    +}
    +
    +static ssize_t rbu_show_image_name (struct rbu_download_device *rbu_dev,
    + char *buf, int type)
    +{
    + unsigned int size = 0;
    + char *image;
    +
    + if (type == MONOLITHIC )
    + image = rbu_data.mono_image;
    + else
    + image = rbu_data.packet_image;
    +
    + size = sprintf(buf, "%s\n", image);
    + return size;
    +}
    +
    +static ssize_t rbu_store_image_name (struct rbu_download_device *rbu_dev,
    + const char *buf, size_t count, int type)
    +{
    + int rc = count;
    + const struct firmware *fw_entry;
    + char *image_name = NULL;
    +
    + spin_lock(&rbu_data.lock);
    +
    + if (strlen(buf) < 256 ) {
    + if (type == MONOLITHIC ){
    + sscanf(buf, "%s",rbu_data.mono_image);
    + image_name = rbu_data.mono_image;
    + } else {
    + sscanf(buf, "%s",rbu_data.packet_image);
    + image_name = rbu_data.packet_image;
    + }
    +
    + spin_unlock(&rbu_data.lock);
    +
    + rc = request_firmware(&fw_entry, image_name,
    + &rbu_device);
    + if (rc) {
    + printk(KERN_ERR "rbu_store_image_name: "
    + "Firmware not available %d\n", rc);
    + return rc;
    + }
    +
    + pr_debug("rbu_store_image_name: "
    + "request_firmware is successful "
    + "fw->size = %lu\n", fw_entry->size);
    +
    + spin_lock(&rbu_data.lock);
    + if (type == MONOLITHIC ){
    + rbu_data.bios_image_size = fw_entry->size;
    + rc = img_update_realloc(fw_entry->size);
    + if (rc == 0) {
    + memcpy(rbu_data.image_update_buffer,
    + fw_entry->data, fw_entry->size);
    + rc = count;
    + }
    + } else {
    + /* if a new packet is entered free all previsou
    + packets and start over.
    + */
    + if ( rbu_data.packetsize != fw_entry->size )
    + packet_empty_list();
    +
    + rbu_data.packetsize = fw_entry->size;
    + rc = packetize_data(fw_entry->data, fw_entry->size);
    + if ( rc == 0 )
    + rc = count;
    + }
    + spin_unlock(&rbu_data.lock);
    + release_firmware(fw_entry);
    + } else {
    + spin_unlock(&rbu_data.lock);
    + rc = -ENOMEM;
    + }
    +
    + return rc;
    +}
    +
    +/* no default attributes yet. */
    +static struct attribute * def_attrs[] = { NULL, };
    +
    +struct rbu_attribute {
    + struct attribute attr;
    + ssize_t (*show) (struct rbu_download_device *rbu_dev,
    + char *buf, int type);
    + ssize_t (*store)(struct rbu_download_device *rbu_dev,
    + const char *buf , size_t count, int type);
    + int type;
    +};
    +
    +#define RBU_DEVICE_ATTR(_name,_mode,_show,_store, _type) \
    +struct rbu_attribute rbu_attr_##_name = { \
    + .attr ={.name= __stringify(_name), .mode= _mode, .owner= THIS_MODULE},\
    + .show = _show, \
    + .store = _store, \
    + .type = _type, \
    +};
    +
    +#define to_rbu_attr(_attr) container_of(_attr,struct rbu_attribute,attr)
    +#define to_rbu_download_device(obj) \
    + container_of(obj,struct rbu_download_device,kobj)
    +
    +
    +static RBU_DEVICE_ATTR(mono_name, 0644, rbu_show_image_name,
    + rbu_store_image_name, MONOLITHIC);
    +
    +static RBU_DEVICE_ATTR(mono_size, 0444, rbu_show_image_size,
    + dummy_store, MONOLITHIC);
    +
    +static RBU_DEVICE_ATTR(packet_name, 0644, rbu_show_image_name,
    + rbu_store_image_name, PACKETIZED);
    +
    +static RBU_DEVICE_ATTR(packet_size, 0444, rbu_show_image_size,
    + dummy_store, PACKETIZED);
    +
    +static ssize_t rbu_attr_store (struct kobject *kobj,
    + struct attribute *attr,
    + const char *buf,
    + size_t count)
    +{
    + struct rbu_download_device *rbu_dev = to_rbu_download_device(kobj);
    + struct rbu_attribute *rbu_attr = to_rbu_attr(attr);
    + ssize_t rc = count;
    +
    + pr_debug("rbu_attr_store: entry type = %d\n", rbu_attr->type);
    +
    + if (rbu_attr->store)
    + rc = rbu_attr->store(rbu_dev, buf, count, rbu_attr->type);
    +
    + return rc;
    +}
    +
    +static ssize_t rbu_attr_show (struct kobject * kobj,
    + struct attribute *attr,
    + char *buf)
    +{
    + struct rbu_download_device *rbu_dev = to_rbu_download_device(kobj);
    + struct rbu_attribute *rbu_attr = to_rbu_attr(attr);
    + ssize_t rc = 0;
    +
    + pr_debug("rbu_attr_show: entry type = %d\n", rbu_attr->type);
    +
    + if (rbu_attr->show)
    + rc = rbu_attr->show(rbu_dev, buf, rbu_attr->type);
    + return rc;
    +}
    +
    +static struct sysfs_ops rbu_attr_ops = {
    + .show = rbu_attr_show,
    + .store = rbu_attr_store,
    +};
    +
    +static struct kobj_type ktype_dell_rbu = {
    + .sysfs_ops = &rbu_attr_ops,
    + .default_attrs = def_attrs,
    +};
    +static decl_subsys(dell_rbu,&ktype_dell_rbu,NULL);
    +
    +static int rbu_download_device_register(struct rbu_download_device *rbu_dev,
    + int type)
    +{
    + int rc = 0;
    + if (!rbu_dev)
    + return 1;
    + memset(rbu_dev, 0, sizeof (*rbu_dev));
    + if (type == MONOLITHIC)
    + kobject_set_name(&rbu_dev->kobj, "monolithic");
    + else
    + kobject_set_name(&rbu_dev->kobj, "packetized");
    + kobj_set_kset_s(rbu_dev,dell_rbu_subsys);
    + rc = kobject_register(&rbu_dev->kobj);
    + if (!rc) {
    + if (type == MONOLITHIC ) {
    + sysfs_create_file(&rbu_dev->kobj,
    + &rbu_attr_mono_name.attr);
    + sysfs_create_file(&rbu_dev->kobj,
    + &rbu_attr_mono_size.attr);
    + } else {
    + sysfs_create_file(&rbu_dev->kobj,
    + &rbu_attr_packet_name.attr);
    + sysfs_create_file(&rbu_dev->kobj,
    + &rbu_attr_packet_size.attr);
    + }
    + } else
    + pr_debug("rbu_download_device_register: "
    + "kobject_register %d \n", rc);
    + pr_debug("rbu_download_device_register: rbu_dev addr %p\n", rbu_dev);
    + return rc;
    +}
    +
    +static struct rbu_download_device *create_rbu_download_entry(int type)
    +{
    + struct rbu_download_device *rbu_dev = NULL;
    + rbu_dev = kmalloc( sizeof(struct rbu_download_device),
    + GFP_KERNEL);
    + if (!rbu_dev) {
    + printk(KERN_ERR "create_rbu_download_entry: kmalloc failed\n");
    + return NULL;
    + }
    + rbu_dev->type = type;
    + if (rbu_download_device_register(rbu_dev, type)) {
    + pr_debug("create_rbu_download_entry: "
    + "rbu_download_device_register failed \n");
    + kfree(rbu_dev);
    + }
    +
    + pr_debug("create_rbu_download_entry: rbu_dev %p\n", rbu_dev);
    + return rbu_dev;
    +}
    +
    +static void remove_rbu_download_entry(struct rbu_download_device *rbu_dev,
    + int type)
    +{
    + pr_debug("remove_rbu_download_entry: rbu_dev ptr %p \n", rbu_dev);
    + if (rbu_dev != NULL) {
    + kobject_unregister(&rbu_dev->kobj);
    + kfree(rbu_dev);
    + }
    +
    +}
    +
    +static int __init dcdrbu_init(void)
    +{
    + int rc = 0;
    + spin_lock_init(&rbu_data.lock);
    +
    + init_packet_head();
    +
    + device_initialize(&rbu_device);
    +
    + rc = firmware_register(&dell_rbu_subsys);
    + if (rc < 0) {
    + printk(KERN_WARNING "dcdrbu_init: firmware_register"
    + " dell_rbu failed\n");
    + return rc;
    + }
    +
    + rbu_download_mono = create_rbu_download_entry (MONOLITHIC);
    + if (rbu_download_mono == NULL) {
    + firmware_unregister(&dell_rbu_subsys);
    + return -ENOMEM;
    + }
    +
    + rbu_download_packet= create_rbu_download_entry (PACKETIZED);
    + if (rbu_download_packet == NULL) {
    + remove_rbu_download_entry(rbu_download_mono, MONOLITHIC);
    + firmware_unregister(&dell_rbu_subsys);
    + return -ENOMEM;
    + }
    + strncpy(rbu_device.bus_id,"firmware", BUS_ID_SIZE);
    + return rc;
    +}
    +
    +static __exit void dcdrbu_exit( void)
    +{
    + spin_lock(&rbu_data.lock);
    + packet_empty_list();
    + img_update_free();
    + spin_unlock(&rbu_data.lock);
    + remove_rbu_download_entry(rbu_download_packet, PACKETIZED);
    + remove_rbu_download_entry(rbu_download_mono, MONOLITHIC);
    + firmware_unregister(&dell_rbu_subsys);
    +}
    +
    +module_exit(dcdrbu_exit);
    +module_init(dcdrbu_init);
    +
    diff -uprN linux-2.6.11.8.ORIG/drivers/firmware/Kconfig linux-2.6.11.8/drivers/firmware/Kconfig
    --- linux-2.6.11.8.ORIG/drivers/firmware/Kconfig 2005-05-13 12:07:58.000000000 -0500
    +++ linux-2.6.11.8/drivers/firmware/Kconfig 2005-05-13 12:07:00.000000000 -0500
    @@ -58,4 +58,16 @@ config EFI_PCDP
     
               See <http://www.dig64.org/specifications/DIG64_HCDPv20_042804.pdf>
     
    +config DELL_RBU
    + tristate "BIOS update support for DELL systems via sysfs"
    + default n
    + help
    + Say Y if you want to have the option of updating the BIOS for your
    + DELL system. Note you need a supporting application to comunicate
    + with the BIOS regardign the new image for the image update to
    + take effect.
    +
    + See <file:Documentation/DELL_RBU.txt> for more details on the driver.
    +
    +
     endmenu
    diff -uprN linux-2.6.11.8.ORIG/drivers/firmware/Makefile linux-2.6.11.8/drivers/firmware/Makefile
    --- linux-2.6.11.8.ORIG/drivers/firmware/Makefile 2005-05-13 12:08:12.000000000 -0500
    +++ linux-2.6.11.8/drivers/firmware/Makefile 2005-05-09 15:15:16.000000000 -0500
    @@ -4,3 +4,4 @@
     obj-$(CONFIG_EDD) += edd.o
     obj-$(CONFIG_EFI_VARS) += efivars.o
     obj-$(CONFIG_EFI_PCDP) += pcdp.o
    +obj-$(CONFIG_DELL_RBU) += dell_rbu.o
    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/


  • Next message: Martin J. Bligh: "Re: Avoiding external fragmentation with a placement policy Version 12"

    Relevant Pages