Linux Device Drivers ETRI
ETRI 2
ETRI 3
File operation special file(=device file) (Character Device Driver) (Block Device Driver) (Network Device Driver) ETRI 4
Ex. (/dev/console), (/dev/ttys0) open, close, read, write Random Access File System Disk, Cdrom, Floppy socket(), bind(). Ethernet, PPP, ATM, ISDN ETRI 5
ETRI 6
ETRI 7
Ex. ttyxx: 4, hdaxx: 3, 2.2, 2.4 256 (0~255) : 0, ( ) Ex. ttys0 : 48, ttys1 : 49 8 ETRI 8
ETRI 9
ETRI 10
struct file_operations init_module, cleanup_module gcc -c -D KERNEL -D MODULE -O2 hello.c register_chrdev(), register_blkdev(), register_netdev() struct file_operations dummy_fops = { open : dummy_open, // open read : dummy_read, // read write : dummy_write, // write release : dummy_release, // release ; ETRI 11
#mknod /dev/ {b,c ) #mknod /dev/led_dd c 124 0 : #chmod ug+w /dev/led_dd #insmod.o ) #insmod led_dd.o #rmmod ) #rmmod led_dd (.o ) #lsmod depmod : (/lib/modules/*/modules.dep ) modprobe : modules.dep,. kerneld : / on modprobe / ETRI 12
main( ) / int init_module(void) : void cleanup_module(void) : dynamic linking : symbol argc/argv MODULE_PARM() : : ( b, h, i, l, s ) (ex, 2-4i ) int myint = 3; int myarray[4]; MODULE_PARM (myint, i ); MODULE_PARM (myarray, 2-4i ); insmod./hello.o myarray=1,4,3 ETRI 13
. #include <linux/module.h> : insmod f (minmor : 2.4.x) symbol (unresolved symbol error ) symbol export (/proc/ksyms ksyms ) inline optimizing : O ETRI 14
init_module() init_module() register_capability() register_capability() printk() cleanup_module() cleanup_module() unregister_capability() unregister_capability() ETRI 15
init_module() main() cleanup_module() register_chrdev(major,, ); unregister_chrdev( ); register_blkdev(major,, ); Unregister_blkdev( ); ETRI 16
file_operations struct file_operations { /* <linux/fs.h> */ struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char *, size_t, loff_t *); ssize_t (*write) (struct file *, const char *, size_t, loff_t *); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); 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 (*flush) (struct file *); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *); ; ETRI 17
file_operations file_operations loff_t (*llseek)(struct file *, loff_t, int); read/write ssize_t (*read)(struct file *, char *, size_t, loff_t); ssize_t (*write)(struct file *, const char *, size_t, loff_t); int (*readaddr)(struct file *, void, filldir_t); unsigned int (*poll)(struct file *, struct poll_table_struct *); int (*ioctl)(strcut inode *, struct file *, unsigned int, unsigned long); ETRI 18
int (*mmap)(struct file *, struct vm_area_struct *); int (*open)(struct inode *, struct file *); int (*flush)(struct file *); int (*release)(struct inode *, struct file *); int (*fsync)(struct file *, struct dentry *); int (*fasync)(int, struct file *, int); int (*lock)(struct file *, int, struct file_lock *); ETRI 19
file_operation block_device_operation int (*open) (struct inode *inode, struct file *filp); int (*release) (struct inode *inode, struct file *filp); int (*ioctl) (struct inode *inode, struct file *filp, unsigned command, unsigned long argument); int (*check_media_change) (kdev_t dev); int (*revalidate) (kdev_t dev); read, write??? I/O, I/O, request request blk_init_queue(), blk_cleanup_queue() ETRI 20
Module init_module() Kernel register_blkdev() blk_init_queue() request() cleanup_module() unregister_blkdev() Blk_cleanup_queue() ETRI 21
module-init-tools (modprobe, insmod, rmmod, depmod, lsmod ) modules.conf modprobe.conf module aliases 2.4 MODULE_PARM module_param(name, type, perm) 2.4 MOD_INC_USE_COUNT try_module_get(&module) module_put() ETRI 22
2.4 version magic (,, SMP, ) link-once ELF 2.4 Makefile. vermagic.o.o.ko 2.4 ANSI C.read = simple_read ETRI 23
2.4 devfs_register(), file_oeprations devfs_mk_cdev() devfs_mk_bdev() workqueue interface Mutual exclusion with seqlocks Sleeping and waking up Dealing with interrupts USB driver API changes open ioctl. ETRI 24
ETRI 25
ETRI 26
OS OS ETRI 27
(base address irq ) ETRI 28
: : CPU : CPU.. I/O I/O I/O I/O get_free_page() : 2, 128KB kmalloc() :,, 128KB vmalloc() : get_user(), put_user() : copy_to_user(), copy_from_user() : ETRI 29
: request_irq() : free_irq() : #cat /proc/interrupts IRQ (cf. Bottom half) Blocking : Non-blocking : gdb CPU, ETRI 30
mmap(), munmap() LED : 0x10600000 LED : 8 8 LED ON/OFF ( 1: ON, 0: OFF) ETRI 31
#include <stdlib.h> #include <stdio.h> #define ADDR_OF_LED 0x10600000 int main(void) { /* */ if ((fd=open("/dev/mem",o_rdwr O_SYNC)) < 0) { perror("mem open fail\n"); exit (1) ; led_addr = mmap(null, 1, PROT_WRITE, MAP_SHARED, fd, ADDR_OF_LED); (1) if ( led_addr < 0 ){ /* */ printf("\n8bit LED IO Blink Example\n"); *led_addr = 0xAA; (2) blink_count = 10; blink_delay = 1000000; for ( i=0; i<blink_count; i++) { *led_addr ^= 0xFF; (3) usleep(blink_delay); (4) *led_addr = 0x00; munmap(led_addr, 1); (5) close(fd); return 0; ETRI 32
ETRI 33
Key Button : 0x10500000 Key Button : 8 8 ( 1: ON, 0: OFF) #include <stdlib.h> #include <unistd.h> #include <sys/mman.h> #include <asm/fcntl.h> #include <stdio.h> #define ADDR_OF_LED 0x10600000 #define ADDR_OF_KEYS0x10500000 int main(void) { int fd; unsigned char *led_addr, *kb_addr; unsigned char kb_input, shift_direction='l'; unsigned char val, val2; if ((fd=open("/dev/mem",o_rdwr O_SYNC)) < 0) { ETRI 34
perror("mem open fail\n"); exit ( 1 ) ; led_addr = mmap(null,1,prot_write,map_shared,fd, ADDR_OF_LED); kb_addr = mmap(null,1,prot_read,map_shared,fd, ADDR_OF_KEYS); (1) if ( led_addr < 0 kb_addr < 0 ) { close(fd); printf("mmap error: led_addr(%x) kb_addr(%x)\n", led_addr, kb_addr); exit(2); printf("\n8bit LED IO & Key Button Control Example\n"); printf("--------------------------------------\n"); printf(" push the s1, then left shift\n"); printf(" push the s8, then right shift\n"); printf(" otherwise exit\n"); printf("--------------------------------------\n"); *led_addr = 0; val = 0; val2 = 0; ETRI 35
while(shift_direction!= 'q') { if(shift_direction == 'l') { (2) val2 = (~( val >> 7)) & 0x1; val = (val << 1) val2; else if( shift_direction == 'r') { val2 = (~(val << 7)) & 0x80; val = (val >> 1) val2; *led_addr = val; (3) kb_input = ~(*kb_addr); (4) if( kb_input!= 0x0) { (5) if((kb_input&0x1) == 0x1) shift_direction = 'r'; else if((kb_input&0x80) == 0x80) shift_direction = 'l'; else shift_direction = 'q'; usleep(100000); *led_addr = 0x00; (6) munmap(led_addr, 1); munmap(kb_addr,1); close(fd); return 0; ETRI 36
#define MODULE #include <linux/module.h> int init_module(void) { printk( <1>Hello World\n ); return 0; void cleanup_module(void) { printk( <1>Goodbye world\n ); root# gcc c hello.c root# insmod hello.o root# rmmod hello ETRI 37
#include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #define DEV_NAME "virtual_device" int virtual_device_open(struct inode *, struct file *); int virtual_device_release(struct inode *, struct file *); ssize_t virtual_device_write(struct file *, const char *, size_t, loff_t *) ; ssize_t virtual_device_read(struct file *, char *, size_t, loff_t *); //Global variable static int virtual_device_usage = 0; static int virtual_device_major = 0; ETRI 38
/* Virtual Device file operation */ static struct file_operations vd_fops = { NULL, NULL, (void *)virtual_device_read, (void *)virtual_device_write, /* */ (void *)virtual_device_open, NULL, (void *)virtual_device_release, /* */ ; int init_module(void) { int major_num; major_num = register_chrdev(0, DEV_NAME,&vd_fops); if(major_num < 0) { /* */ virtual_device_major = major_num; return 0; ETRI 39
void cleanup_module(void) { if( unregister_chrdev(virtual_device_major, DEV_NAME)<0 ) printk(kern_warning"%s DRIVER CLEANUP FAIL\n", DEV_NAME); /* other cleanup code */ // Functions... int virtual_device_open(struct inode *minode, struct file *mfile) { printk("virtual_device open function called!!\n"); if(virtual_device_usage!= 0) return -EBUSY; MOD_INC_USE_COUNT; virtual_device_usage = 1; return 0; int virtual_device_release(struct inode *minode, struct file *mfile) { printk("virtual_device release function called!!\n"); MOD_DEC_USE_COUNT; virtual_device_usage = 0; return 0; ETRI 40
ssize_t virtual_device_write(struct file *inode, const char *gdata, size_t length, loff_t *off_what) { printk("virtual_device write function called!!\n"); return length; ssize_t virtual_device_read(struct file *inode, char *gdata, size_t length, loff_t *off_what) { printk("virtual_device read function called!!\n"); return length; ETRI 41
ETRI 42
, /dev ETRI 43
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #define DEV_NAME "/dev/virtual_device int main(int argc, char **argv) { int dev; char buff[10]; printf( Device driver test.\n ); dev = open (DEV_NAME, O_RDWR); if (dev == -1) { printf( device file open error!!\n ); exit(1); write( dev, "1234", 4 ); real( dev, buff, 4); close(dev); ETRI 44
#rmmod virtual_driver ETRI 45
LCD ETRI 46
LCD : LCD LCD ETRI 47
#include <linux/module.h> /* include */ #include "textlcd_drv.h void setcommand(unsigned short command){ command &= 0x00FF; outl((command 0x0000),TEXTLCDPORT_ADDRESS); udelay(50); void writebyte(char ch) { unsigned short data; data = ch & 0x00FF; outl((data 0x100),TEXTLCDPORT_ADDRESS); udelay(50); void initialize_textlcd(){ function_set(2,0); //Function Set:8bit,display 2lines,5x7 mod display_control(1,0,0); // Display on, Cursor off clear_display(); // Display clear entry_mode_set(1,0); // Entry Mode Set : shift right cursor return_home(); // go home udelay(2000); ETRI 48
void write_string(int row, char *str,int length){ command = row? command + 0x40 : command; setcommand(command); for(i=0;i<length;i++) { /* str LCD */ int function_set(int rows, int nfonts){ unsigned short command = 0x30; if(rows == 2) command = 0x08; else if(rows == 1) command &= 0xf7; else return -1; command = nfonts? (command 0x04) : command; setcommand(command); return 1; int display_control(int display_enable, int cursor_enable, int nblink){ unsigned short command = 0x08; command = display_enable? (command 0x04) : command; command = cursor_enable? (command 0x02) : command; command = nblink? (command 0x01) : command; setcommand(command); return 1; ETRI 49
int cusrsor_shit(int set_screen, int set_rightshit){ command = set_rightshit? (command 0x04) : command; setcommand(command); int entry_mode_set(int increase, int nshift){ setcommand(command); int return_home(){ setcommand(command); int clear_display(){ unsigned short command = 0x01; setcommand(command); int set_ddram_address(int pos){ ETRI 50
/* */ static int textlcdport_open(struct inode *minode, struct file *mfile) { initialize_textlcd(); return 0; static int textlcdport_release(struct inode *minode, struct file *mfile) { MOD_DEC_USE_COUNT; textlcdport_usage = 0; return 0; static ssize_t textlcdport_write(struct file *inode, ) { struct strcommand_varible strcommand; copy_from_user(&strcommand,gdata,32); if(strcommand.rows == 0) write_string(0,strcommand.buf,strcommand.strlength); else if(strcommand.rows == 1) write_string(1,strcommand.buf,strcommand.strlength); return length; ETRI 51
static int textlcdport_ioctl(struct inode *inode, struct file *file,unsigned int cmd,unsigned long gdata) { struct strcommand_varible strcommand; copy_from_user(&strcommand,(char *)gdata,32); switch(cmd){ case TEXTLCD_COMMAND_SET: setcommand(strcommand.command); break; case TEXTLCD_FUNCTION_SET: function_set((int)(strcommand.rows+1), (int)(strcommand.nfonts)); break; case TEXTLCD_DISPLAY_CONTROL: display_control((int)strcommand.display_enable, (int)strcommand.cursor_enable,(int)strcommand.nblink); break; case TEXTLCD_CURSOR_SHIFT: cusrsor_shit((int)strcommand.set_screen, return 0; static struct file_operations textlcd_fops = { write : textlcdport_write, ioctl : textlcdport_ioctl, open : textlcdport_open, release : textlcdport_release, ; ETRI 52
int init_module(void){ int result; result = register_chrdev(textlcdport_major,textlcdport_name, &textlcd_fops); if(result < 0) { printk(kern_warning"can't get any major\n"); return result; textlcdport_major = result; if(!check_region(textlcdport_address,textlcdport_address_range)) request_region(textlcdport_address,textlcdport_address_range, TEXTLCDPORT_NAME); else printk(kern_warning"can't get IO Region 0x%x\n", TEXTLCDPORT_ADDRESS); printk("init module, textlcdport major number : %d\n",result); return 0; void cleanup_module(void) { release_region(textlcdport_address,textlcdport_address_range); if(unregister_chrdev(textlcdport_major,textlcdport_name)) printk(kern_warning"%s DRIVER CLEANUP FALLED\n", TEXTLCDPORT_NAME); ETRI 53
ETRI 54
, /dev ETRI 55
#include <stdio.h> /* */ #include <sys/ioctl.h> #define TEXTLCDPORT_BASE 0xbc #define TEXTLCD_COMMAND_SET _IOW(TEXTLCDPORT_BASE,0,32) #define TEXTLCD_WRITE_BYTE _IOW(TEXTLCDPORT_BASE,8,32) struct strcommand_varible { char rows; char nfonts; char display_enable; char cursor_enable; char nblink; char set_screen; char set_rightshit; char increase; char nshift; char pos; char command; char strlength; char buf[20]; ; ETRI 56
int main(int argc, char **argv) { int i,dev; char welcome[20] = "Welcome to"; char welcome2[20] = "the embeded World!"; struct strcommand_varible strcommand; strcommand.rows = 0; /* strcommand */ strcommand.command = 1; printf("\nlcd Test program!!\n"); dev = open("/dev/textlcd_driver", O_WRONLY O_NDELAY ); if (dev!= -1) { ioctl(dev,textlcd_clear,&strcommand,sizeof(strcommand)); printf("write: %s\n", welcome); strcommand.pos = 10; strcommand.strlength = strlen(welcome); memcpy(strcommand.buf, welcome,20); write(dev, &strcommand, sizeof(strcommand)); strcommand.pos = 40; ioctl(dev,textlcd_dd_address,&strcommand,sizeof(strcommand)); printf("write: %s\n", welcome2); for(i=0;i<strlen(welcome2);i++) { memcpy(&strcommand.buf[0],&welcome2[i],1); ioctl(dev,textlcd_write_byte,&strcommand,sizeof(strcommand)); close(dev); ETRI 57
#rmmod textlcd_driver ETRI 58
ETRI 59
USB Host Controller USB Devices and Transfer Characteristics Enumeration and Device Descriptors USB Device Drivers USB Configuring USB Devices USB Transfers ETRI 60
PC USB 1.0 127, 12Mbit/s USB 2.0, Mater/Slave, ETRI 61
Compaq, Microsoft, National Semiconductor, Intel,, CPU ETRI 62
Self Powered: Bus Powered: USB Both: Low speed device:, 1.5Mbit/s Full speed device:, 12Mbit/s 90% 10Mbit/s ETRI 63
ETRI 64
3 reguest, send, configure send, request, Ex., SCSI polling Bulk interrupt request ETRI 65
-GET_STATUS -CLEAR_FEATURE -SET_FEATURE -SET_ADDRESS -GET_DESCRIPTOR -SET_DESCRIPTOR -GET_CONFIGURATION -SET_CONFIGURATION -GET_INTERFACE -SET_INTERFACE -SYNCH_FRAME ETRI 66
ETRI 67
USB USB, 1~127 ETRI 68
USB Spec. ETRI 69
,, ETRI 70
,,,,,,,,, Application USB Core application Application interface ETRI 71
2 USB 180 16 ETRI 72
struct usb_driver { const char *name; void *(*probe)(struct usb_device *, unsigned int); void (*disconnect)(struct usb_device *, void *); struct list_head driver_list; struct file_operation *fops; int minor; name: module probe:,,, disconnect: USB, probe driver_list:, {NULL, NULL fops: minor:, 16 ETRI 73
void *probe( struct usb_device *dev, unsigned int interface ) { struct driver_context *context; /* Device Vendor ID Product ID interface.*/ if( dev->descriptor.idvendor == 0xXXXX && dev->descriptor.idproduct == 0xXXXX && interface == 1 ) { MOD_INC_USE_COUNT; /* Module usage count.*/ /* Driver resource. */ context = allocate_driver_resources(); /* Bind device context return.*/ return context; return NULL; ETRI 74
Static void disconnect(struct usb_device *usbdev, void *drv_context) { /* driver_context pointer.*/ struct driver_context *s = drv_context; /* remove pending flag setting. */ s->remove_pending = 1; /* driver sleeping part.*/ wake_up( &s->wait ); /* Driver device data structure release. */ sleep_on( &s->remove_ok ); /* resource free.*/ free_driver_resources(s); /* Module usage count.*/ MOD_DEC_USE_COUNT; ETRI 75
USB int usb_register(struct usb_driver *drv); void usb_deregister(struct usb_driver *drv); void usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void *drv_context); Probe int usb_interdace_claimed(struct usb_interface *iface); void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface); ETRI 76
int usb_set_configuration(struct usb_device *dev, int configuratoin); int usb_set_interface(struct usb_device *dev, int interface, int alternate); int usb_get_device_descriptor(struct usb_device *dev); int usb_get_descriptor(struct usb_device *dev, unsigned char desctype, unsigned char descindex, void *buf, int size); int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char index, void *buf, int size); int usb_string(struct usb_device *dev, int index, char *buf, size_t size); int usb_get_status(struct usb_device *dev, int type, int target, void *data); Int usb_clear_halt(struct usb_device *dev, int pipe); ETRI 77
int usb_get_protocol(struct usb_device *dev); int usb_set_protocol(struct usb_device *dev, int protocol); int usb_get_report(struct usb_device *dev, unsigned char type, unsigned char id, unsigned char index, void *buf, int size); int usb_set_idle(struct usb_device *dev, int duration, int report_id); ETRI 78
A B C A1 A2 A3 B1 B2 B3 C1 C2 C3 A1 B1 C1 A2 B2 C2 A3 B3 C3 Token Data Handshake ETRI 79
Control transfer Control information control endpoint,. Ex.) device descriptor host input control transfer Bulk transfer bulk endpoint 64 bytes data. IN/OUT, IN, OUT data device. lossless data, latency., host (bandwidth) bulk transfer. Interrupt transfer bulk transfer interrupt endpoint, interrupt endpoint 64 bytes. interrupt endpoint, polling interval 1 255 millisecond. Isochronous transfer 1023 bytes data isochronous endpoint, isochronous enpoint. data, (audio). ETRI 80
// Isochronous packet descriptor. typedef struct { unsigned int offset; unsigned int length; // expected length unsigned int actual_length; unsigned int status; iso_packet_descriptor_t, *piso_packet_descriptor_t; struct urb; typedef void (*usb_complete_t)(struct urb *); // URB data structure typedef struct urb { spinlock_t lock; // lock for the URB void *hcpriv; // private data for host controller struct list_head urb_list; // list pointer to all active urbs struct urb *next; // pointer to next URB struct usb_device *dev; // pointer to associated USB device unsigned int pipe; // pipe information int status; // returned status unsigned int transfer_flags; //USB_DISABLE_SPD USB_ISO_ASAP USB_URB_EARLY_COMPLETE void *transfer_buffer; // associated data buffer int transfer_buffer_length; // data buffer length int actual_length; // actual data buffer length unsigned char *setup_packet; // setup packet (control only) // int start_frame; // start frame (iso/irq only) int number_of_packets; // number of packets in this request (iso/irq only) int interval; // polling interval (irq only) int error_count; // number of errors in this transfer (iso only) int timeout; // timeout (in jiffies) void *context; usb_complete_t complete; // context for completion routine // pointer to completion routine iso_packet_descriptor_t iso_frame_desc[0]; urb_t, ETRI *purb_t; 81
Ex. ETRI 82
Low speed: 5Mbps Full speed: 15Mbps High speed: 480Mbps ETRI 83
ETRI 84