Wednesday, December 18, 2013

Write to block device from kernel...


int
disk_open(dev_minor_t *devinst, const char *path)
{
        int     partnum;
        dev_t   devt;
        struct  hd_struct *part;
        unsigned char   *buf = NULL;
        struct  bio *bb;
        int     i, j;
        struct  page *page;
        unsigned char *d = NULL;
        DECLARE_COMPLETION_ONSTACK(waithdl);

        if ((devt = blk_lookup_devt(path, 0)) == 0) {
                printk(KERN_INFO "blk_lookup_devt %d\n", devt);
                return (-1);
        }
        printk(KERN_INFO "Success blk_lookup_devt %d\n", devt);
        if ((devinst->disk = get_gendisk(devt, &partnum)) == NULL) {
                printk(KERN_INFO "Failed to get generic device ..");
                return (-1);
        }
  
        if ((part = disk_get_part(devinst->disk, 0)) == NULL) {
                printk(KERN_INFO "failed disk_get_part");
                return (-1);
        }
  
        if ((devinst->bdev = bdget_disk(devinst->disk, 0)) == NULL) {
                printk(KERN_INFO "failed bdget_disk");
                return (-1);
        }
        return (0);
}


void
disk_close(struct file *filp)
{
     
}


int
disk_read(dev_minor_t *devinst, unsigned long long offset,
                                       unsigned char *data, unsigned int size)
{
        struct  page *page;
        unsigned char *d = NULL;
        DECLARE_COMPLETION_ONSTACK(waithdl);
        unsigned char   *buf = NULL;
        struct  bio *bb;
        int     ret = 0;

        printk(KERN_INFO "Enter %s\n", __FUNCTION__);
        if (data == NULL) {
                page = alloc_page(GFP_KERNEL);
                d = page_address(page);
                memset(d, 0x00, 4096);
        } else {
                d = data;
                memset(d, 0x00, 4096);
        }

        bb = bio_map_kern(devinst->disk->queue, d, 4096, GFP_KERNEL);
        if (IS_ERR(bb)) {
                printk(KERN_ALERT "FAiled to map kernel memory");
                return (-1);
        }
        //bb->bi_sector = offset >> 9;
        bb->bi_sector = 0;
        printk(KERN_INFO "offset %d size %d\n", bb->bi_sector, size);
        bb->bi_bdev = devinst->bdev->bd_contains;
        if (bb->bi_bdev == NULL) {
                bb->bi_bdev = blkdev_get_by_dev(devinst->bdev->bd_dev, FMODE_READ|FMODE_WRITE, NULL);
        }
        if (bb->bi_bdev == NULL) {
                printk(KERN_ALERT "Doesn't have blk device ...\n");
                return (-1);
        }
        bb->bi_end_io = callback_fn;
        bb->bi_private = &waithdl;
        bio_get(bb);

        submit_bio(READ_SYNC, bb); //read operation
        wait_for_completion(&waithdl);

        if (!bio_flagged(bb, BIO_UPTODATE)) {
               printk(KERN_ALERT "Failed ...");
                ret = -1;
        }
        bio_put(bb);
        printk(KERN_INFO "Data read %x%x%x%x", d[0],d[1],d[2],d[3]);
#if 0
        if (data)
                memcpy(data, d, 4096);
#endif
        return (ret);
}

