Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  1 /*
  2  * Sample disk driver, from the beginning.
  3  */
  4 
  5 #include <linux/config.h>
  6 #include <linux/module.h>
  7 #include <linux/moduleparam.h>
  8 #include <linux/init.h>
  9 
 10 #include <linux/sched.h>
 11 #include <linux/kernel.h>       /* printk() */
 12 #include <linux/slab.h>         /* kmalloc() */
 13 #include <linux/fs.h>           /* everything... */
 14 #include <linux/errno.h>        /* error codes */
 15 #include <linux/timer.h>
 16 #include <linux/types.h>        /* size_t */
 17 #include <linux/fcntl.h>        /* O_ACCMODE */
 18 #include <linux/hdreg.h>        /* HDIO_GETGEO */
 19 #include <linux/kdev_t.h>
 20 #include <linux/vmalloc.h>
 21 #include <linux/genhd.h>
 22 #include <linux/blkdev.h>
 23 #include <linux/buffer_head.h>  /* invalidate_bdev */
 24 #include <linux/bio.h>
 25 
 26 MODULE_LICENSE("Dual BSD/GPL");
 27 
 28 static int sbull_major = 0;
 29 module_param(sbull_major, int, 0);
 30 static int hardsect_size = 512;
 31 module_param(hardsect_size, int, 0);
 32 static int nsectors = 1024;     /* How big the drive is */
 33 module_param(nsectors, int, 0);
 34 static int ndevices = 4;
 35 module_param(ndevices, int, 0);
 36 
 37 /*
 38  * The different "request modes" we can use.
 39  */
 40 enum {
 41         RM_SIMPLE  = 0, /* The extra-simple request function */
 42         RM_FULL    = 1, /* The full-blown version */
 43         RM_NOQUEUE = 2, /* Use make_request */
 44 };
 45 static int request_mode = RM_SIMPLE;
 46 module_param(request_mode, int, 0);
 47 
 48 /*
 49  * Minor number and partition management.
 50  */
 51 #define SBULL_MINORS    16
 52 #define MINOR_SHIFT     4
 53 #define DEVNUM(kdevnum) (MINOR(kdev_t_to_nr(kdevnum)) >> MINOR_SHIFT
 54 
 55 /*
 56  * We can tweak our hardware sector size, but the kernel talks to us
 57  * in terms of small sectors, always.
 58  */
 59 #define KERNEL_SECTOR_SIZE      512
 60 
 61 /*
 62  * After this much idle time, the driver will simulate a media change.
 63  */
 64 #define INVALIDATE_DELAY        30*HZ
 65 
 66 /*
 67  * The internal representation of our device.
 68  */
 69 struct sbull_dev {
 70         int size;                       /* Device size in sectors */
 71         u8 *data;                       /* The data array */
 72         short users;                    /* How many users */
 73         short media_change;             /* Flag a media change? */
 74         spinlock_t lock;                /* For mutual exclusion */
 75         struct request_queue *queue;    /* The device request queue */
 76         struct gendisk *gd;             /* The gendisk structure */
 77         struct timer_list timer;        /* For simulated media changes */
 78 };
 79 
 80 static struct sbull_dev *Devices = NULL;
 81 
 82 /*
 83  * Handle an I/O request.
 84  */
 85 static void sbull_transfer(struct sbull_dev *dev, unsigned long sector,
 86                 unsigned long nsect, char *buffer, int write)
 87 {
 88         unsigned long offset = sector*KERNEL_SECTOR_SIZE;
 89         unsigned long nbytes = nsect*KERNEL_SECTOR_SIZE;
 90 
 91         if ((offset + nbytes) > dev->size) {
 92                 printk (KERN_NOTICE "Beyond-end write (%ld %ld)\n", offset, nbytes);
 93                 return;
 94         }
 95         if (write)
 96                 memcpy(dev->data + offset, buffer, nbytes);
 97         else
 98                 memcpy(buffer, dev->data + offset, nbytes);
 99 }
