Sie sind auf Seite 1von 34

Serialdrivers

Serialdrivers
ThomasPetazzoni FreeElectrons

FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Copyright2009,FreeElectrons. CreativeCommonsBYSA3.0license Latestupdate:Mar20,2013, Documentsources,updatesandtranslations: http://freeelectrons.com/docs/serialdrivers Corrections,suggestions,contributionsandtranslationsarewelcome!

Architecture(1)
Systemcallinterface Line discipline

Characterdriver TTYCore

Characterdriver

TTYdriver

TTYdriver serial_core

Serialdriver
2
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Architecture(2)
TobeproperlyintegratedinaLinuxsystem,serialportsmustbe visibleasTTYdevicesfromuserspaceapplications Therefore,theserialdrivermustbepartofthekernelTTY subsystem Until2.6,serialdriverswereimplementeddirectlybehindtheTTY core
Alotofcomplexitywasinvolved

Since2.6,aspecializedTTYdriver,serial_core,easesthe developmentofserialdrivers
Seeinclude/linux/serial_core.hforthemaindefinitionsof theserial_coreinfrastructure
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Thelinedisciplinethatcooksthedataexchangedwiththetty

Datastructures
Adatastructurerepresentingadriver:uart_driver
Singleinstanceforeachdriver uart_register_driver()anduart_unregister_driver()

Adatastructurerepresentingaport:uart_port
Oneinstanceforeachport(severalperdriverarepossible) uart_add_one_port()anduart_remove_one_port()

Adatastructurecontainingthepointerstotheoperations: uart_ops
Linkedfromuart_portthroughtheopsfield

4
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

uart_driver
Usually
Definedstaticallyinthedriver Registeredinmodule_init() Unregisteredinmodule_cleanup()

Contains
owner,usuallysettoTHIS_MODULE driver_name dev_name,thedevicenameprefix,usuallyttyS majorandminor
UseTTY_MAJORand64togetthenormalnumbers.Buttheymight conflictwiththe8250reservednumbers
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

nr,themaximumnumberofports

uart_drivercodeexample(1)
staticstructuart_driveratmel_uart={ .owner=THIS_MODULE, .driver_name="atmel_serial", .dev_name=ATMEL_DEVICENAME, .major=SERIAL_ATMEL_MAJOR, .minor=MINOR_START, .nr=ATMEL_MAX_UART, .cons=ATMEL_CONSOLE_DEVICE, }; staticstructplatform_driveratmel_serial_driver={ .probe=atmel_serial_probe, .remove=__devexit_p(atmel_serial_remove), .suspend=atmel_serial_suspend, .resume=atmel_serial_resume, .driver={ .name="atmel_usart", .owner=THIS_MODULE, }, };

Examplecodefromdrivers/serial/atmel_serial.c

FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

uart_drivercodeexample(2)

staticint__initatmel_serial_init(void) { uart_register_driver(&atmel_uart); platform_driver_register(&atmel_serial_driver); return0; } staticvoid__exitatmel_serial_exit(void) { platform_driver_unregister(&atmel_serial_driver); uart_unregister_driver(&atmel_uart); } module_init(atmel_serial_init); module_exit(atmel_serial_exit);

Warning:error management removed!

7
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

uart_port
Canbeallocatedstaticallyordynamically Usuallyregisteredatprobe()timeandunregisteredat remove()time Mostimportantfields
iotype,typeofI/Oaccess,usuallyUPIO_MEMformemory mappeddevices mapbase,physicaladdressoftheregisters irq,theIRQchannelnumber membase,thevirtualaddressoftheregisters uartclk,theclockrate ops,pointertotheoperations
8

FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

uart_portcodeexample(1)
staticint__devinitatmel_serial_probe(structplatform_device*pdev) { structatmel_uart_port*port; port=&atmel_ports[pdev>id]; port>backup_imr=0; atmel_init_port(port,pdev); uart_add_one_port(&atmel_uart,&port>uart); platform_set_drvdata(pdev,port); return0; } staticint__devexitatmel_serial_remove(structplatform_device*pdev) { structuart_port*port=platform_get_drvdata(pdev); platform_set_drvdata(pdev,NULL); uart_remove_one_port(&atmel_uart,port); return0; }

