Home
Reading
Searching
Subscribe
Sponsors
Statistics
Posting
Contact
Spam
Lists
Links
About
Hosting
Filtering
Features Download
Marketing
Archives
FAQ
Blog
 
Gmane
From: Naranjo Manuel Francisco <manuel <at> mendietabar.com.ar>
Subject: [RFC] Aircable USB Driver Revision 4
Newsgroups: gmane.linux.usb.devel
Date: Tuesday 18th July 2006 18:29:01 UTC (over 11 years ago)
Hi everybody:
I'm still working on AIRcable USB driver, and still having a little isues
to
correct. I have made some deep changes to my source, it now has a Circular 
Buffer for buffering the output bytes. With this I have been able to send
almost
1 kB with out problems.
My problems start when I recieve more than 300 bytes. The system crashes. I
have
tried to delay the output of data in order to give the hardware time to
push
the recived data out, but could get to anything.
I'm not sending this as a patch because it is not the final release.
If anyone can help me a bit, It will be great.

Regards,
Manuel Naranjo

/*
 * AIRcable USB Bluetooth dondgle Driver.
 *
 * Copyright (C) 2006 Manuel Francisco Naranjo ([email protected])
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 as published by the Free Software Foundation.
 *
 * The device works as an standard CDC device, it has 2 interfaces, the
first 
 * one is for firmware access and the second is the serial one.
 * The device simply need some stuff to understand data comming from the
usb 
 * buffer: The First and Second byte is used for a Header, the Third and
Fourth
 * tells the  device the amount of information the package holds.
 * Packages are 60 bytes long Header Stuff.
 *
 * The driver registers himself with the USB-serial core and the USB Core.
 * I had to implement a probe function agains USB-serial, because other
way, the
 * driver was attaching himself to both interfaces. I have tryed with
different
 * configurations of usb_serial_driver with out exit, only the probe
function
 * could handle this correctly.
 *
 * I have also taken some info from a Greg Kroah-Hartman article
 * url: http://www.linuxjournal.com/article/6573
 * And from Linux Device Driver Kit CD, which is a great work, the authors
 * taken the work to recopile lots of information an knowladge in drivers
 * development and made it all avaible inside a cd.
 * URL: http://kernel.org/pub/linux/kernel/people/gregkh/ddk/
 *
 */

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "usb-serial.h"


static int debug;

#define AIRCABLE_VID		0x16CA	/* Vendor Id */
#define AIRCABLE_USB_PID	0x1502	/* Product Id */

/* write buffer size defines */
#define AIRCABLE_BUF_SIZE	1024
#define AIRCABLE_FRAME_RATE	100 * HZ

/* The following defintions are required to Add/Strip HCI headers from URB
sent
   over bulk endpoints */
#define HCI_HEADER_LENGTH	0x4
#define HCI_HEADER_0		0x20
#define HCI_HEADER_1		0x29
#define MAX_HCI_FRAMESIZE	60
#define HCI_COMPLETE_FRAME	64

/*
 * Version Information
 */
#define DRIVER_VERSION "v1.0b2"
#define DRIVER_AUTHOR "Naranjo, Manuel Francisco
"
#define DRIVER_DESC "AIRcable USB Driver"

/* ID table that will be registered with USB core */
static struct usb_device_id id_table [] = {
	{USB_DEVICE(AIRCABLE_VID, AIRCABLE_USB_PID)},
	{},
};
MODULE_DEVICE_TABLE(usb, id_table);

static int aircable_probe(struct usb_serial*, const struct usb_device_id*);
static int aircable_write(struct usb_serial_port*, const unsigned char *,
int);
static void aircable_send(struct usb_serial_port*);
static void aircable_read_bulk_callback(struct urb*, struct pt_regs *);
static void aircable_write_bulk_callback(struct urb*, struct pt_regs *);
static void aircable_shutdown (struct usb_serial*);
static int aircable_attach (struct usb_serial*);

/* circular buffer */
static struct circ_buf *serial_buf_alloc(void);
static void serial_buf_free(struct circ_buf*);
static void serial_buf_clear(struct circ_buf*);
static int serial_buf_data_avail(struct circ_buf*);
static int serial_buf_put(struct circ_buf*, const char*, int);
static int serial_buf_get(struct circ_buf*, char*, int);

