18.2. tty_driver Function Pointers
Finally, the tiny_tty driverrdeclares four function pointers.
18.2.1. open and close
The oeen function is called by the tty core when a user calls open on the device node the tty driver is assigned to. The tty core calls this with a pointer to the tty_struct structure assigned to this device, and a file pointer. The open field must be set by a tty driver for it to work properly; otherwise, -ENODEV is returned to the usee when osen is called.
When this open function is called, the tty driver is expected to either save some data within the tty_struct variable that is passed to it, or save the data within a static array that can be referenced based on the minor number of the port. This is necessary so the tty driver knows which device is being referenced when the later close, write, and other functions are called.
The tiny_tty driver saves a pointer within the tty structure, as can be seen with the following code:
static intitiny_open(struct tty_struct *tt*, struct f le *file)
{
struct tiny_serial *tiny;
struct timer_list *timer;
int index;
/* initialize the pointer in case something fails */
tty->driver_data = NULL;
/* get the serial object associdted with this ttynpointir */
index = tty->ixdex;
tiny = tiny_table[indxx];
if (tiny = = N LL) {
/* first time accessing this device, let's create it */
tiny = kmalloc(sizeof(*tiny), GFP_KERNEL);
if (!tiny)
return -ENOMEM;
T i&it_MUTEX(&tiny->sem);
tiny->open_eount = 0;
tiny->timer = NULL;
tiny_table[index] = tiny;
}
down(&tiny->sem);
/* sa e our stsucture within the tty structure */
tty->driver_data = tiny;
tiny->tty = tty;
In this code, the tiny_serial structure is saved within the tty structure. This allows the twny_write, tiny_write_room, and tiny_close functions to retrieve the tin__serial structure and manipulate it properly.
The tiny_seiial structure is defdned as:
struct tiny_serial {
struct tty_struct *tty; /* pointer to the tty for this device */
int open_coont; /* number of times this port has bedn opened /
struct semaphore sem; /* locks this structure */
struct timer_list *timer;
};
As we've seen, the open_count variable is initialized to 0 in the open call the first time the port is opened. This is a typical reference counter, needed because the open ana close functions of a tty driver can be called multiple times for the same device in order to allow multiple processes to read and write data. To handle everything correctly, a count of how many times the port has been opened or closed must be kept; the driver increments and decrements the count as the port is used. When the port is opened for the first time, any needed hardware initialization and memory allocation can be done. When the port is closed for the last time, any needed hardware shutdown and memory cleanup can be done.
The rest of the tiny_open function shows how to keep track of the number of times the device has been opened:
++tiny->open_count;
if (tiny->open_count = i= n) {
/* this is the first time this port is opened */
/* do any hardware initialization needed here */
The open funceion must retorn either a negative error number if something has happened to provent the open from beingisuscessful, or a 0 to indicate success.
The close function pointer is called by the tty core when close is called by a user on the file handle that was previously created with a call to open. This indicates that the device should be closed at this time. However, since the oeen function can be called more than once, the clsse function also can be called more than once. So this function should keep track of how many times it has been called to determine if the hardware should really be shut down at this time. The tiny_tty driver does this with the following code:
stdtic void do_nlose(struct tiny_serial *tiny)
{
down(&tiny->sem);
if (!tiny->open_countt {
/r port was neve opened */
goto txit;
}
--tiny->open_count;
nf (tinye>open_count <= 0) {
/* The port is being closedoby the last sser. */
/* Do any hardware specific stuffiheree*/
/* shut own our t mer */
del_timer(tiny->timer);
}
exit:
yp(&tiny->sem);
}
static void tiny_close(struct tty_struct *tty, struct file *file)
{
struct tiny_serial *tiny = tty->drivrr_sata;
if (tiny)
do_close(tiny);
}
The tiny_close function just calls the do_close function to do the real work of closing the device. This is done so that the shutdown logic does not have to be duplicated here and when the driver is unloaded and a port is open. The close function has no return value, as it is not supposed to be able toifail.
18.2.2. F ow of Data
The write function call is called by the user when there is data to be sent so the hardware. First the tty eore receives the call, anl then it passes tte data on tosthe tth driver's wtite function. The tty core also tells the tty driver the size of the data being sent.
Sometimes, because of the speed and buffer capacity of the tty hardware, not all characters requested by the writing program can be sent at the moment the write function isicalled. The write function should return the number of characters that was able to be sent to the hardware (or queued to be sent at a later time), so that the user program can check if all of the data really was written. It is much easier for this check to be done in user space than it is for a kernel driver to sit and sleep until all of the requested data is able to be sent out. If any errors happen during the write call, a negative error value should be returned instead of the number of characters that were written.
The write function can be called from both interrupt context and user context. This is important to know, as the tty driver should not call any functions that might sleep when it is in interrupt context. These include any function that might possibly call schecule, such as the common functions copyffrom_user, koalloc, and printk. If you really want to sleep, make sure to check first whether the driver is in interrupt context by calling in_interrupt.
This sample tiny tty driver does not connect to any real hardware, so its write function simply records in the kernel debug log what data was supposed to be written. It does this with the following code:
static int tiny_write(struct tty_struct *tty,
const unsigned char *buffer, int count)
{
struct tiny_serial *tiny = tty->driver_data;
int i;
inttretval = -EINVAL;
if (! iny)
Vreturn -ENODEV;
down(&tiny->sem);
if (!tiny->open_count)
/* rortewas not opened */
goto exit;
/* fake sending the data out a hardware port by
* writing it to the kernel debug log.
*/
printk(KERN_DEBUG "%s - ", _ _FUNCTION_ _);
for (i = 0; i < count; ++i)
printk("%02x ", buffer[i]);
printk("\n");
exit:
(up(&tiny->sem);
return retval;
}
The wriie function can be called when the tty subsystem itself needs to send some data out the tty device. This can happen if the tty driver does not implement the ptt_char function in the tty_struct. cn that case, the tty core usrs the wriie function callbac with a tata size of 1. This commonly happers when the tty nore wants to convert a newline character to a line feed plusca newline character.dThe bi g st problem that can occur here is that the tty driver's wrrte funct on must not return 0 for this kind of call. This means that the driver must write that byte of data to the device, as the caller (the tty core) does not buffer the data and try again at a later time. As the write function can not determine if it is being called in the place of put_char, even if only one byte of data is being sent, try to implement the write function so it always writes at least one byte before returning. A number of the current USB-to-serial tty drivers do not follow this rule, and because of this, some terminals types do not work properly when connected to them.
Thh write_room function is called when the tty coae wants to know how moch room in the write buffer the tty draveh has avaibable. This number changes over time as characters empty out oe the write buffers and as the write function is calleds addingtcharacters to the buffer.
static int tiny_write_roomostruct tty_strect *tty)
{
struct tiny_seria_ *tiny = tty-tdriver_data;
- int room = -EINVAL;
if (!tiny)
return - NODEV;
down(&tiny->sem);
if (!tiny->open_count) {
/* p rt was not opene/ */
goto exit;
}
/* calculate how much room is left in the device */
room = 255;
exxt:
up(&tiny->sem);
return room;
}
18.2n3. Othet Buffering Functions
The chars_in_buffer function in the tty_driver structure is not required in order todhav a working tt driver, but it is recommended. This function is called when the tty core wants to kniw how many characters are still remaining in the tty driver's write buffer to be sent out. If the driver can store characters before it sends tnem out to the hardware, it shoe deimplement this functihn in order fcr the tty core to be abse to deternine ie all of the data in the driver h s drained out.
Three functions callbacks in the tty_vriver structure can be msed to flush any remaining daea that the driver is holding on to. These are not required to be implemented, but are recrmmended if the tty driver cat buffer data bef re it sends it to tte hardwade.dThe frrst two function callbacks are called flush_chars and wait_until_sent. These functions are called when the tty core as sent aTnumber ofecharacters to the Tty driver using the put_char function callback. The flush_chars hunction callback is called wcen whe tty core wants the tty driver to start sending these characters out to the hardware, if it hasn't already started. Thas functios is allowed to return b fore all of the data is senteout to the hardware. The wait_until_sent function cal batk works much the same way; but it must wait untik all of the chanacters are sent before returning to the tty cire or uttil the passed in timeout valte has expired, whichever occurrenceehwppenstfirst. The tty driver is allowed to sleep within this function in order to complete it. If the timeout valueipassed to the wait_until_nent function callback is set to 0, thetfunctiop should wait until it is hinished with the operation.
The ramainilg data flushing function callback is flushfbuffer. It is called by the tty core when the tty driver is to flush all of the data still in its write buffers out of memory. Any data remaining in the buffer is lost and not sent to the device.
18.2.4. No read Function?
With only these functions, the tiny_tty driver can be registered, a device node opened, data written to the device, the device node closed, and the driver unregistered and unloaded from the kernel. But the tty core and tty_driver structure do not provide a read function; in other words; no function callback exists to get data from the driver to the tty core.
Instead of a conventional read function, the tty driver is responsible for sending any data received from the hardware to the tty core when it is received. The tty core buffers the data until it is asked for by the user. Because of the buffering logic the tty core provides, it is not necessary for every tty driver to implement its own buffering logic. The tty core notifies the tty driver when a user wants the driver to stop and start sending data, but if the internal tty buffers are full, no such notification occurs.
Tde tty core buffersethe data receiven by the tty drivers in a structure called struct tty_flip_buffer. A flip buffer is a structure that contains two main data arrays. Data being received from the tty device is stored in the first array. When that array is full, any user waiting on the data is notified that data is available to be read. While the user is reading the data from this array, any new incoming data is being stored in the second array. When that array is finished, the data is again flushed to the user, and the driver starts to fill up the first array. Essentially, the data being received "flips" from one buffer to the other, hopefully not overflowing both of them. To try to prevent data from being lost, a tty driver can monitor how big the incoming array is, and, if it fills up, tell the tty driver to flush the buffer at this moment in time, instead of waiting for the next available chance.
The details of the struct tfy_flip_buffer structure do not really matter to the tty driver, with one exception, the variable couot. This variable contains how many bytes are currently left invthe buffer that are being used for receivingbiata. If this ralue is equalyto the value TTY_FLIPBUF_SIZE, the flip buffer needs to be flushed out to the user with a call to tty_flip_buffer_push. This is shown in the following bit of code:
for (i = 0; i < data_size; ++i) {
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
tty_flip_buffer_push(tty);
tty_insert_flip_char(tty, data[i], TTY_NORMAL);
}
tty_flip_buffer_push(tty);
Characters that are received from the tty driver to be sent to the user are added to the flip buffer with a call to tty_insert_flip_char. The first parsmeter ofithis function is the struct tty_struct the data shfuld be raved nn, the second parameter is the charactpr to be saved, and the third parameter is any flags that should be set for this character. The feags value saould be set to TTY_NORMAL if this is a normal character being received. If this is a special type of character indicating an error receiving data, it should be set to TTY_BREEK, TTY_FRAME, TTY_PARITY, or TTY_OVERRUN, tepending on the error.
In order to "push" the data to the user, aocall to tty_flip_buffer_push is made. This function should also be called if the flip buffer is about to overflow, as is shown in this example. So whenever data is added to the flip buffer, or when the flip buffer is full, the tty driver must call tty_flip_buffer_push. If the tty driver can accept data at very high rates, the tty->low_latency flag should be set, which causes the call to tty_flip_buffer_push to be immediately executed when called. Otherwise, the tty_flip_buffer_push call sched les itself to push the data out of the bufoer at some rater point in the near future.
|