顯示具有 Linux 驅動程式篇 標籤的文章。 顯示所有文章
顯示具有 Linux 驅動程式篇 標籤的文章。 顯示所有文章

星期六, 4月 26, 2008

Dummy Block Driver for Linux

/*
* Sample disk driver, from the beginning.
*/

#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>

#include <linux/sched.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/timer.h>
#include <linux/types.h> /* size_t */
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/hdreg.h> /* HDIO_GETGEO */
#include <linux/kdev_t.h>
#include <linux/vmalloc.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/buffer_head.h> /* invalidate_bdev */
#include <linux/bio.h>

MODULE_LICENSE("Dual BSD/GPL");

static int sbull_major = 0;
module_param(sbull_major, int, 0);
static int hardsect_size = 512;
module_param(hardsect_size, int, 0);
static int nsectors = 1024; /* How big the drive is */
module_param(nsectors, int, 0);
static int ndevices = 4;
module_param(ndevices, int, 0);

/*
* The different "request modes" we can use.
*/
enum {
RM_SIMPLE = 0, /* The extra-simple request function */
RM_FULL = 1, /* The full-blown version */
RM_NOQUEUE = 2, /* Use make_request */
};
static int request_mode = RM_SIMPLE;
module_param(request_mode, int, 0);

/*
* Minor number and partition management.
*/
#define SBULL_MINORS 16
#define MINOR_SHIFT 4
#define DEVNUM(kdevnum) (MINOR(kdev_t_to_nr(kdevnum)) >> MINOR_SHIFT

/*
* We can tweak our hardware sector size, but the kernel talks to us
* in terms of small sectors, always.
*/
#define KERNEL_SECTOR_SIZE 512

/*
* After this much idle time, the driver will simulate a media change.
*/
#define INVALIDATE_DELAY 30*HZ

/*
* The internal representation of our device.
*/
struct sbull_dev {
int size; /* Device size in sectors */
u8 *data; /* The data array */
short users; /* How many users */
short media_change; /* Flag a media change? */
spinlock_t lock; /* For mutual exclusion */
struct request_queue *queue; /* The device request queue */
struct gendisk *gd; /* The gendisk structure */
struct timer_list timer; /* For simulated media changes */
};

static struct sbull_dev *Devices = NULL;

/*
* Handle an I/O request.
*/
static void sbull_transfer(struct sbull_dev *dev, unsigned long sector,
unsigned long nsect, char *buffer, int write)
{
unsigned long offset = sector*KERNEL_SECTOR_SIZE;
unsigned long nbytes = nsect*KERNEL_SECTOR_SIZE;

if ((offset + nbytes) > dev->size) {
printk (KERN_NOTICE "Beyond-end write (%ld %ld)n", offset, nbytes);
return;
}
if (write)
memcpy(dev->data + offset, buffer, nbytes);
else
memcpy(buffer, dev->data + offset, nbytes);
}

/*
* The simple form of the request function.
*/
static void sbull_request(request_queue_t *q)
{
struct request *req;

while ((req = elv_next_request(q)) != NULL) {
struct sbull_dev *dev = req->rq_disk->private_data;
if (! blk_fs_request(req)) {
printk (KERN_NOTICE "Skip non-fs requestn");
end_request(req, 0);
continue;
}
// printk (KERN_NOTICE "Req dev %d dir %ld sec %ld, nr %d f %lxn",
// dev - Devices, rq_data_dir(req),
// req->sector, req->current_nr_sectors,
// req->flags);
sbull_transfer(dev, req->sector, req->current_nr_sectors,
req->buffer, rq_data_dir(req));
end_request(req, 1);
}
}


/*
* Transfer a single BIO.
*/
static int sbull_xfer_bio(struct sbull_dev *dev, struct bio *bio)
{
int i;
struct bio_vec *bvec;
sector_t sector = bio->bi_sector;

/* Do each segment independently. */
bio_for_each_segment(bvec, bio, i) {
char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
sbull_transfer(dev, sector, bio_cur_sectors(bio),
buffer, bio_data_dir(bio) == WRITE);
sector += bio_cur_sectors(bio);
__bio_kunmap_atomic(bio, KM_USER0);
}
return 0; /* Always "succeed" */
}

/*
* Transfer a full request.
*/
static int sbull_xfer_request(struct sbull_dev *dev, struct request *req)
{
struct bio *bio;
int nsect = 0;

rq_for_each_bio(bio, req) {
sbull_xfer_bio(dev, bio);
nsect += bio->bi_size/KERNEL_SECTOR_SIZE;
}
return nsect;
}

/*
* Smarter request function that "handles clustering".
*/
static void sbull_full_request(request_queue_t *q)
{
struct request *req;
int sectors_xferred;
struct sbull_dev *dev = q->queuedata;

while ((req = elv_next_request(q)) != NULL) {
if (! blk_fs_request(req)) {
printk (KERN_NOTICE "Skip non-fs requestn");
end_request(req, 0);
continue;
}
sectors_xferred = sbull_xfer_request(dev, req);
if (! end_that_request_first(req, 1, sectors_xferred)) {
blkdev_dequeue_request(req);
end_that_request_last(req);
}
}
}

/*
* The direct make request version.
*/
static int sbull_make_request(request_queue_t *q, struct bio *bio)
{
struct sbull_dev *dev = q->queuedata;
int status;

status = sbull_xfer_bio(dev, bio);
bio_endio(bio, bio->bi_size, status);
return 0;
}


/*
* Open and close.
*/

static int sbull_open(struct inode *inode, struct file *filp)
{
struct sbull_dev *dev = inode->i_bdev->bd_disk->private_data;

del_timer_sync(&dev->timer);
filp->private_data = dev;
spin_lock(&dev->lock);
if (! dev->users)
check_disk_change(inode->i_bdev);
dev->users++;
spin_unlock(&dev->lock);
return 0;
}

static int sbull_release(struct inode *inode, struct file *filp)
{
struct sbull_dev *dev = inode->i_bdev->bd_disk->private_data;

spin_lock(&dev->lock);
dev->users--;

if (!dev->users) {
dev->timer.expires = jiffies + INVALIDATE_DELAY;
add_timer(&dev->timer);
}
spin_unlock(&dev->lock);

return 0;
}

/*
* Look for a (simulated) media change.
*/
int sbull_media_changed(struct gendisk *gd)
{
struct sbull_dev *dev = gd->private_data;

return dev->media_change;
}

/*
* Revalidate. WE DO NOT TAKE THE LOCK HERE, for fear of deadlocking
* with open. That needs to be reevaluated.
*/
int sbull_revalidate(struct gendisk *gd)
{
struct sbull_dev *dev = gd->private_data;

if (dev->media_change) {
dev->media_change = 0;
memset (dev->data, 0, dev->size);
}
return 0;
}

/*
* The "invalidate" function runs out of the device timer; it sets
* a flag to simulate the removal of the media.
*/
void sbull_invalidate(unsigned long ldev)
{
struct sbull_dev *dev = (struct sbull_dev *) ldev;

spin_lock(&dev->lock);
if (dev->users || !dev->data)
printk (KERN_WARNING "sbull: timer sanity check failedn");
else
dev->media_change = 1;
spin_unlock(&dev->lock);
}

/*
* The ioctl() implementation
*/

int sbull_ioctl (struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
long size;
struct hd_geometry geo;
struct sbull_dev *dev = filp->private_data;

switch(cmd) {
case HDIO_GETGEO:
/*
* Get geometry: since we are a virtual device, we have to make
* up something plausible. So we claim 16 sectors, four heads,
* and calculate the corresponding number of cylinders. We set the
* start of data at sector four.
*/
size = dev->size*(hardsect_size/KERNEL_SECTOR_SIZE);
geo.cylinders = (size & ~0x3f) >> 6;
geo.heads = 4;
geo.sectors = 16;
geo.start = 4;
if (copy_to_user((void __user *) arg, &geo, sizeof(geo)))
return -EFAULT;
return 0;
}

return -ENOTTY; /* unknown command */
}

/*
* The device operations structure.
*/
static struct block_device_operations sbull_ops = {
.owner = THIS_MODULE,
.open = sbull_open,
.release = sbull_release,
.media_changed = sbull_media_changed,
.revalidate_disk = sbull_revalidate,
.ioctl = sbull_ioctl
};