100 
101 /*
102  * The simple form of the request function.
103  */
104 static void sbull_request(request_queue_t *q)
105 {
106         struct request *req;
107 
108         while ((req = elv_next_request(q)) != NULL) {
109                 struct sbull_dev *dev = req->rq_disk->private_data;
110                 if (! blk_fs_request(req)) {
111                         printk (KERN_NOTICE "Skip non-fs request\n");
112                         end_request(req, 0);
113                         continue;
114                 }
115     //          printk (KERN_NOTICE "Req dev %d dir %ld sec %ld, nr %d f %lx\n",
116     //                          dev - Devices, rq_data_dir(req),
117     //                          req->sector, req->current_nr_sectors,
118     //                          req->flags);
119                 sbull_transfer(dev, req->sector, req->current_nr_sectors,
120                                 req->buffer, rq_data_dir(req));
121                 end_request(req, 1);
122         }
123 }
124 
125 
126 /*
127  * Transfer a single BIO.
128  */
129 static int sbull_xfer_bio(struct sbull_dev *dev, struct bio *bio)
130 {
131         int i;
132         struct bio_vec *bvec;
133         sector_t sector = bio->bi_sector;
134 
135         /* Do each segment independently. */
136         bio_for_each_segment(bvec, bio, i) {
137                 char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
138                 sbull_transfer(dev, sector, bio_cur_sectors(bio),
139                                 buffer, bio_data_dir(bio) == WRITE);
140                 sector += bio_cur_sectors(bio);
141                 __bio_kunmap_atomic(bio, KM_USER0);
142         }
143         return 0; /* Always "succeed" */
144 }
145 
146 /*
147  * Transfer a full request.
148  */
149 static int sbull_xfer_request(struct sbull_dev *dev, struct request *req)
150 {
151         struct bio *bio;
152         int nsect = 0;
153     
154         rq_for_each_bio(bio, req) {
155                 sbull_xfer_bio(dev, bio);
156                 nsect += bio->bi_size/KERNEL_SECTOR_SIZE;
157         }
158         return nsect;
159 }
160 
161 
162 
163 /*
164  * Smarter request function that "handles clustering".
165  */
166 static void sbull_full_request(request_queue_t *q)
167 {
168         struct request *req;
169         int sectors_xferred;
170         struct sbull_dev *dev = q->queuedata;
171 
172         while ((req = elv_next_request(q)) != NULL) {
173                 if (! blk_fs_request(req)) {
174                         printk (KERN_NOTICE "Skip non-fs request\n");
175                         end_request(req, 0);
176                         continue;
177                 }
178                 sectors_xferred = sbull_xfer_request(dev, req);
179                 if (! end_that_request_first(req, 1, sectors_xferred)) {
180                         blkdev_dequeue_request(req);
181                         end_that_request_last(req);
182                 }
183         }
184 }
185 
186 
187 
188 /*
189  * The direct make request version.
190  */
191 static int sbull_make_request(request_queue_t *q, struct bio *bio)
192 {
193         struct sbull_dev *dev = q->queuedata;
194         int status;
195 
196         status = sbull_xfer_bio(dev, bio);
197         bio_endio(bio, bio->bi_size, status);
198         return 0;
199 }
200 
201 
202 /*
203  * Open and close.
204  */
205 
206 static int sbull_open(struct inode *inode, struct file *filp)
207 {
208         struct sbull_dev *dev = inode->i_bdev->bd_disk->private_data;
209 
210         del_timer_sync(&dev->timer);
211         filp->private_data = dev;
212         spin_lock(&dev->lock);
213         if (! dev->users) 
214                 check_disk_change(inode->i_bdev);
215         dev->users++;
216         spin_unlock(&dev->lock);
217         return 0;
218 }
219 
220 static int sbull_release(struct inode *inode, struct file *filp)
221 {
222         struct sbull_dev *dev = inode->i_bdev->bd_disk->private_data;
223 
224         spin_lock(&dev->lock);
225         dev->users--;
226 
227         if (!dev->users) {
228                 dev->timer.expires = jiffies + INVALIDATE_DELAY;
229                 add_timer(&dev->timer);
230         }
231         spin_unlock(&dev->lock);
232 
233         return 0;
234 }
235 
236 /*
237  * Look for a (simulated) media change.
238  */
239 int sbull_media_changed(struct gendisk *gd)
240 {
241         struct sbull_dev *dev = gd->private_data;
242         
243         return dev->media_change;
244 }
245 
246 /*
247  * Revalidate.  WE DO NOT TAKE THE LOCK HERE, for fear of deadlocking
248  * with open.  That needs to be reevaluated.
249  */
250 int sbull_revalidate(struct gendisk *gd)
251 {
252         struct sbull_dev *dev = gd->private_data;
253         
254         if (dev->media_change) {
255                 dev->media_change = 0;
256                 memset (dev->data, 0, dev->size);
257         }
258         return 0;
259 }
260 
261 /*
262  * The "invalidate" function runs out of the device timer; it sets
263  * a flag to simulate the removal of the media.
264  */
265 void sbull_invalidate(unsigned long ldev)
266 {
267         struct sbull_dev *dev = (struct sbull_dev *) ldev;
268 
269         spin_lock(&dev->lock);
270         if (dev->users || !dev->data) 
271                 printk (KERN_WARNING "sbull: timer sanity check failed\n");
272         else
273                 dev->media_change = 1;
274         spin_unlock(&dev->lock);
275 }
276 
277 /*
278  * The ioctl() implementation
279  */
280 
281 int sbull_ioctl (struct inode *inode, struct file *filp,
282                  unsigned int cmd, unsigned long arg)
283 {
284         long size;
285         struct hd_geometry geo;
286         struct sbull_dev *dev = filp->private_data;
287 
288         switch(cmd) {
289             case HDIO_GETGEO:
290                 /*
291                  * Get geometry: since we are a virtual device, we have to make
292                  * up something plausible.  So we claim 16 sectors, four heads,
293                  * and calculate the corresponding number of cylinders.  We set the
294                  * start of data at sector four.
295                  */
296                 size = dev->size*(hardsect_size/KERNEL_SECTOR_SIZE);
297                 geo.cylinders = (size & ~0x3f) >> 6;
298                 geo.heads = 4;
299                 geo.sectors = 16;
300                 geo.start = 4;
301                 if (copy_to_user((void __user *) arg, &geo, sizeof(geo)))
302                         return -EFAULT;
303                 return 0;
304         }
305 
306         return -ENOTTY; /* unknown command */
307 }
308 
309 
310 
311 /*
312  * The device operations structure.
313  */
314 static struct block_device_operations sbull_ops = {
315         .owner           = THIS_MODULE,
316         .open            = sbull_open,
317         .release         = sbull_release,
318         .media_changed   = sbull_media_changed,
319         .revalidate_disk = sbull_revalidate,
320         .ioctl           = sbull_ioctl
321 };
322 
323 
324 /*
325  * Set up our internal device.
326  */
327 static void setup_device(struct sbull_dev *dev, int which)
328 {
329         /*
330          * Get some memory.
331          */
332         memset (dev, 0, sizeof (struct sbull_dev));
333         dev->size = nsectors*hardsect_size;
334         dev->data = vmalloc(dev->size);
335         if (dev->data == NULL) {
336                 printk (KERN_NOTICE "vmalloc failure.\n");
337                 return;
338         }
339         spin_lock_init(&dev->lock);
340         
341         /*
342          * The timer which "invalidates" the device.
343          */
344         init_timer(&dev->timer);
345         dev->timer.data = (unsigned long) dev;
346         dev->timer.function = sbull_invalidate;
347         
348         /*
349          * The I/O queue, depending on whether we are using our own
350          * make_request function or not.
351          */
352         switch (request_mode) {
353             case RM_NOQUEUE:
354                 dev->queue = blk_alloc_queue(GFP_KERNEL);
355                 if (dev->queue == NULL)
356                         goto out_vfree;
357                 blk_queue_make_request(dev->queue, sbull_make_request);
358                 break;
359 
360             case RM_FULL:
361                 dev->queue = blk_init_queue(sbull_full_request, &dev->lock);
362                 if (dev->queue == NULL)
363                         goto out_vfree;
364                 break;
365 
366             default:
367                 printk(KERN_NOTICE "Bad request mode %d, using simple\n", request_mode);
368                 /* fall into.. */
369         
370             case RM_SIMPLE:
371                 dev->queue = blk_init_queue(sbull_request, &dev->lock);
372                 if (dev->queue == NULL)
373                         goto out_vfree;
374                 break;
375         }
376         blk_queue_hardsect_size(dev->queue, hardsect_size);
377         dev->queue->queuedata = dev;
378         /*
379          * And the gendisk structure.
380          */
381         dev->gd = alloc_disk(SBULL_MINORS);
382         if (! dev->gd) {
383                 printk (KERN_NOTICE "alloc_disk failure\n");
384                 goto out_vfree;
385         }
386         dev->gd->major = sbull_major;
387         dev->gd->first_minor = which*SBULL_MINORS;
388         dev->gd->fops = &sbull_ops;
389         dev->gd->queue = dev->queue;
390         dev->gd->private_data = dev;
391         snprintf (dev->gd->disk_name, 32, "sbull%c", which + 'a');
392         set_capacity(dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));
393         add_disk(dev->gd);
394         return;
395 
396   out_vfree:
397         if (dev->data)
398                 vfree(dev->data);
399 }
400 
401 
402 
403 static int __init sbull_init(void)
404 {
405         int i;
406         /*
407          * Get registered.
408          */
409         sbull_major = register_blkdev(sbull_major, "sbull");
410         if (sbull_major <= 0) {
411                 printk(KERN_WARNING "sbull: unable to get major number\n");
412                 return -EBUSY;
413         }
414         /*
415          * Allocate the device array, and initialize each one.
416          */
417         Devices = kmalloc(ndevices*sizeof (struct sbull_dev), GFP_KERNEL);
418         if (Devices == NULL)
419                 goto out_unregister;
420         for (i = 0; i < ndevices; i++) 
421                 setup_device(Devices + i, i);
422     
423         return 0;
424 
425   out_unregister:
426         unregister_blkdev(sbull_major, "sbd");
427         return -ENOMEM;
428 }
429 
430 static void sbull_exit(void)
431 {
432         int i;
433 
434         for (i = 0; i < ndevices; i++) {
435                 struct sbull_dev *dev = Devices + i;
436 
437                 del_timer_sync(&dev->timer);
438                 if (dev->gd) {
439                         del_gendisk(dev->gd);
440                         put_disk(dev->gd);
441                 }
442                 if (dev->queue) {
443                         if (request_mode == RM_NOQUEUE)
444                                 blk_put_queue(dev->queue);
445                         else
446                                 blk_cleanup_queue(dev->queue);
447                 }
448                 if (dev->data)
449                         vfree(dev->data);
450         }
451         unregister_blkdev(sbull_major, "sbull");
452         kfree(Devices);
453 }
454         
455 module_init(sbull_init);
456 module_exit(sbull_exit);
457 
  This page was automatically generated by the LXR engine.