现在的位置: 首页 > 综合 > 正文

a tty driver

2013年01月07日 ⁄ 综合 ⁄ 共 4789字 ⁄ 字号 评论关闭

1. my_tty_driver.c

#include <linux/module.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include "echodev.h"

#define MYDEV_MAX_NDEV 16		/* maximum number of devices supported */

static unsigned ndev = 1;		/* number of devices to create */
static unsigned major = 0;		/* save major number assigned to driver */

module_param(ndev, uint, 0);		/* allow configuration of # devices */
module_param(major, uint, 0);		/* allow manual specification of major # */

struct mydev_struct {
	struct echodev *e;
	int open_count;
};

struct mydev_struct *mydev_table[MYDEV_MAX_NDEV];

static void
mydev_rx(void *priv, const unsigned char *data, int len)
{
	struct tty_struct *tty = priv;
	int rc;

	printk("received %d bytes from device\n", len);
}

static int
mydev_open(struct tty_struct * tty, struct file * filp)
{
	struct mydev_struct *dev = mydev_table[tty->index];

	if (dev == NULL) {
		if (!(dev = kmalloc(sizeof *dev, GFP_KERNEL)))
			return -ENOMEM;

		if (!(dev->e = echodev_init(tty, mydev_rx))) {
			kfree(dev);
			return -ENOMEM;
		}

		mydev_table[tty->index] = dev;
	}

	++dev->open_count;
	tty->driver_data = dev;

	return 0;
}

static void
mydev_close(struct tty_struct *tty, struct file * filp)
{
	struct mydev_struct *dev = tty->driver_data;

	if (dev) {
		--dev->open_count;
		if (dev->open_count <= 0) {
			echodev_destroy(dev->e);
			kfree(dev);
			mydev_table[tty->index] = NULL;
		}
	}
}

static int
mydev_write(struct tty_struct *tty, const unsigned char *buf, int count)
{
	struct mydev_struct *dev = tty->driver_data;
	printk("attempting to send %d bytes to device\n", count);
	return echodev_write(dev->e, buf, count);
}

static int
mydev_writeroom(struct tty_struct *tty)
{
	struct mydev_struct *dev = tty->driver_data;
	int room = echodev_free(dev->e);
	printk("mydev_writeroom returns %d\n", room);
	return room;
}

static struct tty_operations mydev_ops = {
	.open = mydev_open,
	.close = mydev_close,
	.write = mydev_write,
	.write_room = mydev_writeroom,
};

static struct tty_driver *mydev_driver;

static int __init
mymod_init (void)
{
	int rc;

	/* Ensure ndev isn't too big */

	if (ndev > MYDEV_MAX_NDEV) {
		printk("Capping ndev to %d\n", MYDEV_MAX_NDEV);
		ndev = MYDEV_MAX_NDEV;
	}

	if (!(mydev_driver = alloc_tty_driver(ndev)))
		return -ENOMEM;

	mydev_driver->owner = THIS_MODULE;
	mydev_driver->driver_name = "mydev_tty";
	mydev_driver->name = "mydevtty";
	mydev_driver->major = major;
	mydev_driver->type = TTY_DRIVER_TYPE_SERIAL;
	mydev_driver->subtype = SERIAL_TYPE_NORMAL;
	mydev_driver->flags = TTY_DRIVER_REAL_RAW;
	mydev_driver->init_termios = tty_std_termios;
	mydev_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
	tty_set_operations(mydev_driver, &mydev_ops);

	if ((rc = tty_register_driver(mydev_driver))) {
		printk("tty_register_driver() returned %d\n", rc);
		put_tty_driver(mydev_driver);
		return rc;
	}

	/* Initialize hardware */

	
	printk("Hello from mydev!\n");
	return 0;
}  

static void __exit
mymod_exit(void) 
{
	tty_unregister_driver(mydev_driver);
	put_tty_driver(mydev_driver);

	printk("Goodbye from mydev.\n"); 
}  

module_init(mymod_init);
module_exit(mymod_exit);

MODULE_AUTHOR("Me"); 
MODULE_LICENSE("GPL"); 
MODULE_DESCRIPTION("Character driver skeleton.");

2. Makefile

obj-m := mydev.o
mydev-objs := mymod.o echodev.o

# With this simplified Makefile, type 'make' to build your module
# Type 'make install' to build and install the module to the target file system

KERNELDIR=$(shell ls -d ~/linux-2.6.[0-9]*)
TARGETDIR=/targetfs

PWD := $(shell pwd)

default:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules modules_install INSTALL_MOD_PATH=$(TARGETDIR)

3. echodev.c

#include <linux/slab.h>
#include "echodev.h"

static void
__echodev_work_fn(struct work_struct *work)
{
	struct echodev *e = container_of(work, struct echodev, work);
	int nbytes;

	if ((nbytes = atomic_read(&e->nbuf))) {
		if (e->r + nbytes > ECHODEV_BUFSIZE) {
			int i = ECHODEV_BUFSIZE - e->r;
			e->rx_fn(e->priv, e->buffer + e->r, i);
			e->r = nbytes - i;
			e->rx_fn(e->priv, e->buffer, e->r);
		} else {
			e->rx_fn(e->priv, e->buffer + e->r, nbytes);
			e->r += nbytes;
		}

		atomic_sub(nbytes, &e->nbuf);
	}
}

struct echodev*
echodev_init(void *priv, void (*rx_fn)(void*, const unsigned char*, int))
{
	struct echodev *e = kzalloc(sizeof *e, GFP_KERNEL);

	if(!e)
		return NULL;

	e->priv = priv;
	e->rx_fn = rx_fn;
	INIT_WORK(&e->work, __echodev_work_fn);

	return e;
}

void
echodev_destroy(struct echodev *e)
{
	cancel_work_sync(&e->work);
	kfree(e);
}

int
echodev_write (struct echodev *e, const unsigned char * buf, int nbytes)
{
	int nfree = ECHODEV_BUFSIZE - atomic_read(&e->nbuf);

	if (nbytes > nfree)
		nbytes = nfree;

	if (e->w + nbytes > ECHODEV_BUFSIZE) {
		int i = ECHODEV_BUFSIZE - e->w;
		memcpy(e->buffer + e->w, buf, i);
		e->w = nbytes - i;
		memcpy(e->buffer, buf + i, e->w);
	} else {
		memcpy(e->buffer + e->w, buf, nbytes);
		e->w += nbytes;
	}

	atomic_add(nbytes, &e->nbuf);
	schedule_work(&e->work);

	return nbytes;
}

int
echodev_free(struct echodev *e)
{
	return ECHODEV_BUFSIZE - atomic_read(&e->nbuf);
}

4. echodev.h

#ifndef __ECHODEV_H__
#define __ECHODEV_H__

#include <linux/workqueue.h>
#include <asm/atomic.h>

#define ECHODEV_BUFSIZE 32

struct echodev {
	char buffer[ECHODEV_BUFSIZE];
	atomic_t nbuf;
	void (*rx_fn)(void *, const unsigned char *, int);
	int r, w;
	struct work_struct work;
	void *priv;
};

struct echodev *echodev_init (void *, void (*)(void*, const unsigned char*, int));
void echodev_destroy (struct echodev *);
int echodev_write(struct echodev *, const unsigned char *, int);
int echodev_free(struct echodev *);

#endif

抱歉!评论已关闭.