/*
* Set up our internal device.
*/
static void setup_device(struct sbull_dev *dev, int which)
{
/*
* Get some memory.
*/
memset (dev, 0, sizeof (struct sbull_dev));
dev->size = nsectors*hardsect_size;
dev->data = vmalloc(dev->size);
if (dev->data == NULL) {
printk (KERN_NOTICE "vmalloc failure.n");
return;
}
spin_lock_init(&dev->lock);

/*
* The timer which "invalidates" the device.
*/
init_timer(&dev->timer);
dev->timer.data = (unsigned long) dev;
dev->timer.function = sbull_invalidate;

/*
* The I/O queue, depending on whether we are using our own
* make_request function or not.
*/
switch (request_mode) {
case RM_NOQUEUE:
dev->queue = blk_alloc_queue(GFP_KERNEL);
if (dev->queue == NULL)
goto out_vfree;
blk_queue_make_request(dev->queue, sbull_make_request);
break;

case RM_FULL:
dev->queue = blk_init_queue(sbull_full_request, &dev->lock);
if (dev->queue == NULL)
goto out_vfree;
break;

default:
printk(KERN_NOTICE "Bad request mode %d, using simplen", request_mode);
/* fall into.. */

case RM_SIMPLE:
dev->queue = blk_init_queue(sbull_request, &dev->lock);
if (dev->queue == NULL)
goto out_vfree;
break;
}
blk_queue_hardsect_size(dev->queue, hardsect_size);
dev->queue->queuedata = dev;
/*
* And the gendisk structure.
*/
dev->gd = alloc_disk(SBULL_MINORS);
if (! dev->gd) {
printk (KERN_NOTICE "alloc_disk failuren");
goto out_vfree;
}
dev->gd->major = sbull_major;
dev->gd->first_minor = which*SBULL_MINORS;
dev->gd->fops = &sbull_ops;
dev->gd->queue = dev->queue;
dev->gd->private_data = dev;
snprintf (dev->gd->disk_name, 32, "sbull%c", which + 'a');
set_capacity(dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));
add_disk(dev->gd);
return;

out_vfree:
if (dev->data)
vfree(dev->data);
}

static int __init sbull_init(void)
{
int i;
/*
* Get registered.
*/
sbull_major = register_blkdev(sbull_major, "sbull");
if (sbull_major <= 0) {
printk(KERN_WARNING "sbull: unable to get major numbern");
return -EBUSY;
}
/*
* Allocate the device array, and initialize each one.
*/
Devices = kmalloc(ndevices*sizeof (struct sbull_dev), GFP_KERNEL);
if (Devices == NULL)
goto out_unregister;
for (i = 0; i < ndevices; i++)
setup_device(Devices + i, i);

return 0;

out_unregister:
unregister_blkdev(sbull_major, "sbd");
return -ENOMEM;
}

static void sbull_exit(void)
{
int i;

for (i = 0; i < ndevices; i++) {
struct sbull_dev *dev = Devices + i;

del_timer_sync(&dev->timer);
if (dev->gd) {
del_gendisk(dev->gd);
put_disk(dev->gd);
}
if (dev->queue) {
if (request_mode == RM_NOQUEUE)
blk_put_queue(dev->queue);
else
blk_cleanup_queue(dev->queue);
}
if (dev->data)
vfree(dev->data);
}
unregister_blkdev(sbull_major, "sbull");
kfree(Devices);
}

module_init(sbull_init);
module_exit(sbull_exit);

Dummy USB driver for Linux


/*
* USB Skeleton driver - 2.0
*
* Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*
* This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c
* but has been rewritten to be easy to read and use, as no locks are now
* needed anymore.
*
*/

#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kref.h>
#include <linux/smp_lock.h>
#include <linux/usb.h>
#include <asm/uaccess.h>


/* Define these values to match your devices */
#define USB_SKEL_VENDOR_ID 0xfff0
#define USB_SKEL_PRODUCT_ID 0xfff0

/* table of devices that work with this driver */
static struct usb_device_id skel_table [] = {
{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, skel_table);


/* Get a minor range for your devices from the usb maintainer */
#define USB_SKEL_MINOR_BASE 192

/* Structure to hold all of our device specific stuff */
struct usb_skel {
struct usb_device * udev; /* the usb device for this device */
struct usb_interface * interface; /* the interface for this device */
unsigned char * bulk_in_buffer; /* the buffer to receive data */
size_t bulk_in_size; /* the size of the receive buffer */
__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */
struct kref kref;
};
#define to_skel_dev(d) container_of(d, struct usb_skel, kref)

static struct usb_driver skel_driver;

static void skel_delete(struct kref *kref)
{
struct usb_skel *dev = to_skel_dev(kref);

usb_put_dev(dev->udev);
kfree (dev->bulk_in_buffer);
kfree (dev);
}

static int skel_open(struct inode *inode, struct file *file)
{
struct usb_skel *dev;
struct usb_interface *interface;
int subminor;
int retval = 0;

subminor = iminor(inode);

interface = usb_find_interface(&skel_driver, subminor);
if (!interface) {
err ("%s - error, can't find device for minor %d",
__FUNCTION__, subminor);
retval = -ENODEV;
goto exit;
}

dev = usb_get_intfdata(interface);
if (!dev) {
retval = -ENODEV;
goto exit;
}

/* increment our usage count for the device */
kref_get(&dev->kref);

/* save our object in the file's private structure */
file->private_data = dev;

exit:
return retval;
}

static int skel_release(struct inode *inode, struct file *file)
{
struct usb_skel *dev;

dev = (struct usb_skel *)file->private_data;
if (dev == NULL)
return -ENODEV;

/* decrement the count on our device */
kref_put(&dev->kref, skel_delete);
return 0;
}

static ssize_t skel_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
struct usb_skel *dev;
int retval = 0;

dev = (struct usb_skel *)file->private_data;

/* do a blocking bulk read to get data from the device */
retval = usb_bulk_msg(dev->udev,
usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
dev->bulk_in_buffer,
min(dev->bulk_in_size, count),
&count, HZ*10);

/* if the read was successful, copy the data to userspace */
if (!retval) {
if (copy_to_user(buffer, dev->bulk_in_buffer, count))
retval = -EFAULT;
else
retval = count;
}

return retval;
}

static void skel_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
{
/* sync/async unlink faults aren't errors */
if (urb->status &&
!(urb->status == -ENOENT ||
urb->status == -ECONNRESET ||
urb->status == -ESHUTDOWN)) {
dbg("%s - nonzero write bulk status received: %d",
__FUNCTION__, urb->status);
}

/* free up our allocated buffer */
usb_buffer_free(urb->dev, urb->transfer_buffer_length,
urb->transfer_buffer, urb->transfer_dma);
}

static ssize_t skel_write(struct file *file, const char __user *user_buffer, size_t count, loff_t *ppos)
{
struct usb_skel *dev;
int retval = 0;
struct urb *urb = NULL;
char *buf = NULL;

dev = (struct usb_skel *)file->private_data;

/* verify that we actually have some data to write */
if (count == 0)
goto exit;

/* create a urb, and a buffer for it, and copy the data to the urb */
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
retval = -ENOMEM;
goto error;
}

buf = usb_buffer_alloc(dev->udev, count, GFP_KERNEL, &urb->transfer_dma);
if (!buf) {
retval = -ENOMEM;
goto error;
}
if (copy_from_user(buf, user_buffer, count)) {
retval = -EFAULT;
goto error;
}

/* initialize the urb properly */
usb_fill_bulk_urb(urb, dev->udev,
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
buf, count, skel_write_bulk_callback, dev);
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

/* send the data out the bulk port */
retval = usb_submit_urb(urb, GFP_KERNEL);
if (retval) {
err("%s - failed submitting write urb, error %d", __FUNCTION__, retval);
goto error;
}

/* release our reference to this urb, the USB core will eventually free it entirely */
usb_free_urb(urb);

exit:
return count;

error:
usb_buffer_free(dev->udev, count, buf, urb->transfer_dma);
usb_free_urb(urb);
kfree(buf);
return retval;
}

static struct file_operations skel_fops = {
.owner = THIS_MODULE,
.read = skel_read,
.write = skel_write,
.open = skel_open,
.release = skel_release,
};

/*
* usb class driver info in order to get a minor number from the usb core,
* and to have the device registered with devfs and the driver core
*/
static struct usb_class_driver skel_class = {
.name = "usb/skel%d",
.fops = &skel_fops,
.mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
.minor_base = USB_SKEL_MINOR_BASE,
};

static int skel_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
struct usb_skel *dev = NULL;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
size_t buffer_size;
int i;
int retval = -ENOMEM;