9
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

uart_portcodeexample(2)
staticvoid__devinitatmel_init_port(structatmel_uart_port*atmel_port, structplatform_device*pdev) { structuart_port*port=&atmel_port>uart; structatmel_uart_data*data=pdev>dev.platform_data; port>iotype=UPIO_MEM; port>flags=UPF_BOOT_AUTOCONF; port>ops=&atmel_pops; port>fifosize=1; port>line=pdev>id; port>dev=&pdev>dev; port>mapbase=pdev>resource[0].start; port>irq=pdev>resource[1].start; tasklet_init(&atmel_port>tasklet,atmel_tasklet_func, (unsignedlong)port); [...seenextpage...]

10
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

uart_portcodeexample(3)
[...continuedfrompreviouspage...] if(data>regs) /*Alreadymappedbysetupcode*/ port>membase=data>regs; else{ port>flags|=UPF_IOREMAP; port>membase=NULL; } /*forconsole,theclockcouldalreadybeconfigured*/ if(!atmel_port>clk){ atmel_port>clk=clk_get(&pdev>dev,"usart"); clk_enable(atmel_port>clk); port>uartclk=clk_get_rate(atmel_port>clk); clk_disable(atmel_port>clk); /*onlyenableclockwhenUSARTisinuse*/ } }

11
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

uart_ops
Importantoperations
tx_empty(),tellswhetherthetransmissionFIFOisemptyornot set_mctrl()andget_mctrl(),allowtosetandgetthemodem controlparameters(RTS,DTR,LOOP,etc.) start_tx()andstop_tx(),tostartandstopthetransmission stop_rx(),tostopthereception startup()andshutdown(),calledwhentheportis opened/closed request_port()andrelease_port(),request/releaseI/Oor memoryregions set_termios(),changeportparameters

Seethedetaileddescriptionin

12

FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Implementingtransmission
Thestart_tx()methodshouldstarttransmittingcharacters overtheserialport Thecharacterstotransmitarestoredinacircularbuffer, implementedbyastructuart_circstructure.Itcontains
buf[],thebufferofcharacters tail,theindexofthenextcharactertotransmit.Aftertransmit,tail mustbeupdatedusingtail=tail&(UART_XMIT_SIZE 1)

Utilityfunctionsonuart_circ
uart_circ_empty(),tellswhetherthecircularbufferisempty uart_circ_chars_pending(),returnsthenumberofcharacters lefttotransmit
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Fromanuart_portpointer,thisstructurecanbereachedusing

13

Polledmodetransmission

foo_uart_putc(structuart_port*port,unsignedcharc){ while(__raw_readl(port>membase+UART_REG1)&UART_TX_FULL) cpu_relax(); __raw_writel(c,port>membase+UART_REG2); } foo_uart_start_tx(structuart_port*port){ structcirc_buf*xmit=&port>state>xmit; while(!uart_circ_empty(xmit)){ foo_uart_putc(port,xmit>buf[xmit>tail]); xmit>tail=(xmit>tail+1)&(UART_XMIT_SIZE1); port>icount.tx++; } }

14
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Transmissionwithinterrupts(1)

foo_uart_interrupt(intirq,void*dev_id){ [...] if(interrupt_cause&END_OF_TRANSMISSION) foo_uart_handle_transmit(port); [...] } foo_uart_start_tx(structuart_port*port){ enable_interrupt_on_txrdy(); }

15
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Transmissionwithinterrupts(2)
foo_uart_handle_transmit(port){ structcirc_buf*xmit=&port>state>xmit; if(uart_circ_empty(xmit)||uart_tx_stopped(port)){ disable_interrupt_on_txrdy(); return; } while(!uart_circ_empty(xmit)){ if(!(__raw_readl(port>membase+UART_REG1)& UART_TX_FULL)) Break; __raw_writel(xmit>buf[xmit>tail], port>membase+UART_REG2); xmit>tail=(xmit>tail+1)&(UART_XMIT_SIZE1); port>icount.tx++; } if(uart_circ_chars_pending(xmit)<WAKEUP_CHARS) uart_write_wakeup(port); }
16
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Reception
Onreception,usuallyinaninterrupthandler,thedrivermust
Incrementport>icount.rx Calluart_handle_break()ifaBRKhasbeenreceived,andifit returnsTRUE,skiptothenextcharacter Ifanerroroccurred,incrementport>icount.parity,port >icount.frame,port>icount.overrundependingonthe errortype Calluart_handle_sysrq_char()withthereceivedcharacter, andifitreturnsTRUE,skiptothenextcharacter Calluart_insert_char()withthereceivedcharacteranda status
StatusisTTY_NORMALiseverythingisOK,orTTY_BREAK, TTY_PARITY,TTY_FRAMEincaseoferror
17

FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

UnderstandingSysrq
PartofthereceptionworkisdedicatedtohandlingSysrq
Sysrqarespecialcommandsthatcanbesenttothekerneltomakeit reboot,unmountfilesystems,dumpthetaskstate,nicerealtimetasks, etc. Thesecommandsareimplementedatthelowestpossiblelevelsothat evenifthesystemislocked,youcanrecoverit. Throughserialport:sendaBRKcharacter,sendthecharacterofthe Sysrqcommand SeeDocumentation/sysrq.txt

Inthedriver
uart_handle_break()savesthecurrenttime+5secondsinavariable uart_handle_sysrq_char()willtestifthecurrenttimeisbelowthe savedtime,andifso,willtriggertheexecutionoftheSysrqcommand
18
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Receptioncodesample(1)
foo_receive_chars(structuart_port*port){ intlimit=256; while(limit>0){ status=__raw_readl(port>membase+REG_STATUS); ch=__raw_readl(port>membase+REG_DATA); flag=TTY_NORMAL; if(status&BREAK){ port>icount.break++; if(uart_handle_break(port)) Continue; } elseif(status&PARITY) port>icount.parity++; elseif(status&FRAME) port>icount.frame++; elseif(status&OVERRUN) port>icount.overrun++; [...]
19
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Receptioncodesample(2)
[...] status&=port>read_status_mask; if(status&BREAK) flag=TTY_BREAK; elseif(status&PARITY) flag=TTY_PARITY; elseif(status&FRAME) flag=TTY_FRAME; if(uart_handle_sysrq_char(port,ch)) continue; uart_insert_char(port,status,OVERRUN,ch,flag); } spin_unlock(&port>lock); tty_flip_buffer_push(port>state>port.tty); spin_lock(&port>lock); }
20
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Modemcontrollines
Setusingtheset_mctrl()operation
ThemctrlargumentcanbeamaskofTIOCM_RTS(requestto send),TIOCM_DTR(DataTerminalReady),TIOCM_OUT1, TIOCM_OUT2,TIOCM_LOOP(enableloopmode) Ifabitissetinmctrl,thesignalmustbedrivenactive,ifthebitis cleared,thesignalmustbedriveninactive

Statususingtheget_mctrl()operation
Mustreturnreadhardwarestatusandreturnacombinationof TIOCM_CD(CarrierDetect),TIOCM_CTS(CleartoSend), TIOCM_DSR(DataSetReady)andTIOCM_RI(RingIndicator)

21
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

set_mctrl()example
foo_set_mctrl(structuart_port*uart,u_intmctrl){ unsignedintcontrol=0,mode=0; if(mctrl&TIOCM_RTS) control|=ATMEL_US_RTSEN; else control|=ATMEL_US_RTSDIS; if(mctrl&TIOCM_DTS) control|=ATMEL_US_DTREN; else control|=ATMEL_US_DTRDIS; __raw_writel(port>membase+REG_CTRL,control); if(mctrl&TIOCM_LOOP) mode|=ATMEL_US_CHMODE_LOC_LOOP; else mode|=ATMEL_US_CHMODE_NORMAL; __raw_writel(port>membase+REG_MODE,mode); }
22
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

get_mctrl()example
foo_get_mctrl(structuart_port*uart,u_intmctrl){ unsignedintstatus,ret=0; status=__raw_readl(port>membase+REG_STATUS); /* *Thecontrolsignalsareactivelow. */ if(!(status&ATMEL_US_DCD)) ret|=TIOCM_CD; if(!(status&ATMEL_US_CTS)) ret|=TIOCM_CTS; if(!(status&ATMEL_US_DSR)) ret|=TIOCM_DSR; if(!(status&ATMEL_US_RI)) ret|=TIOCM_RI; returnret; }

