All of lore.kernel.org
 help / color / mirror / Atom feed
* New ark3116 driver - how to get included into kernel?
       [not found] <7eb6a4d80909170549k7a19eb01s8975fe2c5f230cee@mail.gmail.com>
@ 2009-09-17 12:52 ` Bart Hartgers
  2009-09-18  6:15   ` Greg KH
  0 siblings, 1 reply; 5+ messages in thread
From: Bart Hartgers @ 2009-09-17 12:52 UTC (permalink / raw)
  To: linux-usb, linux-kernel, gregkh; +Cc: Ondrej Zary, ark3116_driver

[-- Attachment #1: Type: text/plain, Size: 4892 bytes --]

(Sorry for sending an HTML-ized version of this mail before)

Hi All,

I managed to write an improved ark3116 driver after I figured out that
it is just an 16450 UART with some USB glue logic.

The attached files can be compiled outside the kernel tree, and work
for 2.6.31. However, I would like this driver to (eventually) end up
in the kernel tree. In order to get there, who should I sent patches
against what? I've contributed code to the kernel before, but not in
the last 5 or so years, so I am a bit out of touch.

Compared to the old ark3116 driver this one offers the following improvements:
 - cts/rts handshake support
 - break signalling
 - line error detection

Since it is a big step from the previous driver, it made little sense
to modify that one, so I created new driver, named ark316new. Also
this means that both could coexist if the new one doesn't work for
someone.

This driver also includes support for an ark3116-based IrDA dongle,
tested by Ondrej Zary.

Thanks in advance for any input/help.

Groeten,
Bart

>>
>> I also tried to include the IrDA fixes for the old ark3116.c driver
>> that were posted on this list recently, but I could not test if
>> these work, since I don't have the proper hardware.

2009/8/31 Bart Hartgers <bart.hartgers@gmail.com>
>
> 2009/8/29 Ondrej Zary <linux@rainbow-software.org>:
> > On Sunday 23 August 2009 14:46:16 Bart Hartgers wrote:
> >> Hi All,
> >>
> >> I managed to write an improved ark3116 driver after I figured out that
> >> it is just an 16450 UART with some USB glue logic.
> >>
> >> Compared to the old ark3116 driver it offers the following improvements:
> >> - cts/rts handshake support
> >> - break signalling
> >> - line error detection
> >>
> >> Since it is a big step from the previous driver, it made little sense
> >> to modify that one, so I created new driver, named ark316new. Also
> >> this means that both could coexist if the new one doesn't work for
> >> someone.
> >>
> >> I have no datasheet, so the driver is entirely based on reverse
> >> engineering, and some testing by others would be a good idea.
> >>
> >> I also tried to include the IrDA fixes for the old ark3116.c driver
> >> that were posted on this list recently, but I could not test if
> >> these work, since I don't have the proper hardware.
> >>
> >> Because of this, I would appreciate reports of both success and
> >> failure in using this driver.
> >
> > Tested the driver today with that Gembird UIR-22.
> >
> > It did not compile neither in 2.6.30.1 nor in 2.6.31-rc8 with the following
> > error:
> >
> > ark3116new.c:790: error: unknown field shutdown specified in initializer
> > ark3116new/ark3116new.c:790: warning: initialization from incompatible pointer
> > type
> >
> > Commenting that line produced a module that worked:
> > usb 1-1: new full speed USB device using uhci_hcd and address 2
> > usb 1-1: configuration #0 chosen from 1 choice
> > usb 1-1: config 0 descriptor??
> > usbcore: registered new interface driver usbserial
> > usbserial: USB Serial Driver core
> > USB Serial support registered for ARK3116 RS232/IrDA
> > ark3116new 1-1:0.0: ARK3116 RS232/IrDA converter detected
> > usb 1-1: ark3116new using IrDA mode
> > usb 1-1: ARK3116 RS232/IrDA converter now attached to ttyUSB0
> > usbcore: registered new interface driver ark3116new
> > ark3116new:v0.1:USB ARK3116 serial/IrDA driver
> > usb 1-1: ark3116new don't know how to do software flow control
> > NET: Registered protocol family 23
> >
> > Ran "irattach /dev/ttyUSB0 -s" and then "obexftp -i -l" to list files on Nokia
> > 6230i. It worked fine and file transfer too. So the IrDA mode works.
> >
> > When I unplugged the device, irattach remained running and some error message
> > was written to console every few seconds (sorry, I haven't recorded it).
> > Killing irattach then resulted in oops. But this might be expected as the
> > shutdown function was commented out.
> >
> Hi Ondrej,
>
> Thanks for testing this!
>
> I later discovered that there is a change with respect to
> shutdown-functionality between 2.6.28 (on which I made the driver) and
> 2.6.31. I have to update the driver to 2.6.current anyway.
>
> > I think that this driver should replace current ark3116 driver. Having two
> > drivers for the same hardware is a bad thing.
>
> I agree. However, given the experimental nature of the driver, I would
> prefer some more testing. Then again, the original driver is lacks
> some features, and making ark3116new default would increase its
> exposure ;-).
>
> Groeten,
> Bart
>
> >
> > --
> > Ondrej Zary
> >
>
>
>
> --
> Bart Hartgers - New e-mail: bart.hartgers@gmail.com



--
Bart Hartgers - New e-mail: bart.hartgers@gmail.com



--
Bart Hartgers - New e-mail: bart.hartgers@gmail.com

[-- Attachment #2: Makefile.txt --]
[-- Type: text/plain, Size: 493 bytes --]

obj-m    := ark3116new.o

KDIR    := /lib/modules/$(shell uname -r)/build
KMISC   := /lib/modules/$(shell uname -r)/kernel/drivers/usb/serial
PWD    := $(shell pwd)

EXTRA_CFLAGS += -I/usr/src/linux/drivers/usb/serial -I/usr/src/linux/include/linux/

modules:
	$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) $(EXTRA_CFLAGS) modules

install: modules
	install -d $(KMISC)
	install -m 644 -c ark3116new.ko $(KMISC)
	/sbin/depmod -a

clean:
	rm -f *.mod.c *.mod *.o *.ko .*.cmd
	rm -rf $(PWD)/.tmp_versions
	

[-- Attachment #3: ark3116new.c --]
[-- Type: text/x-csrc, Size: 24998 bytes --]

/*  -*- eval: c_set_style("linux") -*- */
/* 
 * Driver for the arkmicro 3116 usb2serial convertor chip.
 * 
 * (C) Copyright 2009 by Bart Hartgers (bart.hartgers+ark3116@gmail.com)
 * 
 * Supports full modem status lines, break, hardware flow control. Does not
 * support software flow control, since I do not know how to enable it in hw.
 * 
 * This driver is a new implementation. I initially dug into the old ark3116.c 
 * driver and suddenly realized the ark3116 is a 16450 with a USB interface 
 * glued to it. See comments at the bottom of this file.
 * 
 * Some concepts and code borrowed from Simon Schulz' original ark3116.c.
 *
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 * 
 */

#include <linux/kernel.h>
#include <linux/init.h>
#include <asm/atomic.h>
#include <linux/ioctl.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/serial.h>
#include <linux/serial_reg.h>
#include <linux/uaccess.h>
#include <linux/mutex.h>
#include <linux/capability.h>


#define ARK_DEBUG 0

/*
 * Version information
 */

#define DRIVER_VERSION "v0.2"
#define DRIVER_AUTHOR "Bart Hartgers <bart.hartgers+ark3116@gmail.com>"
#define DRIVER_DESC "USB ARK3116 serial/IrDA driver"
#define DRIVER_DEV_DESC "ARK3116 RS232/IrDA"
#define DRIVER_NAME "ark3116new"

/* usb timeout of 1 second */
#define ARK_TIMEOUT 1*HZ

static struct usb_device_id ark_id_table [] = {
	{ USB_DEVICE(0x6547, 0x0232) }, /* RS232 */
	{ USB_DEVICE(0x18ec, 0x3118) }, /* USB to IrDA */
	{ }
};

static int debug=ARK_DEBUG;

MODULE_DEVICE_TABLE(usb, ark_id_table);

struct ark_private
{
	/* for handling sleeping while waiting for 
	 * msr change to happen
	 */
	wait_queue_head_t       delta_msr_wait;
	struct async_icount	icount;
	int			irda;	/* 1 for irda device */
	
	/* protects hw register updates */
	struct mutex		lock;

	int			quot;	/* baudrate divisor */
	__u8			lcr;	/* line control register value */
	__u8			hcr;	/* handshake control register (0x8) 
					 * value */
	/* flags - updated asynchronously */
	atomic_t		mcr;	/* mcr value */
	atomic_t		msr;
	atomic_t		lsr;
};

static int ark_write_reg( struct usb_serial *serial, unsigned reg, __u8 val )
{
	int result;
	 /* 0xfe 0x40 are magic values taken from original ark3116.c */
	result = usb_control_msg( serial->dev, 
				  usb_sndctrlpipe(serial->dev,0),
				  0xfe, 0x40, val, reg,
				  NULL, 0, ARK_TIMEOUT );
	return result;
}

static int ark_read_reg( struct usb_serial *serial, unsigned reg, unsigned char *buf )
{
	int result;
	/* 0xfe 0xc0 are magic values taken from original ark3116.c */
	result = usb_control_msg( serial->dev,
				  usb_rcvctrlpipe(serial->dev,0),
				  0xfe, 0xc0, 0, reg,
				  buf, 1, ARK_TIMEOUT );
	if (result<0)
		return result;
	else
		return buf[0];
}

static inline void ark_atomic_set_clear( unsigned set, unsigned clear, atomic_t *at )
{
	if ((clear==0) && (set==0))
		return;
#if defined(atomic_set_mask)
	if (clear==0) {
		atomic_set_mask( set, at );
		return;
	}
#endif
#if defined(atomic_clear_mask)
	if (set==0) {
		atomic_clear_mask( clear, at );
		return;
	}
#endif
	/* operation needs to be atomic */
	for(;;) {
		register unsigned old=atomic_read( at );
		register unsigned prev=atomic_cmpxchg( at, old, (old|set)&(~clear) );
		if (likely(prev==old))
			break;
	}
}

static void ark_update_msr( struct usb_serial_port *port, __u8 msr )
{
	struct ark_private *priv = usb_get_serial_port_data(port);
	
	atomic_set(&priv->msr, msr );

	if (msr & UART_MSR_ANY_DELTA) {
		/* update input line counters */
		if (msr & UART_MSR_DCTS)
			priv->icount.cts++;
		if (msr & UART_MSR_DDSR)
			priv->icount.dsr++;
		if (msr & UART_MSR_DDCD)
			priv->icount.dcd++;
		if (msr & UART_MSR_TERI)
			priv->icount.rng++;
		wake_up_interruptible(&priv->delta_msr_wait);
	}
#if 0
	/* Handle CTS flow control. 
	 * I am not sure if this is really necessary:
	 * Because of hw handshake, the write URB will simply block
	 * when CTS is deasserted
	 */
	if (msr & UART_MSR_CTS) {
		struct tty_struct *tty = tty_port_tty_get(&port->port);
		if (tty && C_CRTSCTS(tty))
			tty_wakeup(tty);
		tty_kref_put(tty);
	}
#endif
}

static void ark_combine_lsr( struct usb_serial_port *port, __u8 lsr )
{
	struct ark_private *priv = usb_get_serial_port_data(port);
	
	/* combine bits */
	ark_atomic_set_clear( lsr, 0, &priv->lsr );

	if (lsr&UART_LSR_BRK_ERROR_BITS) {
		if (lsr & UART_LSR_BI)
			priv->icount.brk++;
		if (lsr & UART_LSR_FE)
			priv->icount.frame++;
		if (lsr & UART_LSR_PE)
			priv->icount.parity++;
		if (lsr & UART_LSR_OE)
			priv->icount.overrun++;
	}
}

static void ark_break_ctl( struct tty_struct *tty, int break_state )
{
	struct usb_serial_port *port = tty->driver_data;
	struct ark_private *priv = usb_get_serial_port_data(port);
	
	/* LCR is also used for other things: protect access */
	mutex_lock( &priv->lock );
	
	if (break_state)
		priv->lcr|=UART_LCR_SBC;
	else
		priv->lcr&=UART_LCR_SBC;
	
	ark_write_reg( port->serial, UART_LCR, priv->lcr );
	
	mutex_unlock( &priv->lock );
}

inline int calc_divisor( int bps )
{
	/* Original ark3116 made some exceptions in rounding here
	 * because windows did the same. Assume that is not really
	 * necessary.
	 * Crystal is 12MHz, probably because of USB, but we divide by 4?
	 */	
	return (12000000 + 2*bps) / (4*bps);
}

static void ark_set_termios( struct tty_struct *tty,
			     struct usb_serial_port *port,
			     struct ktermios *old_termios)
{
	struct usb_serial *serial = port->serial;
	struct ark_private *priv = usb_get_serial_port_data(port);
	struct ktermios *termios = tty->termios;
	unsigned cflag=termios->c_cflag;
	int bps = tty_get_baud_rate(tty);
	int quot;
	__u8 lcr, hcr, eval;
	
	/* set data bit count */
	switch(cflag & CSIZE) {
	case CS5:
		lcr=UART_LCR_WLEN5;
		break;
	case CS6:
		lcr=UART_LCR_WLEN6;
		break;
	case CS7:
		lcr=UART_LCR_WLEN7;
		break;
	default:
	case CS8:
		lcr=UART_LCR_WLEN8;
		break;
	}
	if (cflag & CSTOPB)
		lcr |= UART_LCR_STOP;
	if (cflag & PARENB)
		lcr |= UART_LCR_PARITY;
	if (!(cflag & PARODD))
		lcr |= UART_LCR_EPAR;
#ifdef CMSPAR
	if (cflag & CMSPAR)
		lcr |= UART_LCR_SPAR;
#endif
	/* handshake control */
	hcr = (cflag & CRTSCTS) ? 0x03 : 0x00;
	
	/* calc baudrate */
	dbg("%s - setting bps to %d",__func__,bps);
	eval = 0;
	switch(bps) {
	case 0:
		quot = calc_divisor(9600);
		break;
	default:
		if ((bps<75) || (bps>3000000)) {
		       bps = 9600;
		}
		quot = calc_divisor( bps );
		break;
	case 460800:
		eval = 1;
		quot = calc_divisor( bps );
		break;
	case 921600:
		eval = 2;
		quot = calc_divisor( bps );
		break;
	}

	/* Update state: synchronize */
	mutex_lock( &priv->lock );
		
	/* keep old LCR_SBC bit */
	lcr|=(priv->lcr & UART_LCR_SBC);
	
	dbg("%s - setting hcr:0x%02x,lcr:0x%02x,quot:%d", __func__, hcr, lcr, quot );
	
	/* handshake control */
	if (priv->hcr!=hcr) {
		priv->hcr=hcr;
		ark_write_reg( serial, 0x8, hcr );
	}
	
	/* baudrate */
	if (priv->quot!=quot) {
		priv->quot=quot;
		priv->lcr=lcr; /* need to write lcr anyway */
		
		/* disable DMA since transmit/receive is
		 * shadowed by UART_DLL
		 */
		ark_write_reg( serial, UART_FCR, 0 );
		
		ark_write_reg( serial, UART_LCR, 
			       lcr|UART_LCR_DLAB);
		ark_write_reg( serial, UART_DLL, quot & 0xff );
		ark_write_reg( serial, UART_DLM, (quot>>8) & 0xff );
		
		/* restore lcr */
		ark_write_reg( serial, UART_LCR, lcr );
		/* magic baudrate thingy: not sure what it does,
		 * but windows does this as well.
		 */
		ark_write_reg( serial, 0xe, eval );
		
		/* enable DMA */
		ark_write_reg( serial, UART_FCR, UART_FCR_DMA_SELECT );
	} else if (priv->lcr!=lcr) {
		priv->lcr=lcr;
		ark_write_reg( serial, UART_LCR, lcr );
	}
	
	mutex_unlock( &priv->lock );
	
	/* check for software flow control */
	if (I_IXOFF(tty) || I_IXON(tty)) {
		dev_warn( &serial->dev->dev,
			  "%s: don't know how to do software flow control\n",
			  KBUILD_MODNAME );
	}
	
	/* Don't rewrite B0 */
	if (tty_termios_baud_rate(termios))
		tty_termios_encode_baud_rate(termios, bps, bps);
}

static int ark_tiocmget(struct tty_struct *tty, struct file *file)
{
	struct usb_serial_port *port = tty->driver_data;
	struct ark_private *priv = usb_get_serial_port_data(port);
	
	/* read modem status */
	unsigned status = atomic_read( &priv->msr );
	/* modem control is output */
	unsigned ctrl = atomic_read( &priv->mcr );
	
	return  (status & UART_MSR_DSR  ? TIOCM_DSR  : 0) |
		(status & UART_MSR_CTS  ? TIOCM_CTS  : 0) |
		(status & UART_MSR_RI   ? TIOCM_RI   : 0) |
		(status & UART_MSR_DCD  ? TIOCM_CD   : 0) |
		(ctrl   & UART_MCR_DTR  ? TIOCM_DTR  : 0) |
		(ctrl   & UART_MCR_RTS  ? TIOCM_RTS  : 0) |
		(ctrl   & UART_MCR_OUT1 ? TIOCM_OUT1 : 0) |
		(ctrl   & UART_MCR_OUT2 ? TIOCM_OUT2 : 0);
}

static int ark_tiocmset(struct tty_struct *tty, struct file *file,
			unsigned set, unsigned clr)
{
	struct usb_serial_port *port = tty->driver_data;
	struct ark_private *priv = usb_get_serial_port_data(port);
	unsigned setmask=0;
	unsigned clrmask=0;
	
	if (set & TIOCM_RTS)
		setmask|=UART_MCR_RTS;
	if (set & TIOCM_DTR)
		setmask|=UART_MCR_DTR;
	if (set & TIOCM_OUT1)
		setmask|=UART_MCR_OUT1;
	if (set & TIOCM_OUT2)
		setmask|=UART_MCR_OUT2;
	if (clr & TIOCM_RTS)
		clrmask|=UART_MCR_RTS;
	if (clr & TIOCM_DTR)
		clrmask|=UART_MCR_DTR;
	if (clr & TIOCM_OUT1)
		clrmask|=UART_MCR_OUT1;
	if (clr & TIOCM_OUT2)
		clrmask|=UART_MCR_OUT2;

	ark_atomic_set_clear( setmask, clrmask, &priv->mcr );
	
	/* unfortunately, we need the mutex, to make sure that the value
	 * in priv->mcr is actually the one in the hardware
	 */
	
	mutex_lock( &priv->lock );
	ark_write_reg( port->serial, UART_MCR, atomic_read(&priv->mcr) );
	mutex_unlock( &priv->lock );
	
	return 0;
}

static void ark_read_int_callback( struct urb *urb )
{
	struct usb_serial_port *port = urb->context;
	int status = urb->status;
	const __u8 *data=urb->transfer_buffer;	
	int result;

	switch (status) {
	case -ECONNRESET:
	case -ENOENT:
	case -ESHUTDOWN:
		/* this urb is terminated, clean up */
		dbg("%s - urb shutting down with status: %d",
		    __func__, status);
		return;
	default:
		dbg("%s - nonzero urb status received: %d",
		    __func__, status);
		break;
	case 0: /* success */
		/* discovered this by trail and error... */
		if ((urb->actual_length==4) && (data[0]==0xe8))
		{
			const __u8 id=data[1]&UART_IIR_ID;
			dbg("%s: iir=%02x",__func__,data[1]);
			if (id==UART_IIR_MSI) {
				dbg("%s: msr=%02x",__func__,data[3]);
				ark_update_msr( port, data[3] );
				break;
			} else if (id==UART_IIR_RLSI) {
				dbg("%s: lsr=%02x",__func__,data[2]);
				ark_combine_lsr( port, data[2] );
				break;
			}
		}
		/*
		 * Not sure what this does yet...
		 */
		usb_serial_debug_data( debug, &port->dev, 
				       __func__,
				       urb->actual_length, 
				       urb->transfer_buffer);
		break;
	}

	result = usb_submit_urb(urb, GFP_ATOMIC);
	if (result)
		dev_err(&urb->dev->dev,
			"%s - Error %d submitting interrupt urb\n",
			__func__, result);
}


/* Data comes in via the bulk (data) URB, erors/interrupts via the int URB.
 * This means that we cannot be sure which data byte has an associated error 
 * condition, so we report an error for all data in the next bulk read. 
 *
 * Actually, there might even be a window between the bulk data leaving the 
 * ark and reading/resetting the lsr in the read_bulk_callback where an 
 * interrupt for the next data block could come in.
 * Without somekind of handshaking on the ark, we would have to report the 
 * error for the next block of data as well...
 * For now, let's pretend this can't happen.
 */

static void send_to_tty( struct tty_struct *tty, 
			 const unsigned char *chars,
			 size_t size, char flag )
{
	if (size==0)
		return;
	if (flag == TTY_NORMAL) {
		tty_insert_flip_string(tty, chars, size);
	} else {
		int i;
		for(i=0; i<size; ++i)
			tty_insert_flip_char(tty, chars[i], flag);
	}
}

static void ark_read_bulk_callback(struct urb *urb)
{
	struct usb_serial_port *port =  urb->context;
	struct ark_private *priv = usb_get_serial_port_data(port);
	const __u8 *data = urb->transfer_buffer;
	int status = urb->status;
	struct tty_struct *tty;
	unsigned long flags;
	int result;
	char flag;
	__u8 lsr;

	switch (status) {
	case -ECONNRESET:
	case -ENOENT:
	case -ESHUTDOWN:
		/* this urb is terminated, clean up */
		dbg("%s - urb shutting down with status: %d",
		    __func__, status);
		return;
	default:
		dbg("%s - nonzero urb status received: %d",
		    __func__, status);
		break;
	case 0: /* success */
		
		lsr = atomic_read(&priv->lsr);
		ark_atomic_set_clear( 0, UART_LSR_BRK_ERROR_BITS, 
				      &priv->lsr );

		/* Throttle the device if requested by tty */
		spin_lock_irqsave(&port->lock, flags);
		port->throttled = port->throttle_req;
		if (port->throttled) {
			spin_unlock_irqrestore(&port->lock, flags);
			return;
		} else
			spin_unlock_irqrestore(&port->lock, flags);
		
		if (unlikely(lsr & UART_LSR_BI))
			flag = TTY_BREAK;
		else if (unlikely(lsr & UART_LSR_PE))
			flag = TTY_PARITY;
		else if (unlikely(lsr & UART_LSR_FE))
			flag = TTY_FRAME;
		else
			flag = TTY_NORMAL;
		
		tty = tty_port_tty_get(&port->port);
		if (tty) {
			tty_buffer_request_room(tty, urb->actual_length + 1);
			/* overrun is special, not associated with a char */
			if (unlikely(lsr & UART_LSR_OE))
				tty_insert_flip_char(tty, 0, TTY_OVERRUN);
			send_to_tty(tty, data, urb->actual_length, flag );
			tty_flip_buffer_push(tty);
			tty_kref_put(tty);
		}
	}
	/* Continue reading from device */
#if 0   
	/* does not seem to be needed */
	usb_fill_bulk_urb(urb, port->serial->dev,
			  usb_rcvbulkpipe(port->serial->dev,
					  port->bulk_in_endpointAddress),
			  urb->transfer_buffer,
			  urb->transfer_buffer_length,
			  ark_read_bulk_callback,
			  port);
#endif
	result = usb_submit_urb(urb, GFP_ATOMIC);
	if (result)
		dev_err(&urb->dev->dev, "%s - failed resubmitting"
			" read urb, error %d\n", __func__, result);
}

static int ark_ioctl(struct tty_struct *tty, struct file *file,
		     unsigned int cmd, unsigned long arg)
{
	struct usb_serial_port *port = tty->driver_data;
	struct ark_private *priv = usb_get_serial_port_data(port);
	
	switch(cmd) {
	case TIOCMIWAIT:
		for(;;) {
			struct async_icount prev=priv->icount;
			interruptible_sleep_on(&priv->delta_msr_wait);
			/* see if a signal did it */
			if (signal_pending(current))
				return -ERESTARTSYS;
			if ((prev.rng==priv->icount.rng) &&
			    (prev.dsr==priv->icount.dsr) &&
			    (prev.dcd==priv->icount.dcd) &&
			    (prev.cts==priv->icount.cts)) 
				return -EIO;
			if ((arg & TIOCM_RNG && (prev.rng!=priv->icount.rng)) ||
			    (arg & TIOCM_DSR && (prev.dsr!=priv->icount.dsr)) ||
			    (arg & TIOCM_CD  && (prev.dcd!=priv->icount.dcd)) ||
			    (arg & TIOCM_CTS && (prev.cts!=priv->icount.cts)))
				return 0;
		}
		break;
	case TIOCGICOUNT: {
		struct serial_icounter_struct icount;	
		struct async_icount cnow=priv->icount;
		memset(&icount, 0, sizeof(icount));
		icount.cts = cnow.cts;
		icount.dsr = cnow.dsr;
		icount.rng = cnow.rng;
		icount.dcd = cnow.dcd;
		icount.rx = cnow.rx;
		icount.tx = cnow.tx;
		icount.frame = cnow.frame;
		icount.overrun = cnow.overrun;
		icount.parity = cnow.parity;
		icount.brk = cnow.brk;
		icount.buf_overrun = cnow.buf_overrun;
		if (copy_to_user((void __user *)arg, &icount, sizeof(icount)))
			return -EFAULT;
		return 0;
	}
	}
	return -ENOIOCTLCMD;
}

static void ark_close( struct usb_serial_port *port )
{
	struct usb_serial *serial = port->serial;

	/* disable DMA */
	ark_write_reg( serial, UART_FCR, 0 );
	
	/* deactivate interrupts */
	ark_write_reg( serial, UART_IER, 0 );
	
	if (serial->dev) {
		/* shutdown any bulk reads that might be going on */
		if (serial->num_bulk_out)
			usb_kill_urb(port->write_urb);
		if (serial->num_bulk_in)
			usb_kill_urb(port->read_urb);
		if (serial->num_interrupt_in)
			usb_kill_urb(port->interrupt_in_urb);
	}
}

static int ark_open(struct tty_struct *tty, struct usb_serial_port *port,
		    struct file *filp)
{
	struct ark_private *priv = usb_get_serial_port_data(port);
	struct usb_serial *serial = port->serial;
	int result;
	unsigned char *buf;
	
	buf = kmalloc( 4, GFP_KERNEL );
	if (buf==NULL)
		return -ENOMEM;
	
	result = usb_serial_generic_open(tty, port, filp);
	if (result) {
		dbg("%s - usb_serial_generic_open failed: %d", __func__, result );
		goto error_out;
	}

	/* setup termios */
	if (tty)
		ark_set_termios(tty, port, NULL );
	
	/* remove any data still left: also clears error state */
	ark_read_reg( serial, UART_RX, buf );
	
	/* read modem status */
	atomic_set( &priv->msr, ark_read_reg( serial, UART_MSR, buf ) );
	/* read line status */
	atomic_set( &priv->lsr, ark_read_reg( serial, UART_LSR, buf ) );
	
	result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
	if (result) {
		dev_err(&port->dev, "submit irq_in urb failed %d\n",
			result);
		ark_close( port );
		goto error_out;
	} 
	
	/* activate interrupts */
	ark_write_reg( port->serial, UART_IER, UART_IER_MSI|UART_IER_RLSI );
	    
	/* enable DMA */
	ark_write_reg( port->serial, UART_FCR, UART_FCR_DMA_SELECT );

error_out:
	kfree(buf);
	return result;
}

static int ark_is_irda( struct usb_serial *serial )
{
	__u16 vendor  = le16_to_cpu(serial->dev->descriptor.idVendor);
	__u16 product = le16_to_cpu(serial->dev->descriptor.idProduct);
	if ((product==0x3118) && (vendor==0x18ec))
		return 1;
	return 0;
}


static int ark_attach( struct usb_serial *serial )
{
	struct usb_serial_port *port=serial->port[0];
	struct ark_private *priv;
	
	/* make sure we have our end-points */
	if ((serial->num_bulk_in==0) || 
	    (serial->num_bulk_out==0) || 
	    (serial->num_interrupt_in==0)) {
		dev_err( &serial->dev->dev,
			 "%s - missing endpoint - "
			 "bulk in: %d, bulk out: %d, int in %d\n",
			 KBUILD_MODNAME,
			 serial->num_bulk_in,
			 serial->num_bulk_out,
			 serial->num_interrupt_in );
		return -EINVAL;
	}
	
	priv = kzalloc( sizeof(struct ark_private), 
					    GFP_KERNEL);
	if (!priv)
		return -ENOMEM;
	
	init_waitqueue_head(&priv->delta_msr_wait);
	mutex_init(&priv->lock);
	
	priv->irda = ark_is_irda( serial );
	
	usb_set_serial_port_data(port, priv);
	
	/* setup the hardware */
	ark_write_reg( serial, UART_IER, 0 );
	/* disable DMA */
	ark_write_reg( serial, UART_FCR, 0 );
	/* handshake control */
	priv->hcr = 0;
	ark_write_reg( serial, 0x8     , 0 );
	/* modem control */
	atomic_set( &priv->mcr, 0 );
	ark_write_reg( serial, UART_MCR, 0 );

	if (priv->irda==0) {
		ark_write_reg( serial, 0xb , 0    );
	} else {
		ark_write_reg( serial, 0xb , 1    );
		ark_write_reg( serial, 0xc , 0    );
		ark_write_reg( serial, 0xd , 0x41 );
		ark_write_reg( serial, 0xa , 0x01 );
	}
	
	/* setup baudrate */
	ark_write_reg( serial, UART_LCR, UART_LCR_DLAB );
	
	/* setup for 9600 8N1 */
	priv->quot=calc_divisor( 9600 );
	ark_write_reg( serial, UART_DLL, priv->quot & 0xff );
	ark_write_reg( serial, UART_DLM, (priv->quot>>8) & 0xff );
	
	priv->lcr = UART_LCR_WLEN8;
	ark_write_reg( serial, UART_LCR, UART_LCR_WLEN8 );

	ark_write_reg( serial, 0x0e , 0 );
	
	if (priv->irda) {
		ark_write_reg( serial, 0x9 , 0 );
	}
	
        dev_info( &serial->dev->dev,
		  "%s using %s mode\n",
		  KBUILD_MODNAME,
		  priv->irda ? "IrDA" : "RS232" );
	return 0;
}

static void ark_release( struct usb_serial *serial )
{
	struct usb_serial_port *port=serial->port[0];	
	struct ark_private *priv = usb_get_serial_port_data(port);
	
	/* device is closed, so URBs and DMA should be down */
	
	usb_set_serial_port_data(port, NULL );

	mutex_destroy( &priv->lock );
	
	kfree( priv );
}

static struct usb_driver ark_driver = {
	.name =		DRIVER_NAME,
	.probe =	usb_serial_probe,
	.disconnect =	usb_serial_disconnect,
	.id_table =	ark_id_table,
	.no_dynamic_id =	1,
};


static struct usb_serial_driver ark_device = {
	.driver = {
		.owner =	THIS_MODULE,
		.name =		DRIVER_NAME,
	},
	.description =		DRIVER_DEV_DESC,
	.id_table =		ark_id_table,
	.usb_driver =		&ark_driver,
	.num_ports =		1,
	.attach =		ark_attach,
	.release =		ark_release,
	.set_termios =		ark_set_termios,
	.tiocmget =		ark_tiocmget,
	.tiocmset =		ark_tiocmset,
	.ioctl =		ark_ioctl,
	.open =			ark_open,
	.close =		ark_close,
	.break_ctl = 		ark_break_ctl,
	.read_int_callback = 	ark_read_int_callback,
	.read_bulk_callback =	ark_read_bulk_callback,
};


static int __init ark_init(void)
{
	int retval;

	retval = usb_serial_register(&ark_device);
	if (retval==0) {
		retval = usb_register(&ark_driver);
		if (retval==0) {
			printk( KERN_INFO "%s:"
				DRIVER_VERSION ":"
				DRIVER_DESC "\n",
				KBUILD_MODNAME );
			return retval;
		}
		usb_serial_deregister(&ark_device);
	}
	return retval;
}

static void __exit ark_exit(void)
{
	usb_deregister(&ark_driver);
	usb_serial_deregister(&ark_device);
}

module_init(ark_init);
module_exit(ark_exit);

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

module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Enable debug");

/*
 * The following describes what I learned from studying the old
 * ark3116.c driver, disassembling the windows driver, and some lucky
 * guesses. Since I do not have any datasheet or other 
 * documentation, inaccuracies are almost guaranteed.
 * 
 * Some specs for the ARK3116 can be found here: 
 * http://web.archive.org/web/20060318000438/www.arkmicro.com/en/products/view.php?id=10
 * On that page, 2 GPIO pins are mentioned: I assume these are the 
 * OUT1 and OUT2 pins of the UART, so I added support for those
 * through the MCR. Since the pins are not available on my hardware,
 * I could not verify this. 
 * Also, it states there is "on-chip hardware flow control". I have
 * discovered how to enable that. Unfortunately, I do not know how to
 * enable XON/XOFF (software) flow control, which would need support
 * from the chip as well to work. Because of the wording on the web
 * page there is a real possibility the chip simply does not support
 * software flow control.
 * 
 * I got my ark3116 as part of a mobile phone adapter cable. On the
 * PCB, the following numbered contacts are present:
 * 
 *  1:- +5V
 *  2:o DTR 
 *  3:i RX  
 *  4:i DCD
 *  5:o RTS
 *  6:o TX
 *  7:i RI
 *  8:i DSR
 * 10:- 0V
 * 11:i CTS
 * 
 * On my chip, all signals seem to be 3.3V, but 5V tolerant. But that 
 * may be different for the one you have ;-).
 *
 * The windows driver limits the registers to 0-F, so I assume there 
 * are actually 16 present on the device.
 * 
 * On an UART interrupt, 4 bytes of data come in on the interrupt 
 * endpoint. The bytes are 0xe8 IIR LSR MSR.
 * 
 * The baudrate seems to be generated from the 12MHz crystal, using
 * 4-times subsampling. So quot=12e6/(4*baud). Also see description
 * of register E.
 * 
 * Registers 0-7: 
 * These seem to be the same as for a regular 16450. The FCR is set
 * to UART_FCR_DMA_SELECT (0x8), I guess to enable transfers between
 * the UART and the USB bridge/DMA engine. 
 * 
 * Register 8:
 * By trial and error, I found out that bit 0 enables hardware CTS,
 * stopping TX when CTS is +5V. Bit 1 does the same for RTS, making
 * RTS +5V when the 3116 cannot transfer the data to the USB bus
 * (verified by disabling the reading URB). Note that as far as I can
 * tell, the windows driver does NOT use this, so there might be some
 * hardware bug or something.
 *
 * According to a patch provided here
 * (http://lkml.org/lkml/2009/7/26/56), the ARK3116 can also be used
 * as an IrDA dongle. Since I do not have such a thing, I could not
 * investigate that aspect. However, I can speculate ;-).
 * 
 * - IrDA encodes data differently than RS232. Most likely, one of
 *   the bits in registers 9..E enables the IR ENDEC (encoder/decoder).
 * - Depending on the IR transceiver, the input and output need to be
 *   inverted, so there are probably bits for that as well.  
 * - IrDA is half-duplex, so there should be a bit for selecting that.
 * 
 * This still leaves at least two registers unaccounted for. Perhaps 
 * The chip can do XON/XOFF or CRC in HW?
 * 
 * Register 9:
 * Set to 0x00 for IrDA, when the baudrate is initialised.
 * 
 * Register A:
 * Set to 0x01 for IrDA, at init.
 * 
 * Register B:
 * Set to 0x01 for IrDA, 0x00 for RS232, at init.
 *
 * Register C:
 * Set to 00 for IrDA, at init.
 * 
 * Register D:
 * Set to 0x41 for IrDA, at init. 
 * 
 * Register E:
 * Somekind of baudrate override. The windows driver seems to set
 * this to 0x00 for normal baudrates, 0x01 for 460800, 0x02 for 921600.
 * Since 460800 and 921600 cannot be obtained by dividing 3MHz by an integer,
 * it could be somekind of subdivisor thingy.
 * However,it does not seem to do anything: selecting 921600 (divisor 3, 
 * reg E=2), still gets 1 MHz. I also checked if registers 9, C or F would 
 * work, but they don't.
 * 
 * Register F: unknown
 */

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: New ark3116 driver - how to get included into kernel?
  2009-09-17 12:52 ` New ark3116 driver - how to get included into kernel? Bart Hartgers
@ 2009-09-18  6:15   ` Greg KH
  2009-09-18 12:15     ` Bart Hartgers
  0 siblings, 1 reply; 5+ messages in thread
From: Greg KH @ 2009-09-18  6:15 UTC (permalink / raw)
  To: Bart Hartgers; +Cc: linux-usb, linux-kernel, Ondrej Zary, ark3116_driver

On Thu, Sep 17, 2009 at 02:52:29PM +0200, Bart Hartgers wrote:
> (Sorry for sending an HTML-ized version of this mail before)
> 
> Hi All,
> 
> I managed to write an improved ark3116 driver after I figured out that
> it is just an 16450 UART with some USB glue logic.
> 
> The attached files can be compiled outside the kernel tree, and work
> for 2.6.31. However, I would like this driver to (eventually) end up
> in the kernel tree. In order to get there, who should I sent patches
> against what? I've contributed code to the kernel before, but not in
> the last 5 or so years, so I am a bit out of touch.

Take a look at the file, Documentation/SubmittingPatches, it should
describe what you need to do.

> Compared to the old ark3116 driver this one offers the following improvements:
>  - cts/rts handshake support
>  - break signalling
>  - line error detection

Why can't you just send patches adding support for these features to the
existing driver?  It shouldn't be that much different between the two
versions, right?

That's the preferred method, I'd like to not drop the existing one if at
all possible.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: New ark3116 driver - how to get included into kernel?
  2009-09-18  6:15   ` Greg KH
@ 2009-09-18 12:15     ` Bart Hartgers
  2009-09-18 12:55       ` Oliver Neukum
  2009-09-18 16:18       ` Greg KH
  0 siblings, 2 replies; 5+ messages in thread
From: Bart Hartgers @ 2009-09-18 12:15 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-usb, linux-kernel, Ondrej Zary, ark3116_driver

Hi Greg,

Thanks for your reply.

2009/9/18 Greg KH <greg@kroah.com>:
> On Thu, Sep 17, 2009 at 02:52:29PM +0200, Bart Hartgers wrote:
>> (Sorry for sending an HTML-ized version of this mail before)
>>
>> Hi All,
>>
>> I managed to write an improved ark3116 driver after I figured out that
>> it is just an 16450 UART with some USB glue logic.
>>
>> The attached files can be compiled outside the kernel tree, and work
>> for 2.6.31. However, I would like this driver to (eventually) end up
>> in the kernel tree. In order to get there, who should I sent patches
>> against what? I've contributed code to the kernel before, but not in
>> the last 5 or so years, so I am a bit out of touch.
>
> Take a look at the file, Documentation/SubmittingPatches, it should
> describe what you need to do.
>
Thanks. But the question I had was more that I didn't know where to
put a new driver. In drivers/usb/serial, or perhaps in
drivers/staging. Anyway, if we are going to replace the existing
driver, it is obvious what the patch should be against.

>> Compared to the old ark3116 driver this one offers the following improvements:
>>  - cts/rts handshake support
>>  - break signalling
>>  - line error detection
>
> Why can't you just send patches adding support for these features to the
> existing driver?  It shouldn't be that much different between the two
> versions, right?

The difference is actually quite significant. The old driver is pretty
much a dumb parameterized replay of the windows usb-snoops. The new
driver actually "understands" the hardware. That's why I made a
completely new driver in the first place. A diff between the two is
ore or less the same as a complete replacing. I could try to minimize
the differences, but I would be surprised if more than 30% of the
lines will be shared, and most of those will be red tape, not actual
code. The patch will be hard to read anyhow.

>
> That's the preferred method, I'd like to not drop the existing one if at
> all possible.
>

Do you think it is worth the effort to minimize the diff, or should I
just replace ark3116.c by ark3116new.c?

Groeten,
Bart

> thanks,
>
> greg k-h
>



-- 
Bart Hartgers - New e-mail: bart.hartgers@gmail.com

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: New ark3116 driver - how to get included into kernel?
  2009-09-18 12:15     ` Bart Hartgers
@ 2009-09-18 12:55       ` Oliver Neukum
  2009-09-18 16:18       ` Greg KH
  1 sibling, 0 replies; 5+ messages in thread
From: Oliver Neukum @ 2009-09-18 12:55 UTC (permalink / raw)
  To: Bart Hartgers
  Cc: Greg KH, linux-usb, linux-kernel, Ondrej Zary, ark3116_driver

Am Freitag, 18. September 2009 14:15:57 schrieb Bart Hartgers:
> > That's the preferred method, I'd like to not drop the existing one if at
> > all possible.
>
> Do you think it is worth the effort to minimize the diff, or should I
> just replace ark3116.c by ark3116new.c?

The important part is not the size but the clarity of the diff.

	Regards
		Oliver


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: New ark3116 driver - how to get included into kernel?
  2009-09-18 12:15     ` Bart Hartgers
  2009-09-18 12:55       ` Oliver Neukum
@ 2009-09-18 16:18       ` Greg KH
  1 sibling, 0 replies; 5+ messages in thread
From: Greg KH @ 2009-09-18 16:18 UTC (permalink / raw)
  To: Bart Hartgers; +Cc: linux-usb, linux-kernel, Ondrej Zary, ark3116_driver

On Fri, Sep 18, 2009 at 02:15:57PM +0200, Bart Hartgers wrote:
> Hi Greg,
> 
> Thanks for your reply.
> 
> 2009/9/18 Greg KH <greg@kroah.com>:
> > On Thu, Sep 17, 2009 at 02:52:29PM +0200, Bart Hartgers wrote:
> >> (Sorry for sending an HTML-ized version of this mail before)
> >>
> >> Hi All,
> >>
> >> I managed to write an improved ark3116 driver after I figured out that
> >> it is just an 16450 UART with some USB glue logic.
> >>
> >> The attached files can be compiled outside the kernel tree, and work
> >> for 2.6.31. However, I would like this driver to (eventually) end up
> >> in the kernel tree. In order to get there, who should I sent patches
> >> against what? I've contributed code to the kernel before, but not in
> >> the last 5 or so years, so I am a bit out of touch.
> >
> > Take a look at the file, Documentation/SubmittingPatches, it should
> > describe what you need to do.
> >
> Thanks. But the question I had was more that I didn't know where to
> put a new driver. In drivers/usb/serial, or perhaps in
> drivers/staging. Anyway, if we are going to replace the existing
> driver, it is obvious what the patch should be against.

Yes, please work on fixing up the existing driver, that's the proper way
to do it.

> >> Compared to the old ark3116 driver this one offers the following improvements:
> >>  - cts/rts handshake support
> >>  - break signalling
> >>  - line error detection
> >
> > Why can't you just send patches adding support for these features to the
> > existing driver?  It shouldn't be that much different between the two
> > versions, right?
> 
> The difference is actually quite significant. The old driver is pretty
> much a dumb parameterized replay of the windows usb-snoops. The new
> driver actually "understands" the hardware. That's why I made a
> completely new driver in the first place. A diff between the two is
> ore or less the same as a complete replacing. I could try to minimize
> the differences, but I would be surprised if more than 30% of the
> lines will be shared, and most of those will be red tape, not actual
> code. The patch will be hard to read anyhow.

Then work on converting the driver over incrementally, one step at a
time, so that the patches are readable and understandable :)

> > That's the preferred method, I'd like to not drop the existing one if at
> > all possible.
> >
> 
> Do you think it is worth the effort to minimize the diff, or should I
> just replace ark3116.c by ark3116new.c?

Well, I will not take such a patch, so I would work on the incremental
change version instead.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2009-09-18 16:21 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <7eb6a4d80909170549k7a19eb01s8975fe2c5f230cee@mail.gmail.com>
2009-09-17 12:52 ` New ark3116 driver - how to get included into kernel? Bart Hartgers
2009-09-18  6:15   ` Greg KH
2009-09-18 12:15     ` Bart Hartgers
2009-09-18 12:55       ` Oliver Neukum
2009-09-18 16:18       ` Greg KH

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.