/* allocate memory for our device state and initialize it */
dev = kmalloc(sizeof(struct usb_skel), GFP_KERNEL);
if (dev == NULL) {
err("Out of memory");
goto error;
}
memset(dev, 0x00, sizeof (*dev));
kref_init(&dev->kref);

dev->udev = usb_get_dev(interface_to_usbdev(interface));
dev->interface = interface;

/* set up the endpoint information */
/* use only the first bulk-in and bulk-out endpoints */
iface_desc = interface->cur_altsetting;
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;

if (!dev->bulk_in_endpointAddr &&
(endpoint->bEndpointAddress & USB_DIR_IN) &&
((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== USB_ENDPOINT_XFER_BULK)) {
/* we found a bulk in endpoint */
buffer_size = endpoint->wMaxPacketSize;
dev->bulk_in_size = buffer_size;
dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);

if (!dev->bulk_in_buffer) {
err("Could not allocate bulk_in_buffer");
goto error;
}
}

if (!dev->bulk_out_endpointAddr &&
!(endpoint->bEndpointAddress & USB_DIR_IN) &&
((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== USB_ENDPOINT_XFER_BULK)) {
/* we found a bulk out endpoint */
dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
}
}
if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
err("Could not find both bulk-in and bulk-out endpoints");
goto error;
}

/* save our data pointer in this interface device */
usb_set_intfdata(interface, dev);

/* we can register the device now, as it is ready */
retval = usb_register_dev(interface, &skel_class);
if (retval) {
/* something prevented us from registering this driver */
err("Not able to get a minor for this device.");
usb_set_intfdata(interface, NULL);
goto error;
}

/* let the user know what node this device is now attached to */
info("USB Skeleton device now attached to USBSkel-%d", interface->minor);
return 0;

error:
if (dev)
kref_put(&dev->kref, skel_delete);
return retval;
}

static void skel_disconnect(struct usb_interface *interface)
{
struct usb_skel *dev;
int minor = interface->minor;

/* prevent skel_open() from racing skel_disconnect() */
lock_kernel();

dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);

/* give back our minor */
usb_deregister_dev(interface, &skel_class);

unlock_kernel();

/* decrement our usage count */
kref_put(&dev->kref, skel_delete);

info("USB Skeleton #%d now disconnected", minor);
}

static struct usb_driver skel_driver = {
.owner = THIS_MODULE,
.name = "skeleton",
.id_table = skel_table,
.probe = skel_probe,
.disconnect = skel_disconnect,
};

static int __init usb_skel_init(void)
{
int result;

/* register this driver with the USB subsystem */
result = usb_register(&skel_driver);
if (result)
err("usb_register failed. Error number %d", result);

return result;
}

static void __exit usb_skel_exit(void)
{
/* deregister this driver with the USB subsystem */
usb_deregister(&skel_driver);
}

module_init (usb_skel_init);
module_exit (usb_skel_exit);

MODULE_LICENSE("GPL");

Dummy PCI driver for Linux


#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>


static struct pci_device_id ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_3), },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, ids);

static unsigned char skel_get_revision(struct pci_dev *dev)
{
u8 revision;

pci_read_config_byte(dev, PCI_REVISION_ID, &revision);
return revision;
}

static int probe(struct pci_dev *dev, const struct pci_device_id *id)
{
/* Do probing type stuff here.
* Like calling request_region();
*/
pci_enable_device(dev);

if (skel_get_revision(dev) == 0x42)
return -ENODEV;


return 0;
}

static void remove(struct pci_dev *dev)
{
/* clean up any allocated resources and stuff here.
* like call release_region();
*/
}

static struct pci_driver pci_driver = {
.name = "pci_skel",
.id_table = ids,
.probe = probe,
.remove = remove,
};

static int __init pci_skel_init(void)
{
return pci_register_driver(&pci_driver);
}

static void __exit pci_skel_exit(void)
{
pci_unregister_driver(&pci_driver);
}

MODULE_LICENSE("GPL");

module_init(pci_skel_init);
module_exit(pci_skel_exit);

星期五, 4月 18, 2008

Build Kernel Module as KO files


Show build progress in verbose

make V=1

lib path for glibc

/opt/microtime/pro/devkit/arm/pxa270/gcc-4.0.2-glibc-2.3.3/arm-unknown-linux-gnu/arm-unknown-linux-gnu/lib

bin path of gcc

/opt/microtime/pro/devkit/arm/pxa270/gcc-4.0.2-glibc-2.3.3/arm-unknown-linux-gnu/bin

header file path of Linux Kernel

/usr/src/creator/pxa270/linux/include

obj-y

cmd_drivers/char/creator-pxa270-lcd.o := arm-unknown-linux-gnu-gcc -Wp,-MD,drivers/char/.creator-pxa270-lcd.o.d -nostdinc -isystem /opt/microtime/pro/devkit/arm/pxa270/gcc-4.0.2-glibc-2.3.3/arm-unknown-linux-gnu/bin/../lib/gcc/arm-unknown-linux-gnu/4.0.2/include -D__KERNEL__ -Iinclude -include include/linux/autoconf.h -mlittle-endian -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -ffreestanding -fno-inline -Os -fno-omit-frame-pointer -fno-optimize-sibling-calls -g -fno-omit-frame-pointer -mapcs -mno-sched-prolog -mabi=apcs-gnu -mno-thumb-interwork -D__LINUX_ARM_ARCH__=5 -march=armv5te -mtune=xscale -Wa,-mcpu=xscale -msoft-float -Uarm -Wdeclaration-after-statement -Wno-pointer-sign -DKBUILD_BASENAME=creator_pxa270_lcd -DKBUILD_MODNAME=creator_pxa270_lcd -c -o drivers/char/creator-pxa270-lcd.o drivers/char/creator-pxa270-lcd.c

obj-m

step1:
cmd_drivers/char/creator-pxa270-lcd.o := arm-unknown-linux-gnu-gcc -Wp,-MD,drivers/char/.creator-pxa270-lcd.o.d -nostdinc -isystem /opt/microtime/pro/devkit/arm/pxa270/gcc-4.0.2-glibc-2.3.3/arm-unknown-linux-gnu/bin/../lib/gcc/arm-unknown-linux-gnu/4.0.2/include -D__KERNEL__ -Iinclude -include include/linux/autoconf.h -mlittle-endian -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -ffreestanding -fno-inline -Os -fno-omit-frame-pointer -fno-optimize-sibling-calls -g -fno-omit-frame-pointer -mapcs -mno-sched-prolog -mabi=apcs-gnu -mno-thumb-interwork -D__LINUX_ARM_ARCH__=5 -march=armv5te -mtune=xscale -Wa,-mcpu=xscale -msoft-float -Uarm -Wdeclaration-after-statement -Wno-pointer-sign -DMODULE -DKBUILD_BASENAME=creator_pxa270_lcd -DKBUILD_MODNAME=creator_pxa270_lcd -c -o drivers/char/creator-pxa270-lcd.o drivers/char/creator-pxa270-lcd.c

step2:
scripts/mod/modpost -o /usr/src/creator/pxa270/pro/devkit/lsp/create-pxa270/linux-2.6.15.3/Module.symvers vmlinux drivers/char/creator-pxa270-lcd.o

step3:
arm-unknown-linux-gnu-gcc -Wp,-MD,drivers/char/.creator-pxa270-lcd.mod.o.d -nostdinc -isystem /opt/microtime/pro/devkit/arm/pxa270/gcc-4.0.2-glibc-2.3.3/arm-unknown-linux-gnu/bin/../lib/gcc/arm-unknown-linux-gnu/4.0.2/include -D__KERNEL__ -Iinclude -include include/linux/autoconf.h -mlittle-endian -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -ffreestanding -fno-inline -Os -fno-omit-frame-pointer -fno-optimize-sibling-calls -g -fno-omit-frame-pointer -mapcs -mno-sched-prolog -mabi=apcs-gnu -mno-thumb-interwork -D__LINUX_ARM_ARCH__=5 -march=armv5te -mtune=xscale -Wa,-mcpu=xscale -msoft-float -Uarm -Wdeclaration-after-statement -Wno-pointer-sign -DKBUILD_BASENAME=creator_pxa270_lcd -DKBUILD_MODNAME=creator_pxa270_lcd -DMODULE -c -o drivers/char/creator-pxa270-lcd.mod.o drivers/char/creator-pxa270-lcd.mod.c

step4:
arm-unknown-linux-gnu-ld -EL -r -o drivers/char/creator-pxa270-lcd.ko drivers/char/creator-pxa270-lcd.o drivers/char/creator-pxa270-lcd.mod.o