/* Internal Structure */
struct aircable_private {
	spinlock_t lock;	/* private lock */
	struct circ_buf *buf;	/* write buffer */
	int write_urb_in_use;	/* write urb in use indicator */
};

static struct usb_serial_driver aircable_device = {
	.description =		"AIRcableUSB",
	.id_table = 		id_table,
	.num_ports =		1,
	.attach=		aircable_attach,
	.probe=			aircable_probe,
	.shutdown=		aircable_shutdown,
	.write=			aircable_write,
	.write_bulk_callback=	aircable_write_bulk_callback,
	.read_bulk_callback=	aircable_read_bulk_callback,
};

static struct usb_driver aircable_driver = {
	.name =		"AIRcableUSB",
	.probe =	usb_serial_probe,
	.disconnect =	usb_serial_disconnect,
	.id_table =	id_table,
};


static int aircable_probe(struct usb_serial *serial, const 
						struct usb_device_id *id)
{
	struct usb_host_interface *iface_desc = serial->interface->cur_altsetting;
	struct usb_endpoint_descriptor *endpoint;
	
	int num_bulk_out=0;
	int i;
	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
		endpoint = &iface_desc->endpoint[i].desc;	
		if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
			((endpoint->bmAttributes & 3) == 0x02)) {
			/* we found a bulk out endpoint */
			dbg("found bulk out on endpoint %d", i);	
	
			++num_bulk_out;
		}
	}

	if (num_bulk_out == 0) {
		dbg("Invalid interface, discarding");
		return -ENODEV;
	}

	

	return 0;
}

static int aircable_attach (struct usb_serial *serial){
	struct aircable_private *priv;

	priv = kzalloc(sizeof(struct aircable_private), GFP_KERNEL);
	if (!priv){
		err("%s- kmalloc(%Zd) failed.", __FUNCTION__,
			sizeof(struct aircable_private));
		return -ENOMEM;
	}

	spin_lock_init(&priv->lock);

	/* Allocation of Circular Buffer */
	priv->buf = serial_buf_alloc();
	if (priv->buf == NULL) {
		kfree(priv);
		return -ENOMEM;
	}

	usb_set_serial_port_data(serial->port[0], priv);

	return 0;
}

static void aircable_shutdown (struct usb_serial *serial)
{
	
	struct usb_serial_port *port = serial->port[0];
	struct aircable_private *priv = usb_get_serial_port_data(port);

	dbg("%s", __FUNCTION__);

	if (priv) {
		serial_buf_free(priv->buf);
		usb_set_serial_port_data(port, NULL);
		kfree(priv);
	}
}



static int aircable_write(struct usb_serial_port *port,
					const unsigned char *source, int count)
{
	struct aircable_private *priv = usb_get_serial_port_data(port);
	unsigned long flags;
	
	dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count);

	if (!count){
		dbg("%s - write request of 0 bytes", __FUNCTION__);
		return count;
	}

	spin_lock_irqsave(&priv->lock, flags);
	count = serial_buf_put(priv->buf, source, count);
	spin_unlock_irqrestore(&priv->lock, flags);

	aircable_send(port);

	return count;

}