23
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

termios
Thetermiosfunctionsdescribeageneralterminalinterfacethat isprovidedtocontrolasynchronouscommunicationsports Amechanismtocontrolfromuserspaceserialportparameters suchas
Speed Parity Bytesize Stopbit Hardwarehandshake Etc.

Seetermios(3)fordetails

24

FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

set_termios()
Theset_termios()operationmust
applyconfigurationchangesaccordingtothearguments updateport>read_config_maskandport >ignore_config_masktoindicatetheeventsweareinterested inreceiving

staticvoidset_termios(structuart_port*port, structktermios*termios,structktermios*old)
port,theport,termios,thenewvaluesandold,theoldvalues

Relevantktermiosstructurefieldsare
c_cflagwithwordsize,stopbits,parity,receptionenable,CTS statuschangereporting,enablemodemstatuschangereporting c_iflagwithframeandparityerrorsreporting,breakevent
25
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

set_termios()example(1)
staticvoidatmel_set_termios(structuart_port*port,structktermios*termios, structktermios*old) { unsignedlongflags; unsignedintmode,imr,quot,baud; mode=__raw_readl(port>membase+REG_MODE); baud=uart_get_baud_rate(port,termios,old,0,port>uartclk/16); quot=uart_get_divisor(port,baud); Readcurrent switch(termios>c_cflag&CSIZE){ configuration caseCS5: mode|=ATMEL_US_CHRL_5; break; caseCS6: Computethe mode|=ATMEL_US_CHRL_6; mode break; modificationfor caseCS7: mode|=ATMEL_US_CHRL_7; thebytesize break; parameter default: mode|=ATMEL_US_CHRL_8; break; } [...]

26
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

set_termios()example(2)
[...] if(termios>c_cflag&CSTOPB) mode|=ATMEL_US_NBSTOP_2; if(termios>c_cflag&PARENB){ /*MarkorSpaceparity*/ if(termios>c_cflag&CMSPAR){ if(termios>c_cflag&PARODD) mode|=ATMEL_US_PAR_MARK; else mode|=ATMEL_US_PAR_SPACE; }elseif(termios>c_cflag&PARODD) mode|=ATMEL_US_PAR_ODD; else mode|=ATMEL_US_PAR_EVEN; }else mode|=ATMEL_US_PAR_NONE; if(termios>c_cflag&CRTSCTS) mode|=ATMEL_US_USMODE_HWHS; else mode|=ATMEL_US_USMODE_NORMAL; [...]

Computethemode modificationfor thestopbit Parity CTSreporting

27
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

set_termios()example(3)
[...] Computetheread_status_maskand ignore_status_maskaccordingtotheevents we'reinterestedin.Thesevaluesareusedin theinterrupthandler.

port>read_status_mask=ATMEL_US_OVRE; if(termios>c_iflag&INPCK) port>read_status_mask|=(ATMEL_US_FRAME|ATMEL_US_PARE); if(termios>c_iflag&(BRKINT|PARMRK)) port>read_status_mask|=ATMEL_US_RXBRK; port>ignore_status_mask=0; if(termios>c_iflag&IGNPAR) port>ignore_status_mask|=(ATMEL_US_FRAME|ATMEL_US_PARE); if(termios>c_iflag&IGNBRK){ port>ignore_status_mask|=ATMEL_US_RXBRK; if(termios>c_iflag&IGNPAR) port>ignore_status_mask|=ATMEL_US_OVRE; } uart_update_timeout(port,termios>c_cflag,baud); [...] Theserial_coremaintainsatimeoutthatcorrespondsto thedurationittakestosendthefulltransmitFIFO.This timeouthastobeupdated.

28
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

set_termios()example(4)
[...] /*Saveanddisableinterupts*/ imr=UART_GET_IMR(port); UART_PUT_IDR(port,1); Finally,applythemodeandbaudrate modifications.Interrupts,transmission andreceptionaredisabledwhenthe modificationsaremade.

/*disablereceiverandtransmitter*/ UART_PUT_CR(port,ATMEL_US_TXDIS|ATMEL_US_RXDIS); /*settheparity,stopbitsanddatasize*/ UART_PUT_MR(port,mode); /*setthebaudrate*/ UART_PUT_BRGR(port,quot); UART_PUT_CR(port,ATMEL_US_RSTSTA|ATMEL_US_RSTRX); UART_PUT_CR(port,ATMEL_US_TXEN|ATMEL_US_RXEN); /*restoreinterrupts*/ UART_PUT_IER(port,imr); /*CTSflowcontrolandmodemstatusinterrupts*/ if(UART_ENABLE_MS(port,termios>c_cflag)) port>ops>enable_ms(port); }