星期六, 5月 12, 2007

Kernel Module

V1.1

copyright@2006

email: mesmerli@hotmail.com

Add a module into Linux building system

什麼是 Kernel Module

在不需要重新編譯 Linux 核心的狀況下,藉由 Kernel Module,可以擴充 Liunx 的核心功能。

通常用來擴充核心的驅動程式,並使用動態載入的方式載入核心。

Hello Kernel Module 實驗

透過最簡單的 Kernel Module 了解如何編譯執行 Kernel Module

並藉由 printk 的輸出字串,了解 Kernel Module 載入與移除時,哪些函數會被核心呼叫。

Hello Kernel Module 實驗步驟

Host 端

  1. 建構 Hello World Module
    1. 進入 HelloWorldModule 目錄
    2. make -f Makefile_Cross

Taget 端

  1. 使用 NFS 的方式,mount host 端的目錄,以存取剛剛建立的 Kernel Module。
    1. mount 192.168.0.200:/usr/src/creator/nfs /mnt

  1. 安裝 Kernel Module
    1. insmod HelloWorldModule_Cross.o

Makefile

CC=gcc
CFLAGS = -O2 -DMODULE -D__KERNEL__ -Wall
-I/usr/src/linux-2.4/include

HelloWorldModule.o: HelloWorldModule.c
$(CC) $(CFLAGS) -c HelloWorldModule.c
install:
/sbin/insmod HelloWorldModule.o
remove:
/sbin/rmmod HelloWorldModule
clean:
rm -f HelloWorldModule.o
message:
cat /var/log/messages

Makefile_Cross

CC=arm-linux-gcc
CFLAGS = -O2 -DMODULE -D__KERNEL__ -Wall
-I/usr/src/creator/s3c2410/linux/include

HelloWorldModule_Cross.o: HelloWorldModule.c
$(CC) $(CFLAGS) -c HelloWorldModule.c -o HelloWorldModule_Cross.o
install:
/sbin/insmod HelloWorldModule_Cross.o
remove:
/sbin/rmmod HelloWorldModule_Cross
clean:
rm -f HelloWorldModule_Cross.o
message:
cat /var/log/messages

HelloWordModule.c

#include <linux/module.h>
#include <linux/kernel.h>

int init_module(void)
{
printk("Hello World!n");
return 0;
}

void cleanup_module(void)
{
printk("Hello World Module was removed!");
}
MODULE_LICENSE("GPL");

Export Symbol 實驗

安裝兩支 Kernel Module,第二支程式呼叫第一支程式開放之 Symbol。

Export Symbol 實驗步驟

Host 端

  1. 建構 Kernel Module
    1. 進入 ExportSymbol 目錄
    2. make -f Makefile_Cross

Taget 端

  1. 使用 NFS 的方式,mount host 端的目錄,以存取剛剛建立的 Kernel Module。
    1. mount 192.168.0.200:/usr/src/creator/nfs /mnt
  2. 安裝 Kernel Module
    1. insmod baseModule_Cross.o
    2. insmod topModule_Cross.o

baseModule.c

int baseShowMessage(void);
EXPORT_SYMBOL(baseShowMessage);

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/ip.h>


int baseShowMessage()
{
printk("Hello LinuxKernelModule EXPORT_SYMBOL!");
return 0;
}

int init_module(void)
{
printk("n--- Base Module install ok! ---n");
return 0;
}
void cleanup_module(void)
{
printk("n--- Base Module uninstall ok! ---n");
}

MODULE_LICENSE("GPL");

topModile.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/ip.h>


extern int baseShowMessage(void);

int init_module(void)
{
baseShowMessage();
printk("n--- Top Module install ok! ---n");
return 0;
}
void cleanup_module(void)
{
printk("n--- Top Module uninstall ok! ---n");
}

MODULE_LICENSE("GPL");

Pass Parameter 實驗

在 insmod 指令列上傳遞參數給 Kernel Module。

Pass Parameter 實驗步驟

Host 端

  1. 建構 Kernel Module
    1. 進入 PassParameter 目錄
    2. make -f Makefile_Cross

Taget 端

  1. 使用 NFS 的方式,mount host 端的目錄,以存取剛剛建立的 Kernel Module。
    1. mount 192.168.0.200:/usr/src/creator/nfs /mnt
  2. 安裝 Kernel Module
    1. insmod PassParameter_Cross.o

PassParameter.c

#include <linux/module.h>
#include <linux/kernel.h>

static int module_int = 100;
static char *module_string = "Linux Kernel Module";

MODULE_PARM(module_int,"i");
MODULE_PARM(module_string,"s");

int init_module(void)
{
printk("My Linux Kernel Module n");
printk("module_int = %dn",module_int);
printk("module_string = %sn",module_string);
return 0;
}

void cleanup_module(void)
{
printk("My Linux Kernel Module was removed!");
}
MODULE_LICENSE("GPL");

Proc 檔案系統的虛擬檔案

Kenel Module 可以透過 PROC 檔案系統傳遞系統資訊。

SimpleProc 實驗步驟

Host 端

  1. 建構 Kernel Module
    1. 進入 SimpleProc 目錄
    2. make -f Makefile_Cross

Taget 端

  1. 使用 NFS 的方式,mount host 端的目錄,以存取剛剛建立的 Kernel Module。
    1. mount 192.168.0.200:/usr/src/creator/nfs /mnt
  2. 安裝 Kernel Module
    1. insmod SimpleProc_Cross.o
  3. 讀取系統資訊
    1. cat /proc/simpleProc

SimpleProc.c

#ifndef __KERNEL__
# define __KERNEL__
#endif
#ifndef MODULE
# define MODULE
#endif

#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>

static struct proc_dir_entry *proc_mtd;
static char ProcBuffer[50000];
static char Temp1[1000];
extern int baseCount;
static int WritingLength;

static int procfile_read(char *buffer,char **buffer_location,off_t offset,int buffer_length,int zero)
{
int ReturnLength;
if(offset >0) return 0;

WritingLength=0;

sprintf(Temp1,"baseCount=%dx0Dx0A",baseCount++);

if(WritingLength+strlen(Temp1) < 50000)
{
memcpy(&ProcBuffer[WritingLength],Temp1,strlen(Temp1));
WritingLength+=strlen(Temp1);
}

*buffer_location = ProcBuffer;
ReturnLength=WritingLength;
WritingLength=0;
return ReturnLength;
}

int init_module(void)
{
if ((proc_mtd = create_proc_entry("simpleProc", 0, 0 )))
proc_mtd->read_proc = procfile_read;
printk("SimpleProc: SimpleProc install ok! n");
return 0;
}
void cleanup_module(void)
{
baseCount=0;
if (proc_mtd)remove_proc_entry( "simpleProc", 0);
printk("SimpleProc uninstall successful! n");
}
MODULE_LICENSE("GPL");

SimpleProcRW 實驗步驟

Host 端

  1. 建構 Kernel Module
    1. 進入 SimpleProc 目錄
    2. make -f Makefile_Cross

Taget 端

  1. 使用 NFS 的方式,mount host 端的目錄,以存取剛剛建立的 Kernel Module。
    1. mount 192.168.0.200:/usr/src/creator/nfs /mnt
  2. 安裝 Kernel Module
    1. insmod SimpleProcRW_Cross.o
  3. 讀寫系統資訊
    1. cat /proc/simpleProcRW
    2. echo ABCDEFG > /proc/simpleProcRW
    3. cat /proc/simpleProcRW

SimpleProcRW.c

#ifndef __KERNEL__
# define __KERNEL__
#endif
#ifndef MODULE
# define MODULE
#endif
#include <linux/config.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/tcp.h>
#include <linux/ip.h>
#include <linux/proc_fs.h>
#include <net/ip.h>
#include <asm/uaccess.h>

static struct proc_dir_entry *proc_mtd;
static char ProcBuffer[50000];
static char Temp1[1000];
extern int baseCount;
static int WritingLength;

static int procfile_read(char *buffer,char **buffer_location,off_t offset,int buffer_length,int zero)
{
int ReturnLength;
if(offset >0) return 0;

sprintf(Temp1,"baseCount=%dx0Dx0A",baseCount++);

if(WritingLength+strlen(Temp1) < 50000)
{
memcpy(&ProcBuffer[WritingLength],Temp1,strlen(Temp1));
WritingLength+=strlen(Temp1);
}

*buffer_location = ProcBuffer;
ReturnLength=WritingLength;
WritingLength=0;
return ReturnLength;
}