int
disk_write(dev_minor_t *devinst, unsigned long long offset,
                                     unsigned char *data, unsigned int size)
{
        struct  page *page;
        unsigned char *d = NULL;
        DECLARE_COMPLETION_ONSTACK(waithdl);
        unsigned char   *buf = NULL;
        struct  bio *bb;
        struct bio_vec bio_vec;

        int     ret = 0;

        printk(KERN_INFO "Enter %s\n", __FUNCTION__);

        bb = bio_map_kern(devinst->disk->queue, data, 4096, GFP_KERNEL);
        if (IS_ERR(bb)) {
                printk(KERN_ALERT "FAiled to map kernel memory");
                return (-1);
        }
        bb->bi_sector = 1;
        bb->bi_bdev = devinst->bdev->bd_contains;
        if (bb->bi_bdev == NULL) {
                bb->bi_bdev = blkdev_get_by_dev(devinst->bdev->bd_dev, FMODE_READ|FMODE_WRITE, NULL);
        }
        if (bb->bi_bdev == NULL) {
                printk(KERN_ALERT "Doesn't have blk device ...\n");
                return (-1);
        }
        bb->bi_end_io = callback_fn;
        bb->bi_private = &waithdl;

        bio_get(bb);
        submit_bio(WRITE_SYNC, bb);

        wait_for_completion(&waithdl);

        if (!test_bit(BIO_UPTODATE, &bb->bi_flags)) {
                bio_put(bb);
                ret = -1;
        }
        bio_put(bb);
        return (ret);
}

/* backup code */
page_read() {

        bio_init(&bio);
        bio.bi_io_vec = &bio_vec;
        bio_vec.bv_page = p;
        bio_vec.bv_len = size;
        bio_vec.bv_offset = 0;
        bio.bi_vcnt = 1;
        bio.bi_idx = 0;
        bio.bi_size = size;
        bio.bi_bdev = bdev->bd_contains;
        bio.bi_sector = sect;
        init_completion(&complete);
        bio.bi_private = &complete;
        bio.bi_end_io = rq_complete;

        //submit_bio(WRITE_SYNC, &bio);
        submit_bio(READ_SYNC, &bio);
        wait_for_completion(&complete);

        if (test_bit(BIO_UPTODATE, &bio.bi_flags)) {
               printk(KERN_INFO "Read BIO OK 0x%x%x%x%x\n", d[0],d[1],d[2],d[3]);
        } else {
                printk(KERN_INFO "BIO ERROR\n");
        }
}







Monday, December 9, 2013

How to write to a file from a kernel module

/* The below code, can be used to create a file and write data to a file using vfs call.
*/

#include <linux/fs.h>
#include <asm/segment.h>
#include <asm/uaccess.h>
#include <linux/buffer_head.h>

struct file *
driver_file_open(const char *path, int flags, int mode)
{
        struct file *filp = NULL;
        mm_segment_t    oldfs;
        oldfs   = get_fs();
        set_fs(get_ds());
        filp = filp_open(path, O_CREAT|O_RDWR, S_IRWXU|S_IRWXG|S_IRWXO);
        set_fs(oldfs);
        return (filp);
}



void
driver_file_close(struct file *filp)
{
        filp_close(filp, NULL);
}



int
driver_file_write(struct file *file, unsigned long long offset, unsigned char *data, unsigned int size)
{
        int     ret;
        mm_segment_t    oldfs;
        loff_t  pos = offset;
        oldfs   = get_fs();
        set_fs(get_ds());

        //vfs_setpos(file, pos, pos + PAGE_SIZE);
        //Workaround for vfs_setpos, not implemented on my version of linux.
        spin_lock(&file->f_lock);
        file->f_pos = pos;
        //file->f_version = 0;
        printk(KERN_INFO "set position to  %llx\n", pos);
        spin_unlock(&file->f_lock);


        ret = vfs_write(file, data, size, &pos);
        //vfs_fsync(file, 0);
        set_fs(oldfs);
        return (ret);
}




int
driver_file_read(struct file *file, unsigned long long offset, unsigned char *data, unsigned int size)
{
        int     ret;
        mm_segment_t    oldfs;
        loff_t  pos = offset;
        oldfs   = get_fs();
        set_fs(get_ds());

        //vfs_setpos(file, pos, pos + PAGE_SIZE);
        //Workaround for vfs_setpos, not implemented on my version of linux.
        spin_lock(&file->f_lock);
        file->f_pos = pos;
        //file->f_version = 0;
        printk(KERN_INFO "set position to read %llx\n", pos);
        spin_unlock(&file->f_lock);


        ret = vfs_read(file, data, size, &pos);
        //vfs_fsync(file, 0);
        set_fs(oldfs);
        return (ret);
}