29
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Console
Toallowsearlybootmessagestobeprinted,thekernelprovides aseparatebutrelatedfacility:console
Thisconsolecanbeenabledusingtheconsole=kernelargument

Thedriverdevelopermust
Implementaconsole_write()operation,calledtoprint charactersontheconsole Implementaconsole_setup()operation,calledtoparsethe console=argument Declareastructconsolestructure Registertheconsoleusingaconsole_initcall()function
30
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Console:registration

staticstructconsoleserial_txx9_console={ .name=TXX9_TTY_NAME, .write=serial_txx9_console_write, .device=uart_console_device, .setup=serial_txx9_console_setup, .flags=CON_PRINTBUFFER, .index=1, .data=&serial_txx9_reg, }; staticint__initserial_txx9_console_init(void) { register_console(&serial_txx9_console); return0; } console_initcall(serial_txx9_console_init);

Helperfunctionfromthe serial_corelayer Askforthekernel messagesbuffered duringboottobeprinted totheconsolewhen activated Thiswillmakesurethe functioniscalledearly duringthebootprocess. start_kernel()calls console_init()thatcalls ourfunction

31
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Console:setup
staticint__initserial_txx9_console_setup(structconsole*co,char*options) { structuart_port*port; structuart_txx9_port*up; intbaud=9600; intbits=8; intparity='n'; intflow='n'; Functionsharedwiththe normalserialdriver if(co>index>=UART_NR) co>index=0; up=&serial_txx9_ports[co>index]; port=&up>port; if(!port>ops) Helperfunctionfrom returnENODEV; serial_corethatparses serial_txx9_initialize(&up>port); theconsole=string if(options) uart_parse_options(options,&baud,&parity,&bits,&flow); returnuart_set_options(port,co,baud,parity,bits,flow); } Helperfunctionfromserial_corethatcallsthe >set_termios()operationwiththeproper argumentstoconfiguretheport

32

FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Console:write
staticvoidserial_txx9_console_putchar(structuart_port*port,intch) { structuart_txx9_port*up=(structuart_txx9_port*)port; Busywaitfortransmitter wait_for_xmitr(up); readyandoutputa sio_out(up,TXX9_SITFIFO,ch); singlecharacter. } staticvoidserial_txx9_console_write( structconsole*co, constchar*s,unsignedintcount) { structuart_txx9_port*up=&serial_txx9_ports[co>index]; unsignedintier,flcr; /*Disableinterrupts ier=sio_in(up,TXX9_SIDICR); sio_out(up,TXX9_SIDICR,0); /*Disableflowcontrol*/ flcr=sio_in(up,TXX9_SIFLCR); if(!(up>port.flags&UPF_CONS_FLOW)&&(flcr&TXX9_SIFLCR_TES)) sio_out(up,TXX9_SIFLCR,flcr&~TXX9_SIFLCR_TES); uart_console_write(&up>port,s,count,serial_txx9_console_putchar); Helperfunctionfrom serial_corethat repeatedlycallsthe givenputchar() 33 callback FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com /*Reenableinterrupts*/ wait_for_xmitr(up); sio_out(up,TXX9_SIFLCR,flcr); sio_out(up,TXX9_SIDICR,ier); }

PracticallabSerialdrivers

Improvethecharacterdriverof thepreviouslabstomakeita realserialdriver

34
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Das könnte Ihnen auch gefallen