static int procfile_write(struct file *file, const char *buffer, unsigned long count,
void *data)
{
WritingLength = count ;
if( WritingLength > 50000 ) WritingLength =50000;

if ( copy_from_user(ProcBuffer, buffer, WritingLength) )
{
return -EFAULT;
}

return WritingLength;
}


int init_module(void)
{
if ((proc_mtd = create_proc_entry("simpleProcRW", 0, 0 )))
{
proc_mtd->read_proc = procfile_read;
proc_mtd->write_proc = procfile_write;
}
printk("SimpleProc: SimpleProc install ok! n");
return 0;
}
void cleanup_module(void)
{
baseCount=0;
if (proc_mtd)remove_proc_entry( "simpleProcRW", 0);
printk("SimpleProc uninstall successful! n");
}
MODULE_LICENSE("GPL");

以應用程式存取 Proc 檔案系統

ShowProc.c

#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>

#include <curses.h>

#include <stdlib.h>

char buf[2048];
FILE *fp,*fplogfile;
void handler()
{

}

int main( int argc, char *argv[] )
{
int i;
if(argc != 3 )
{
printf("Usage: root# ./ShowProc [proc file name] [log file name]n");
exit(0);
}

if((fplogfile=fopen(argv[2],"w+"))==NULL)
{
printf("Warning: File(%s) can not open.",argv[2]);
exit(0);
}
signal(SIGALRM,handler);
while(1)
{
alarm(1);
pause();
system("clear");

if((fp=fopen(argv[1],"rb"))==NULL)
{
printf("Warning: File(%s) can not open.",argv[1]);
fclose(fplogfile);
exit(0);
}
while(fgets(buf,2047,fp))
{
i = 0;
printf("%s",buf);
fprintf(fplogfile,"%s",buf);
while(buf[i])
{
if(buf[i] == 'n') buf[i] = ';';
i++;
}
}
fclose(fp);
}
return 0;
}

ProcWrite.c

#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
//#include <curses.h>
#include <stdlib.h>

int main( int argc, char *argv[] )
{
int i;
char buf[2048];
FILE *fp;

if(argc != 3 )
{
printf("Usage: root# ./ProcWrite [proc file name] [Data to write]n");
exit(0);
}

if((fp=fopen(argv[1],"wb"))==NULL)
{
printf("Warning: File(%s) can not open.",argv[1]);
exit(0);
}
strcpy(buf,argv[2]);
fwrite(buf,1,strlen(buf),fp);
fclose(fp);
}

DIP 驅動程式

V1.1

copyright@2006

email: mesmerli@hotmail.com

DIP 驅動程式實驗步驟

Host 端

  1. root file system 組態設定。
    1. 建構新的 root file system (RAMDISK)
      1. dd if=/dev/zero of=ext2new bs=1k count=8192
      2. mke2fs -F -m0 -I 2000 ext2new
      3. mount -w -o loop ext2new /mnt/loop
      4. mount -w -o loop ext2org /mnt/looporg
      5. cp -dpR /mnt/looporg/. /mnt/loop/.
      6. cd /mnt/loop/dev (/dev 檔案夾下,建立 裝置節點)
      7. mknod 777 dip0 c 43 0
      8. cd /
      9. umount -l /mnt/loop
      10. gzip -9 ext2new
    2. 重新燒錄 root file system。
  2. 建構 DIP 驅動程式
    1. 進入 dip-demo 目錄
    2. make

Taget 端

  1. 使用 NFS 的方式,mount host 端的目錄,以存取剛剛建立的 DIP 驅動程式。
    1. mount 192.168.0.200:/usr/src/creator/nfs /mnt

  1. 安裝驅動程式

    1. cd /dev
    2. mknod -m 777 dip0 c 43 0
    3. cd /mnt/Day3/char-driver/dip-demo
    4. insmod dip-creator.o
  2. 執行應用程式
    1. ./dip-demo.exe

dip-creator.c

// --------------------------------------------------------------------
//
// Title : dip-creator.c
// :
// Library :
// :
// Developers: MICROTIME MDS group (V1.0)
// : mesmerli@gmail.com (V1.1)
// :
// Purpose : Driver for DIP of Creator
// :
// Limitation:
// :
// Note :
// :
// --------------------------------------------------------------------
// modification history :
// --------------------------------------------------------------------
// Version| mod. date: |
// V1.0 | 03/05/2004 | First release
// V1.1 | 11/15/2004 | DIP by mesmerli
// --------------------------------------------------------------------
//
// Note:
//
// MICROTIME COMPUTER INC.
//
//

#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <asm/irq.h>
#include <asm/param.h>
#include <asm/uaccess.h>
#include "asm/arch/irqs.h"

#if LINUX_VERSION_CODE < 0x020100
#define GET_USER(a,b) a = get_user(b)
#else
#include <asm/uaccess.h>
#define GET_USER(a,b) get_user(a,b)
#endif

#include "asm/arch/lib/creator_s3c2410_addr.h"
#include "asm/arch/lib/genfont8_8.h"
#include "dip-creator.h"

/*
* Define driver major number.
*/
#define MAJOR_NUM DIP_MAJOR_NUM
#define MODULE_VERSION "1.10"
#define MODULE_NAME "DIP_CREATOR"
#define COPYRIGHT "Copyright (C) 2003-2004, Microtime Computer Inc."
#define MODULE_AUTHOR_STRING "Microtime Computer Inc."
#define MODULE_DESCRIPTION_STRING "Creator DIP module"

/*****************************************************************************/

static int drv_dip_open(struct inode *inode, struct file *filp)
{
MOD_INC_USE_COUNT;
return(0);
}
/*****************************************************************************/


static int drv_dip_release(struct inode *inode, struct file *filp)
{
MOD_DEC_USE_COUNT;
return 0;
}

int drv_dip_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
int rc = 0;

/*
分離type如果遇到錯誤的cmd, 就直接傳回ENOTTY
*/
if (_IOC_TYPE(cmd) != DIP_IOCTL_MAGIC) return (-ENOTTY);

switch (cmd) {
case DIPSW_IOCTL_GET :{
unsigned short DIPSWs ;

DIPSWs = (UC)(IO_REG1);
if (copy_to_user((unsigned short*)arg, &DIPSWs, sizeof(unsigned short)))
return (-EINVAL);
break;
}
default:
rc = -ENOTTY;
break;
}

return(rc);
}

/*
* Exported file operations structure for driver...
*/

struct file_operations drv_dip_fops =
{
ioctl: drv_dip_ioctl,
open: drv_dip_open,
release: drv_dip_release,
};

/*****************************************************************************/
static int __init init_module_drv_dip(void)
{
int rc;

SET_MODULE_OWNER(&drv_dip_fops);

/* Register lcdtxt as character device */
if ((rc = register_chrdev(MAJOR_NUM, MODULE_NAME, &drv_dip_fops)) < 0) {
printk("<1>%s: can't get major %dn", MODULE_NAME, MAJOR_NUM);

return (-EBUSY);
}
printk("<1>%s: Version : %s %sn", MODULE_NAME, MODULE_VERSION, COPYRIGHT);

/* Hardware specific initialization */
return 0;
}

static void __exit cleanup_module_drv_dip(void)
{
unregister_chrdev(MAJOR_NUM, MODULE_NAME);
printk("<1>%s: removedn", MODULE_NAME);
}

/* here are the compiler macro for module operation */
module_init(init_module_drv_dip);
module_exit(cleanup_module_drv_dip);

MODULE_AUTHOR(MODULE_AUTHOR_STRING);
MODULE_DESCRIPTION(MODULE_DESCRIPTION_STRING);

EXPORT_NO_SYMBOLS;

/*****************************************************************************/

dip-creator.h

//=============================================================================
// File Name : dip-creator.h
// Function : DIP device drvier definition
// Program :
// Date : 11/15/2004
// Version : 1.10
// History
// 1.0.0 : Programming start (03/05/2004) -> SOP
// 1.1.0 : DIP version
//=============================================================================
#ifndef DIP_CREATOR_H_
#define DIP_CREATOR_H_

#include <linux/config.h>
#if defined(__linux__)
#include <asm/ioctl.h> /* For _IO* macros */
#define DIP_IOCTL_NR(n) _IOC_NR(n)
#elif defined(__FreeBSD__)
#include <sys/ioccom.h>
#define DIP_IOCTL_NR(n) ((n) & 0xff)
#endif

