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