Designing and developing device drivers Coding drivers
Registering a driver 2 calls to register a driver defined in <linux/fs.h> int register_chrdev_region(dev_t first, unsigned int count, char *name); int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name); Use alloc_chrddev_region They allocate major numbers and register the device Show up in /proc/devices and /sysfs
/proc/devices Character devices: 1 mem 2 pty 3 ttyp 4 ttys 6 lp 7 vcs 10 misc 13 input 14 sound 21 sg 180 usb Block devices: 2 fd 8 sd 11 sr 65 sd 66 sd
Dynamic allocation Eventually all major numbers will be dynamically allocated A script should be provided to initialise the device Can be in /etc/init.d/ A lot depends upon whether and how the driver is to be distributed
A init script #!/bin/sh module="scull" device="scull" mode="664" #invoke insmod with all arguments we got #and use a pathname, as newer modutils don't look in. by default /sbin/insmod./$module.ko $* exit 1 #remove stale nodes rm -f /dev/${device}[0-3] major=$(awk "\\$2==\"$module\" {print \\$1}" /proc/devices) mknod /dev/${device}0 c $major 0 mknod /dev/${device}1 c $major 1 mknod /dev/${device}2 c $major 2 mknod /dev/${device}3 c $major 3 #give appropriate group/permissions, and change the group. #Not all distributions have staff, some have "wheel" instead. group="staff" grep -q '^staff:' /etc/group group="wheel" chgrp $group /dev/${device}[0-3] chmod $mode /dev/${device}[0-3]
Char device de registration To de register or release a device call void unregister_chrdev_region(dev_t first, unsigned int count);
Important kernel data structures There are 3 important kernel data structures for device drvier writers File operations fops A field in File The File struct Not to be confused with stdio FILE The Inode struct
fops file operations In <linux/fs.h> All of the common calls that can be made on a file loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char user *, size_t, loff_t *); ssize_t (*aio_read)(struct kiocb *, char user *, size_t, loff_t); ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*release) (struct inode *, struct file *);
The FILE structure In <linux/fs.h> Used for every open file. Not specific to device drivers mode_t f_mode; loff_t f_pos; unsigned int f_flags; struct file_operations *f_op; void *private_data;
The inode structure Has a large amount of disc, file system and kernel information There are 2 fields important for drivers dev_t i_rdev; Use the macros iminor and imajor to access this struct cdev *i_cdev; The data structure for character devices
struct cdev Can be initialised directly from <linux/cdev.h> struct cdev *my_cdev = cdev_alloc( ); my_cdev >ops = &my_fops; Or embedded in driver s main data structure void cdev_init(struct cdev *cdev, struct file_operations *fops); Finally the kernel has to add the new cdev int cdev_add(struct cdev *dev, dev_t num, unsigned int count); The cdev can be removed with void cdev_del(struct cdev *dev);
The old method In most drivers you work with the older method of character device registration will be used int register_chrdev(unsigned int major, const char *name, struct file_operations *fops); To unregister int unregister_chrdev(unsigned int major, const char *name);
sa1100 audio.h typedef struct { char *id; /* identification string */ struct device *dev; /* device */ audio_buf_t *buffers; /* pointer to audio buffer structures */ u_int usr_head; /* user fragment index */ u_int dma_head; /* DMA fragment index to go */ u_int dma_tail; /* DMA fragment index to complete */ u_int fragsize; /* fragment i.e. buffer size */ u_int nbfrags; /* nbr of fragments i.e. buffers */ u_int pending_frags; /* Fragments sent to DMA */ dma_device_t dma_dev; /* device identifier for DMA */ dma_regs_t *dma_regs; /* points to our DMA registers */ int bytecount; /* nbr of processed bytes */ int fragcount; /* nbr of fragment transitions */ struct semaphore sem; /* account for fragment usage */ wait_queue_head_t wq; /* for poll */ int dma_spinref; /* DMA is spinning */ int mapped:1; /* mmap()'ed buffers */ int active:1; /* actually in progress */ int stopped:1; /* might be active but stopped */ int spin_idle:1; /* have DMA spin on zeros when idle */ int sa1111_dma:1; /* DMA handled by SA1111 */ } audio_stream_t; /* * State structure for one instance */ typedef struct { audio_stream_t *output_stream; audio_stream_t *input_stream; int rd_ref:1; /* open reference for recording */ int wr_ref:1; /* open reference for playback */ int need_tx_for_rx:1; /* if data must be sent while receiving */ void *data; void (*hw_init)(void *); void (*hw_shutdown)(void *); int (*client_ioctl)(struct inode *, struct file *, uint, ulong); struct semaphore sem; /* to protect against races in attach() */ } audio_state_t;
Audio functions and fileops extern int sa1100_audio_attach( struct inode *inode, struct file *file, audio_state_t *state); extern ssize_t audio_write(struct file *file, const char *buffer, size_t count, loff_t * ppos); int sa1100_audio_suspend(audio_state_t *s, pm_message_t state); int sa1100_audio_resume(audio_state_t *s); extern int audio_read(struct file *file, char *buffer, size_t count, loff_t * ppos); static struct file_operations dev_fileops = {.owner = THIS_MODULE,.read = audio_read,.write = audio_write,.ioctl = audio_ioctl,.release = audio_release,.mmap = audio_mmap,.poll = audio_poll,.llseek = audio_llseek, };
sa1100_audio_attach int sa1100_audio_attach(struct inode *inode, struct file *file, audio_state_t *state) { audio_stream_t *os = state >output_stream; audio_stream_t *is = state >input_stream; int err, need_tx_dma; int seq1,seq2; DPRINTK("sa1100_audio_attach fmode %d\n",file >f_mode); file >private_data=state; os >dev >coherent_dma_mask=0xffffffff; down(&state >sem); /* access control */ err = ENODEV; if ((file >f_mode & FMODE_WRITE) &&!os) goto out; if ((file >f_mode & FMODE_READ) &&!is) goto out; err = EBUSY; if ((file >f_mode & FMODE_WRITE) && state >wr_ref) goto out; if ((file >f_mode & FMODE_READ) && state >rd_ref) goto out;
ucb1x00 core.c static int ucb1x00_probe(struct mcp *mcp) { struct ucb1x00 *ucb; struct ucb1x00_driver *drv; unsigned int id; int ret = ENODEV;...... mcp_enable(mcp); id = mcp_reg_read(mcp, UCB_ID); printk("ucb1x00_probe is is %x\n",id); if (id!= UCB_ID_1200 && id!= UCB_ID_1300 && id!= UCB_ID_TC35143) { printk(kern_warning "UCB1x00 ID not found: %04x\n", id); goto err_disable; } Major = register_chrdev(0,device_name,&fops); printk("device Number %d\n",major); goto out;