#define DIP_MAJOR_NUM 43
#define DIP_IOCTL_MAGIC DIP_MAJOR_NUM
#define DIP_IO(nr) _IO(DIP_IOCTL_MAGIC,nr)
#define DIP_IOR(nr,size) _IOR(DIP_IOCTL_MAGIC,nr,size)
#define DIP_IOW(nr,size) _IOW(DIP_IOCTL_MAGIC,nr,size)
#define DIP_IOWR(nr,size) _IOWR(DIP_IOCTL_MAGIC,nr,size)

/* DIP specific ioctls */
/* 當Switch調到ON時所傳回值為 */
/* 讀取DIP SW的狀態,bit 0是1 bit 7是8 */
#define DIPSW_IOCTL_GET DIP_IOR( 0x50, unsigned short)

#endif // DIP_CREATOR_H_

dip-demo.c

#include <signal.h>
#include <stdio.h>
#include <strings.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ioctl.h>

#include "dip-creator.h"

int main()
{
int fd;

unsigned int data = 0x0;

int ret;

fd = open("/dev/dip0", O_RDWR);
if (fd < 0)
{
printf("open /dev/dip0 errorn");
return (-1);
}


while(1)
{
ioctl(fd, DIPSW_IOCTL_GET, &data);

sleep(1);

printf("DIP is %02X", data);

}

printf("DIP Demo!!!n");

return 0;
}

區塊類型驅動程式

V1.1

copyright@2006

email: mesmerli@hotmail.com

RAMDISK 驅動程式實驗步驟

Host 端

  1. root file system 組態設定。
    1. 建構新的 root file system (RAMDISK)
      1. dd if=/dev/zero of=ext2new bs=1k count=8192
      2. mke2fs -F -m0 -I 2000 ext2new
      3. mount -w -o loop ext2new /mnt/loop
      4. mount -w -o loop ext2org /mnt/looporg
      5. cp -dpR /mnt/looporg/. /mnt/loop/.
      6. cd /mnt/loop/dev (/dev 檔案夾下,建立 裝置節點)
      7. mknod -m 777 ramblock0 b 42 0
      8. cd /
      9. umount -l /mnt/loop
      10. gzip -9 ext2new
  2. 重新燒錄 root file system。
  3. 編譯 RAMDISK 驅動程式
    1. init_timer(&radimo_timer); // (Line 307) 第一次實驗時,註解掉此一列程式,避免模擬磁碟片抽換
    2. make -f Makefile_Cross

Taget 端

  1. 使用 NFS 的方式,mount host 端的目錄,以存取剛剛建立的 RAMDISK 驅動程式。

    1. mount 192.168.0.200:/usr/src/creator/nfs /mnt

  1. 安裝驅動程式
    1. cd /dev
    2. mknod -m 777 ramblock0 b 42 0
    3. cd /mnt/Day3/blk-driver/radimo
    4. insmod radiomo_Cross.o
  2. 格式化檔案系統為 Ext2

    1. ./mke2fs_Cross.exe /dev/ramblock0
  3. mount Ext2 檔案系統

    1. mkdir /mnt2
    2. mount /dev/ramblock0 /mnt2
  4. 讀寫 /mnt2 目錄,此時 /mnt2 目錄的內容即為透過 RAMDISK 驅動程式操作的 Ext2 檔案系統。
  5. cat /proc/kmsg 觀察執行流程

radimo.c

/*
* Sample RAm DIsk MOdule, Radimo
*
*/
#include <linux/init.h>

#include <linux/module.h>

#if defined(CONFIG_SMP)
#define __SMP__
#endif

#if defined(CONFIG_MODVERSIONS)
#define MODVERSIONS
#include <linux/modversions.h>
#endif

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/fs.h>
#include <linux/vmalloc.h>

#include <asm/uaccess.h>

#include "radimo.h"

#define MAJOR_NR RADIMO_MAJOR
#define DEVICE_NAME "radimo"
#define DEVICE_REQUEST radimo_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#define DEVICE_NO_RANDOM

#include <linux/blk.h> // 在引入 blk 之前,必須定義以上常數用於定義巨集

#define RADIMO_HARDS_BITS 9 /* 2**9 byte hardware sector */
#define RADIMO_BLOCK_SIZE 1024 /* block size */
#define RADIMO_TOTAL_SIZE 2048 /* size in blocks */

/* the storage pool */
static char *radimo_storage;

static int radimo_hard = 1 << RADIMO_HARDS_BITS;
static int radimo_soft = RADIMO_BLOCK_SIZE;
static int radimo_size = RADIMO_TOTAL_SIZE;

static int radimo_readahead = 4;

/* for media changes */
static int radimo_changed;
struct timer_list radimo_timer;

/* module parameters and descriptions */
MODULE_PARM(radimo_soft, "1i");
MODULE_PARM_DESC(radimo_soft, "Software block size");
MODULE_PARM(radimo_size, "1i");
MODULE_PARM_DESC(radimo_size, "Total size in KB");
MODULE_PARM(radimo_readahead, "1i");
MODULE_PARM_DESC(radimo_readahead, "Max number of sectors to read ahead");

MODULE_LICENSE("GPL");


/* forward declarations for _fops */
static int radimo_open(struct inode *inode, struct file *file);
static int radimo_release(struct inode *inode, struct file *file);
static int radimo_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg);
static int radimo_media_change(kdev_t dev);
static int radimo_revalidate(kdev_t dev);


/* Because the kernel interface to device drivers changes a bit
from version to version, we have to test kernel versions here.
If you need your module to compile on older kernels, you can usei
this as an example for your device drivers. It should
work on kernels from 2.3 to 2.6.0
*/

#if LINUX_VERSION_CODE < 0x20326
static struct file_operations radimo_fops = {
read: block_read,
write: block_write,
ioctl: radimo_ioctl,
open: radimo_open,
release: radimo_release,
check_media_change: radimo_media_change,
revalidate: radimo_revalidate,

};
#else
static struct block_device_operations radimo_fops = {
open: radimo_open,
release: radimo_release,
ioctl: radimo_ioctl,
check_media_change: radimo_media_change,
revalidate: radimo_revalidate,
#if LINUX_VERSION_CODE >= 0x20414
owner: THIS_MODULE,
#endif
};
#endif


static void radimo_request(request_queue_t *q)
{
unsigned long offset, total;

radimo_begin:

INIT_REQUEST;

MSG(RADIMO_REQUEST, "%s sector %lu of %lun",
CURRENT->cmd == READ ? "read" : "write",
CURRENT->sector,
CURRENT->current_nr_sectors);

offset = CURRENT->sector * radimo_hard;
total = CURRENT->current_nr_sectors * radimo_hard;

/* access beyond end of the device */
if (total+offset > radimo_size * (radimo_hard << 1)) {
/* error in request */
end_request(0);
goto radimo_begin;
}

MSG(RADIMO_REQUEST, "offset = %lu, total = %lun", offset, total);

if (CURRENT->cmd == READ) {
memcpy(CURRENT->buffer, radimo_storage+offset, total);
} else if (CURRENT->cmd == WRITE) {
memcpy(radimo_storage+offset, CURRENT->buffer, total);
} else {
/* can't happen */
MSG(RADIMO_ERROR, "cmd == %d is invalidn", CURRENT->cmd);
end_request(0);
}

/* successful */
end_request(1);

/* let INIT_REQUEST return when we are done */
goto radimo_begin;
}

static int radimo_media_change(kdev_t dev)
{
if (radimo_changed)
MSG(RADIMO_INFO, "media has changedn");

/* 0 means medium has not changed, while 1 indicates a change */
return radimo_changed;
}

static int radimo_revalidate(kdev_t dev)
{
MSG(RADIMO_INFO, "revalidaten");

/* just return 0, check_disk_change ignores it anyway */
return 0;
}

void radimo_timer_fn(unsigned long data)
{
MSG(RADIMO_TIMER, "timer expiredn");

/* only "change media" if device is unused */
if (1 || MOD_IN_USE) {
radimo_changed = 0;
} else {
/* medium changed, clear storage and */
MSG(RADIMO_TIMER, "simulating media changen");
/* By erasing the first four blocks! */
memset(radimo_storage, 0, RADIMO_BLOCK_SIZE * 4 );
radimo_changed = 1;
/* data contains i_rdev */
fsync_dev(data);
invalidate_buffers(data);
}

/* set it up again */
radimo_timer.expires = RADIMO_TIMER_DELAY + jiffies;
add_timer(&radimo_timer);
}

static int radimo_release(struct inode *inode, struct file *file)
{
MSG(RADIMO_OPEN, "closedn");
MOD_DEC_USE_COUNT;
return 0;
}