static void aircable_send(struct usb_serial_port *port)
{
	int count, result;
	struct aircable_private *priv = usb_get_serial_port_data(port);
	unsigned long flags;
	unsigned char* buf;

	dbg("%s - port %d", __FUNCTION__, port->number);

	spin_lock_irqsave(&priv->lock, flags);

	if (port->write_urb_busy) {
		spin_unlock_irqrestore(&priv->lock, flags);
		return;
	}

	count = min(serial_buf_data_avail(priv->buf),MAX_HCI_FRAMESIZE);

	if (count == 0) {
		spin_unlock_irqrestore(&priv->lock, flags);
		return;
	}

	buf = kzalloc(count + HCI_HEADER_LENGTH, GFP_KERNEL);
	if (!buf) {
		err("%s- kmalloc(%Zd) failed.", __FUNCTION__, count +
							HCI_HEADER_LENGTH);
		return;
	}

	buf[0] = HCI_HEADER_0;
	buf[1] = HCI_HEADER_1;
	buf[2] = (unsigned char) count;
	buf[3] = (unsigned char)(count >>8);

	serial_buf_get(priv->buf, buf + HCI_HEADER_LENGTH, MAX_HCI_FRAMESIZE);

	memcpy(port->write_urb->transfer_buffer, buf, count + HCI_HEADER_LENGTH);
	kfree(buf);
	port->write_urb_busy = 1;

	spin_unlock_irqrestore(&priv->lock, flags);

	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count +
					HCI_HEADER_LENGTH,
					port->write_urb->transfer_buffer);

	port->write_urb->transfer_buffer_length = count + HCI_HEADER_LENGTH;
	port->write_urb->dev = port->serial->dev;
	result = usb_submit_urb (port->write_urb, GFP_KERNEL);
	if (result) {
		dev_err(&port->dev,
				"%s - failed submitting write urb, error %d\n",
				__FUNCTION__, result);
		port->write_urb_busy = 0;
		//schedule_work(&port->work);
	}

	//Try to delay output.
	//msleep(50);

	schedule_work(&port->work);
}

static void aircable_write_bulk_callback (struct urb *urb, struct pt_regs
*regs)
{
	int result;
	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;

	switch (urb->status) {
		case 0:
			/* success */
			break;
		case -ECONNRESET:
		case -ENOENT:
		case -ESHUTDOWN:
			/* this urb is terminated, clean up */
			dbg("%s - urb shutting down with status: %d",
						__FUNCTION__, urb->status);
			port->write_urb_busy = 0;
			return;
		case -EPIPE: /* no break needed */
			usb_clear_halt(port->serial->dev, 0x02);
		default:
			/* error in the urb, so we have to resubmit it */
			dbg("%s - Overflow in write", __FUNCTION__);
			dbg("%s - nonzero write bulk status received: %d",
						__FUNCTION__, urb->status);
			port->write_urb->transfer_buffer_length = 1;
			port->write_urb->dev = port->serial->dev;
			result = usb_submit_urb(port->write_urb, GFP_KERNEL);
			if (result)
				dev_err(&urb->dev->dev,
				"%s - failed resubmitting write urb, error %d\n"
					,__FUNCTION__, result);
			else
				return;
	}
	
	port->write_urb_busy = 0;

	aircable_send(port);
}

static void aircable_read_bulk_callback(struct urb *urb, struct pt_regs
*regs)
{
	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
	struct tty_struct *tty;
	unsigned char *data = urb->transfer_buffer;
	unsigned long no_packages;
	unsigned long remaining, package_length;	
	unsigned long i;
	int result;

	dbg("%s - port %d", __FUNCTION__, port->number);

	if (urb->status) {
		dbg("%s - urb->status = %d", __FUNCTION__, urb->status);
		if (!port->open_count) {
			dbg("%s - port is closed, exiting.", __FUNCTION__);
			return;
		}
		if (urb->status == -EPROTO) {
			dbg("%s - caught -EPROTO, resubmitting the urb",
								__FUNCTION__);
			urb->status = 0;
			urb->dev = port->serial->dev;
			result = usb_submit_urb(urb, GFP_KERNEL);
			if (result)
				dev_err(&urb->dev->dev,
				"%s - failed resubmitting read urb, error %d\n",
				__FUNCTION__, result);
			return;
		}
		dbg("%s - unable to handle the error, exiting.", __FUNCTION__);
		return;
	}

	usb_serial_debug_data(debug, &port->dev, __FUNCTION__,
					urb->actual_length, data);
	
	tty = port->tty;
	if (tty && urb->actual_length) {
		remaining = urb->actual_length;
		no_packages = urb->actual_length / (HCI_COMPLETE_FRAME);

		if (urb->actual_length % HCI_COMPLETE_FRAME != 0)
			no_packages+=1;
		for (i = 0; i < no_packages ;i++) {
			if (remaining > (HCI_COMPLETE_FRAME))
				package_length = HCI_COMPLETE_FRAME;
			else
				package_length = remaining;
			remaining -= package_length;

			tty_buffer_request_room(tty,package_length - HCI_HEADER_LENGTH);
			tty_insert_flip_string(tty, urb->transfer_buffer +
				HCI_HEADER_LENGTH + (HCI_COMPLETE_FRAME) * (i),
				package_length - HCI_HEADER_LENGTH);
			tty_flip_buffer_push(tty);
		}

	}

	/* Schedule the next read _if_ we are still open */
	if (port->open_count) {
		urb->dev = port->serial->dev;
		result = usb_submit_urb(urb, GFP_ATOMIC);
		if (result)
			dev_err(&urb->dev->dev,
				"%s - failed resubmitting read urb, error %d\n",
				__FUNCTION__, result);
	}
	return;
}

 /* Circular Buffer Functions, code from ti_usb_3410_5052 used */