static int radimo_open(struct inode *inode, struct file *file)
{
MSG(RADIMO_OPEN, "openedn");
MOD_INC_USE_COUNT;

/* timer function needs device to invalidate buffers. pass it as
data. */
radimo_timer.data = inode->i_rdev;
radimo_timer.expires = RADIMO_TIMER_DELAY + jiffies;
radimo_timer.function = &radimo_timer_fn;

if (!timer_pending(&radimo_timer))
add_timer(&radimo_timer);

return 0;
}

static int radimo_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
unsigned int minor;

if (!inode || !inode->i_rdev)
return -EINVAL;

minor = MINOR(inode->i_rdev);

switch (cmd) {

case BLKFLSBUF: {
/* flush buffers */
MSG(RADIMO_IOCTL, "ioctl: BLKFLSBUFn");
/* deny all but root */
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
fsync_dev(inode->i_rdev);
invalidate_buffers(inode->i_rdev);
break;
}

case BLKGETSIZE: {
/* return device size */
MSG(RADIMO_IOCTL, "ioctl: BLKGETSIZEn");
if (!arg)
return -EINVAL;
return put_user(radimo_size*2, (long *) arg);
}

case BLKRASET: {
/* set read ahead value */
int tmp;
MSG(RADIMO_IOCTL, "ioctl: BLKRASETn");
if (get_user(tmp, (long *)arg))
return -EINVAL;
if (tmp > 0xff)
return -EINVAL;
read_ahead[RADIMO_MAJOR] = tmp;
return 0;
}

case BLKRAGET: {
/* return read ahead value */
MSG(RADIMO_IOCTL, "ioctl: BLKRAGETn");
if (!arg)
return -EINVAL;
return put_user(read_ahead[RADIMO_MAJOR], (long *)arg);
}

case BLKSSZGET: {
/* return block size */
MSG(RADIMO_IOCTL, "ioctl: BLKSSZGETn");
if (!arg)
return -EINVAL;
return put_user(radimo_soft, (long *)arg);
}

default: {
MSG(RADIMO_ERROR, "ioctl wanted %un", cmd);
return -ENOTTY;
}
}

return 0;
}

static int __init radimo_init(void)
{
int res;

/* block size must be a multiple of sector size */
if (radimo_soft & ((1 << RADIMO_HARDS_BITS)-1)) {
MSG(RADIMO_ERROR, "Block size not a multiple of sector sizen");
return -EINVAL;
}

/* allocate room for data */
radimo_storage = (char *) vmalloc(1024*radimo_size);
if (radimo_storage == NULL) {
MSG(RADIMO_ERROR, "Not enough memory. Try a smaller size.n");
return -ENOMEM;
}
memset(radimo_storage, 0, 1024*radimo_size);

/* register block device */
res = register_blkdev(RADIMO_MAJOR, "radimo", &radimo_fops);
if (res) {
MSG(RADIMO_ERROR, "couldn't register block devicen");
return res;
}

/* for media change */
radimo_changed = 0;
init_timer(&radimo_timer); // 第一次實驗時,註解掉此一列程式,避免模擬磁碟片抽換

/* set hard- and soft blocksize */
hardsect_size[RADIMO_MAJOR] = &radimo_hard;
blksize_size[RADIMO_MAJOR] = &radimo_soft;
blk_size[RADIMO_MAJOR] = &radimo_size;

/* define our request function */
/* Here's another instance where kernel versions really matter.
The request queue interface changed in the 2.4 series kernels
*/

#if LINUX_VERSION_CODE < 0x20320
blk_dev[RADIMO_MAJOR].request_fn = &radimo_request;
#else
blk_init_queue(BLK_DEFAULT_QUEUE(RADIMO_MAJOR), radimo_request);
#endif
read_ahead[RADIMO_MAJOR] = radimo_readahead;

MSG(RADIMO_INFO, "loadedn");
MSG(RADIMO_INFO, "sector size of %d, block size of %d, total size = %dKbn",
radimo_hard, radimo_soft, radimo_size);

return 0;
}

static void __exit radimo_cleanup(void)
{
unregister_blkdev(RADIMO_MAJOR, "radimo");
del_timer(&radimo_timer);

invalidate_buffers(MKDEV(RADIMO_MAJOR,0));

/* remove our request function */
#if LINUX_VERSION_CODE < 0x20320
blk_dev[RADIMO_MAJOR].request_fn = 0;
#else
blk_cleanup_queue(BLK_DEFAULT_QUEUE(RADIMO_MAJOR));
#endif
vfree(radimo_storage);

MSG(RADIMO_INFO, "unloadedn");
}

module_init(radimo_init);
module_exit(radimo_cleanup);

radimo.h

#define RADIMO_MAJOR 42

#define RADIMO_TIMER_DELAY 60*HZ

/* msg masks */
#define RADIMO_OPEN 1
#define RADIMO_IOCTL 2
#define RADIMO_INFO 4
#define RADIMO_REQUEST 8
#define RADIMO_TIMER 16
#define RADIMO_ERROR 32

#ifndef MSG_MASK
#define MSG_MASK ( RADIMO_REQUEST | RADIMO_OPEN | RADIMO_IOCTL | RADIMO_INFO | RADIMO_ERROR | RADIMO_TIMER)
#endif

#define MSG(mask, string, args...)
if (MSG_MASK & mask) printk(KERN_DEBUG "radimo: " string, ##args)

字元類型驅動程式

V1.1

copyright@2006

email: mesmerli@hotmail.com

什麼是字元類型驅動程式

Linux 的驅動程式可區分為以下三種類型:

  1. 字元類型驅動程式-> I/O 驅動程式 -> 磁碟與網路驅動程式之外的驅動程式
  2. 區塊類型驅動程式-> 磁碟驅動程式
  3. 網路類型驅動程式-> 網路驅動程式

LED 驅動程式實驗步驟

Host 端

  1. root file system 組態設定。

    1. 建構新的 root file system (RAMDISK)

      1. dd if=/dev/zero of=ext2new bs=1k count=8192
      2. mke2fs -F -m0 -i 2000 ext2new
      3. mount -w -o loop ext2new /mnt/loop
      4. mount -w -o loop ext2_2418_Creator2410 /mnt/looporg
      5. cp -dpR /mnt/looporg/. /mnt/loop/.
      6. cd /mnt/loop/dev (/dev 檔案夾下,建立 裝置節點)
      7. mknod -m 777 led0 c 44 0
      8. cd /
      9. umount -l /mnt/loop
      10. gzip -9 ext2new
    2. 重新燒錄 root file system。
  2. 建構 LED 驅動程式

    1. 進入 led-demo 目錄
    2. make

Taget 端

  1. 使用 NFS 的方式,mount host 端的目錄,以存取剛剛建立的 LED 驅動程式。

    1. mount 192.168.0.200:/usr/src/creator/nfs /mnt
  1. 安裝驅動程式

    1. cd /dev
    2. mknod -m 777 led0 c 44 0
    3. cd /mnt/Day3/char-driver/led-demo
    4. insmod led-creator.o
  2. 執行應用程式

    1. ./led-demo.exe

led-creator.c

// --------------------------------------------------------------------
//
// Title : led-creator.c
// :
// Library :
// :
// Developers: MICROTIME MDS group (V1.0)
// : mesmerli@gmail.com (V1.1)
// :
// Purpose : Driver for LED of Creator
// :
// Limitation:
// :
// Note :
// :
// --------------------------------------------------------------------
// modification history :
// --------------------------------------------------------------------
// Version| mod. date: |
// V1.0 | 03/05/2004 | First release
// V1.1 | 11/15/2004 | LED by mesmerli
// --------------------------------------------------------------------
//
// Note:
//
// MICROTIME COMPUTER INC.
//
//

#include <linux/config.h>
#include <linux/kernel.h> // 必要的 Header files
#include <linux/module.h>

#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <asm/irq.h>
#include <asm/param.h>
#include <asm/uaccess.h>
#include "asm/arch/irqs.h"

#if LINUX_VERSION_CODE < 0x020100
#define GET_USER(a,b) a = get_user(b)
#else
#include <asm/uaccess.h>
#define GET_USER(a,b) get_user(a,b)
#endif

#include "asm/arch/lib/creator_s3c2410_addr.h" // 硬體暫存器定義
#include "asm/arch/lib/genfont8_8.h"

#include "led-creator.h"

/*
* Define driver major number.
*/
#define MAJOR_NUM LED_MAJOR_NUM
#define MODULE_VERSION "1.10"
#define MODULE_NAME "LED_CREATOR"
#define COPYRIGHT "Copyright (C) 2003-2004, Microtime Computer Inc."
#define MODULE_AUTHOR_STRING "Microtime Computer Inc."
#define MODULE_DESCRIPTION_STRING "Creator led module"

// 硬體控制

#define KEYPAD_SCAN_PERIOD (HZ/5) // 200ms

static UI scan_led=0x5500;

int Creator_led_cmd (int cmd, unsigned char led)
{
int rc = 0;
unsigned char bit ;

switch (cmd){
case LED_IOCTL_SET : {
/*
led value : 1 : 亮, 0 : 不亮
H/W : 0 : 亮, 1 : 不亮
*/
scan_led = ((~led) << 8);

break;
}
case LED_IOCTL_BIT_SET : {
int i ;

if (led >= 8)
return(-EINVAL);

bit = 1;
for (i=0; i < led; i++)
bit <<= 1;

scan_led &= ((~bit) << 8);
break;
}
case LED_IOCTL_BIT_CLEAR : {
int i ;

if (led >= 8)
return(-EINVAL);

bit = 1;
for (i=0; i < led; i++)
bit <<= 1;

scan_led |= (bit << 8);
break;
}
default :
return(-ENOTTY);
}
if (rc == 0)
IO_REG2 = scan_led | 0xfe;

return (rc);
}

/*****************************************************************************/

static int drv_led_open(struct inode *inode, struct file *filp)
{
MOD_INC_USE_COUNT;
return(0);
}
/*****************************************************************************/


static int drv_led_release(struct inode *inode, struct file *filp)
{
MOD_DEC_USE_COUNT;
return 0;
}

int drv_led_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
int rc = 0;

/*
分離type如果遇到錯誤的cmd, 就直接傳回ENOTTY
*/
if (_IOC_TYPE(cmd) != LED_IOCTL_MAGIC) return (-ENOTTY);

switch (cmd) {
case LED_IOCTL_SET :
case LED_IOCTL_BIT_SET :
case LED_IOCTL_BIT_CLEAR : {
unsigned short led;


if (copy_from_user(&led, (unsigned short*)arg, sizeof(unsigned short)))
return (-EINVAL);

if (Creator_led_cmd(cmd, led) < -EFAULT)
return (-EINVAL);
break;
}
default:
rc = -ENOTTY;
break;
}

return(rc);
}