/*
 * serial_buf_alloc
 *
 * Allocate a circular buffer and all associated memory.
 */
static struct circ_buf *serial_buf_alloc(void)
{
	struct circ_buf *cb;

	cb = (struct circ_buf *)kmalloc(sizeof(struct circ_buf), GFP_KERNEL);
	if (cb == NULL)
		return NULL;

	cb->buf = kmalloc(AIRCABLE_BUF_SIZE, GFP_KERNEL);
	if (cb->buf == NULL) {
		kfree(cb);
		return NULL;
	}

	serial_buf_clear(cb);

	return cb;
}

/*
 * serial_buf_free
 *
 * Free the buffer and all associated memory.
 */
static void serial_buf_free(struct circ_buf *cb)
{
	kfree(cb->buf);
	kfree(cb);
}

/*
 * serial_buf_clear
 *
 * Clear out all data in the circular buffer.
 */
static void serial_buf_clear(struct circ_buf *cb)
{
	cb->head = cb->tail = 0;
}


/*
 * serial_buf_data_avail
 *
 * Return the number of bytes of data available in the circular
 * buffer.
 */
static int serial_buf_data_avail(struct circ_buf *cb)
{
	return CIRC_CNT(cb->head,cb->tail,AIRCABLE_BUF_SIZE);
}

/*
 * serial_buf_put
 *
 * Copy data data from a user buffer and put it into the circular buffer.
 * Restrict to the amount of space available.
 *
 * Return the number of bytes copied.
 */
static int serial_buf_put(struct circ_buf *cb, const char *buf, int count)
{
	int c, ret = 0;

	while (1) {
		c = CIRC_SPACE_TO_END(cb->head, cb->tail, AIRCABLE_BUF_SIZE);
		if (count < c)
			c = count;
		if (c <= 0)
			break;
		memcpy(cb->buf + cb->head, buf, c);
		cb->head = (cb->head + c) & (AIRCABLE_BUF_SIZE-1);
		buf += c;
		count -= c;
		ret= c;
	}

	return ret;
}

/*
 * serial_buf_get
 *
 * Get data from the circular buffer and copy to the given buffer.
 * Restrict to the amount of data available.
 *
 * Return the number of bytes copied.
 */

static int serial_buf_get(struct circ_buf *cb, char *buf, int count)
{
	int c, ret = 0;

	while (1) {
		c = CIRC_CNT_TO_END(cb->head, cb->tail, AIRCABLE_BUF_SIZE);
		if (count < c)
			c = count;
		if (c <= 0)
			break;
		memcpy(buf, cb->buf + cb->tail, c);
		cb->tail = (cb->tail + c) & (AIRCABLE_BUF_SIZE-1);
		buf += c;
		count -= c;
		ret= c;
	}

	return ret;
}

static int __init aircable_init (void)
{
	int retval;
	retval = usb_serial_register(&aircable_device);
	if (retval)
		goto failed_serial_register;
	retval = usb_register(&aircable_driver);
	if (retval) 
		goto failed_usb_register;
	return 0;

failed_serial_register:
	usb_serial_deregister(&aircable_device);
failed_usb_register:
	return retval;	
}

static void __exit aircable_exit (void)
{
	usb_deregister(&aircable_driver);
	usb_serial_deregister(&aircable_device);	
}

MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");

module_init(aircable_init);
module_exit(aircable_exit);

module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug enabled or not");

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share
your
opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
 
CD: 4ms