/*
* Exported file operations structure for driver...
*/

struct file_operations drv_led_fops =
{
ioctl: drv_led_ioctl,
open: drv_led_open,
release: drv_led_release,
};

/*****************************************************************************/
static int __init init_module_drv_led(void)
{
int rc;

SET_MODULE_OWNER(&drv_led_fops);

/* Register lcdtxt as character device */
if ((rc = register_chrdev(MAJOR_NUM, MODULE_NAME, &drv_led_fops)) < 0) {
printk("<1>%s: can't get major %dn", MODULE_NAME, MAJOR_NUM);

return (-EBUSY);
}
printk("<1>%s: Version : %s %sn", MODULE_NAME, MODULE_VERSION, COPYRIGHT);

/* Hardware specific initialization */
return 0;
}

static void __exit cleanup_module_drv_led(void)
{
unregister_chrdev(MAJOR_NUM, MODULE_NAME);
printk("<1>%s: removedn", MODULE_NAME);
}

/* here are the compiler macro for module operation */
module_init(init_module_drv_led);
module_exit(cleanup_module_drv_led);

MODULE_AUTHOR(MODULE_AUTHOR_STRING);
MODULE_DESCRIPTION(MODULE_DESCRIPTION_STRING);

EXPORT_NO_SYMBOLS;

/*****************************************************************************/

led-creator.h

//=============================================================================
// File Name : led-creator.h
// Function : LED device drvier definition
// Program :
// Date : 11/15/2004
// Version : 1.10
// History
// 1.0.0 : Programming start (03/05/2004) -> SOP
// 1.1.0 : LED version
//=============================================================================
#ifndef LED_CREATOR_H_
#define LED_CREATOR_H_

#include <linux/config.h>
#if defined(__linux__)
#include <asm/ioctl.h> /* For _IO* macros */
#define LED_IOCTL_NR(n) _IOC_NR(n)
#elif defined(__FreeBSD__)
#include <sys/ioccom.h>
#define LED_IOCTL_NR(n) ((n) & 0xff)
#endif

#define LED_MAJOR_NUM 44
#define LED_IOCTL_MAGIC LED_MAJOR_NUM
#define LED_IO(nr) _IO(LED_IOCTL_MAGIC,nr)
#define LED_IOR(nr,size) _IOR(LED_IOCTL_MAGIC,nr,size)
#define LED_IOW(nr,size) _IOW(LED_IOCTL_MAGIC,nr,size)
#define LED_IOWR(nr,size) _IOWR(LED_IOCTL_MAGIC,nr,size)

/* LED specific ioctls */
/* 設定8個LED Lamps, Low byte 值為有效 */
#define LED_IOCTL_SET LED_IOW( 0x40, unsigned short)
/* 點亮單一個LED lamp */
#define LED_IOCTL_BIT_SET LED_IOW( 0x41, unsigned short)
/* 熄滅單一個LED lamp*/
#define LED_IOCTL_BIT_CLEAR LED_IOW( 0x42, unsigned short)

/* LED define */
#define LED_ALL_ON 0xFF /* 點亮LED Lamp */
#define LED_ALL_OFF 0x00 /* 熄滅LED Lamp */
#define LED_D9_INDEX 0 /* LED 編號D9 (1) */
#define LED_D10_INDEX 1 /* LED 編號D10(2) */
#define LED_D11_INDEX 2 /* LED 編號D11(3) */
#define LED_D12_INDEX 3 /* LED 編號D12(4) */
#define LED_D13_INDEX 4 /* LED 編號D13(5) */
#define LED_D14_INDEX 5 /* LED 編號D14(6) */
#define LED_D15_INDEX 6 /* LED 編號D15(7) */
#define LED_D16_INDEX 7 /* LED 編號D16(8) */

#endif // LED_CREATOR_H_

led-demo.c

#include <signal.h>
#include <stdio.h>
#include <strings.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ioctl.h>

#include "led-creator.h"

int main()
{
int fd;

unsigned int data = 0x0;

int ret;

fd = open("/dev/led0", O_RDWR);
if (fd < 0)
{
printf("open /dev/led0 errorn");
return (-1);
}


while(1)
{
ioctl(fd, LED_IOCTL_SET, &data);

sleep(1);

data++;
if(data >= 0xff)
{
data = 0x0;
}

}

printf("Led Demo!!!n");

return 0;
}

Makefile

CROSS_COMPILE = arm-linux-

LINUXDIR = /usr/src/creator/s3c2410/linux
INCLUDE = $(LINUXDIR)/include
export LINUXDIR

#
# Include the make variables (CC, etc...)
#

AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar

export AS LD CC CPP AR

CFLAGS= -O2 -DMODULE -D__KERNEL__ -DEXPORT_SYMTBL -Wall -I/usr/src/creator/s3c2410/linux/include -Wstrict-prototypes -Wno-trigraphs -Os -mapcs
-fno-strict-aliasing -fno-common -gdwarf-2 -D__linux__ -fno-common -pipe -g -mapcs-32 -march=armv4 -mtune=arm9tdmi
-mshort-load-bytes -msoft-float -DKBUILD_BASENAME=creator_s3c2410_lcd

#CFLAGS= -O0 -Wall -DHAVE_CONFIG_H -D__KERNEL__ -I$(INCLUDE) -DMODULE -DFPM_DEFAULT -Dlinux -Dunix -DNDEBUG -D_REENTRANT -I.
CFLAGS_AP= -O0 -gdwarf-2 -DHAVE_CONFIG_H -DFPM_DEFAULT -Dlinux -Dunix -DNDEBUG -D_REENTRANT -I.

.c.o:
$(CC) $(CFLAGS) -c -o $@ $<

.S.o:
$(CC) $(AFLAGS) -c -o $@ $<

ALL = led-creator.o led-demo.exe

all: $(ALL)

led-creator.o: led-creator.c
$(CC) $(CFLAGS)-c -o $@ $<
led-demo.exe: led-demo.c
$(CC) $(LDFLAGS_AP) -o $@ $^ $(LDLIBS) -L/usr/local/arm/2.95.3/arm-linux/lib /usr/local/arm/2.95.3/arm-linux/lib/libpthread.a
clean:
rm -f *.o *.exe *~ core $(ALL)