Skip to contentSkip to conversation

new driver synclink_gt

PATCH In review Paul Fulghum 2 participants 3 messages Nov 2, 2005 – Nov 5, 2005

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 new driver synclink_gt

Patches for a new character device driver for the SyncLink GT and SyncLink AC families of synchronous and asynchronous serial adapters are located at (patches too large for mailing list):

http://www.microgate.com/ftp/linux/synclink_gt-2.6.14/patch-2.6.14-Kconfig
http://www.microgate.com/ftp/linux/synclink_gt-2.6.14/patch-2.6.14-Makefile
http://www.microgate.com/ftp/linux/synclink_gt-2.6.14/patch-2.6.14-synclink.h
http://www.microgate.com/ftp/linux/synclink_gt-2.6.14/patch-2.6.14-synclink_gt.c

The complete driver in non patch form is at
http://www.microgate.com/ftp/linux/synclink_gt-2.6.14/synclink_gt.c

This driver had been tested for a few months and these particular patches have been tested against 2.6.14

Signature · 2 lines
Paul Fulghum
[email protected]
#1 lore raw
Quoted 10 lines · Paul Fulghum

Patches for a new character device driver for
the SyncLink GT and SyncLink AC families of synchronous and asynchronous serial adapters are located at (patches too large for mailing list):

http://www.microgate.com/ftp/linux/synclink_gt-2.6.14/patch-2.6.14-Kconfig
http://www.microgate.com/ftp/linux/synclink_gt-2.6.14/patch-2.6.14-Makefile
http://www.microgate.com/ftp/linux/synclink_gt-2.6.14/patch-2.6.14-synclink.h
http://www.microgate.com/ftp/linux/synclink_gt-2.6.14/patch-2.6.14-synclink_gt.c

There's not much point in presenting it as a patch-per-file, really - we need all four patches to have a buildable driver, so it may as well be a single patch.

We need to get a driverdude onto this - I'm not one any more. Quick notes:

- Tx (at least) appears to DMA straight out of kmalloced memory. If I read it right, it needs to use the PCI DMA API fully - pci_map_single()
and friends. <checks> Maybe I misread it.

- It seems to be both a tty driver and a net driver, or something. Can you please describe the hardware?

- Be aware that the tty ldisc APIs were redone in -mm, so this driver probably won't compile in -mm and fixups will be needed sometime.

Quoted 8 lines

/*
* compatibility and utility macros
*/
#define GET_USER(error,value,addr) error = get_user(value,addr)
#define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0 #define PUT_USER(error,value,addr) error = put_user(value,addr)
#define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0

eww. Please just open-code these.

Quoted 8 lines

/*
* module configuration and status
*/
static int pci_registered = 0;
...
static struct _slgt_info *slgt_device_list = NULL; static int slgt_device_count = 0;

Quoted 4 lines

static int ttymajor=0;
static int debug_level = 0;
static int maxframe[MAX_DEVICES] = {0,}; static int dosyncppp[MAX_DEVICES] = {0,};

None of these initialisations to zero are needed.

Quoted 4 lines

module_param(ttymajor, int, 0); module_param(debug_level, int, 0); module_param_array(maxframe, int, NULL, 0); module_param_array(dosyncppp, int, NULL, 0);

MODULE_PARM_DESCs needed, please.

Quoted 12 lines

/*
* device specific structures, macros and functions */

#define SLGT_MAX_PORTS 4
#define SLGT_REG_SIZE 256

/*
* DMA buffer descriptor and access macros */
typedef struct

Quoted 11 lines
{
	unsigned short count;
	unsigned short status;
	unsigned int pbuf;  /* physical address of data buffer */
	unsigned int next;  /* physical address of next descriptor */

	/* driver book keeping */
	char *buf;          /* virtual  address of data buffer */
    	unsigned int pdesc; /* physical address of this descriptor */
	dma_addr_t buf_dma_addr;
} SLGT_DESC;

typedefs are unpopular in new code. Just use `struct slgt_desc' everywhere.

Quoted 11 lines

#define set_desc_buffer(a,b) (a).pbuf = cpu_to_le32((unsigned int)(b))
#define set_desc_next(a,b) (a).next = cpu_to_le32((unsigned int)(b))
#define set_desc_count(a,b)(a).count = cpu_to_le16((unsigned short)(b))

#define set_desc_eof(a,b)  (a).status = cpu_to_le16((b) ? (le16_to_cpu((a).status) | BIT0) : (le16_to_cpu((a).status) & ~BIT0))
#define desc_count(a)      (le16_to_cpu((a).count))
#define desc_status(a)     (le16_to_cpu((a).status))
#define desc_complete(a)   (le16_to_cpu((a).status) & BIT15)
#define desc_eof(a)        (le16_to_cpu((a).status) & BIT2)
#define desc_crc_error(a)  (le16_to_cpu((a).status) & BIT1)
#define desc_abort(a)      (le16_to_cpu((a).status) & BIT0)
#define desc_residue(a)    ((le16_to_cpu((a).status) & 0x38) >> 3)

That makes the driver harder to follow, but I guess as long as it's followed religiously then it can make working on the driver more reliable.

Quoted 1 line

typedef struct _slgt_info {

Another typedef to remove, please.

Quoted 5 lines
#ifdef CONFIG_HDLC
	struct net_device *netdev;

#endif

} SLGT_INFO;

Ah, so the netdev is for hdlc transport.

Quoted 7 lines
static MGSL_PARAMS default_params = {
	MGSL_MODE_HDLC,			/* unsigned long mode */
	0,				/* unsigned char loopback; */
	HDLC_FLAG_UNDERRUN_ABORT15,	/* unsigned short flags; */
	HDLC_ENCODING_NRZI_SPACE,	/* unsigned char encoding; */
	0,				/* unsigned long clock_speed; */
	0xff,				/* unsigned char addr_filter; */
Quoted 8 lines
	HDLC_CRC_16_CCITT,		/* unsigned short crc_type; */
	HDLC_PREAMBLE_LENGTH_8BITS,	/* unsigned char preamble_length; */
	HDLC_PREAMBLE_PATTERN_NONE,	/* unsigned char preamble; */
	9600,				/* unsigned long data_rate; */
	8,				/* unsigned char data_bits; */
	1,				/* unsigned char stop_bits; */
	ASYNC_PARITY_NONE		/* unsigned char parity; */
};

Please use the

.field = value,

form here.

Quoted 12 lines

static void set_termios(struct tty_struct *tty, struct termios *old_termios)

{
	SLGT_INFO *info = (SLGT_INFO *)tty->driver_data;
	unsigned long flags;

	DBGINFO(("%s set_termios\n", tty->driver->name));

	/* just return if nothing has changed */
	if ((tty->termios->c_cflag == old_termios->c_cflag)
	    && (RELEVANT_IFLAG(tty->termios->c_iflag)
		== RELEVANT_IFLAG(old_termios->c_iflag)))
	  return;

whitespace went weird.

Quoted 16 lines

static void put_char(struct tty_struct *tty, unsigned char ch)

{
	SLGT_INFO *info = (SLGT_INFO *)tty->driver_data;
	SLGT_INFO *info = (SLGT_INFO *)tty->driver_data;
	unsigned long flags;

	if (sanity_check(info, tty->name, "put_char"))
		return;
	DBGINFO(("%s put_char(%d)\n", info->device_name, ch));
	if (!tty || !info->tx_buf)
		return;
	spin_lock_irqsave(&info->lock,flags);
	if (!info->tx_active && (info->tx_count < info->max_frame_size))
		info->tx_buf[info->tx_count++] = ch;
	spin_unlock_irqrestore(&info->lock,flags);
}

Is silent droppage on overflow OK?

Quoted 14 lines

/*
* Service an IOCTL request
*
* Arguments
*
* tty pointer to tty instance data
* file pointer to associated file object for device
* cmd IOCTL command code
* arg command argument/context
*
* Return 0 if success, otherwise error code
*/
static int ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)

Quoted 2 lines
{
	SLGT_INFO *info = (SLGT_INFO *)tty->driver_data;

Unneeded cast from void* (through the whole file, please).

Quoted 6 lines
	/* Append serial signal status to end */
	ret += sprintf(buf+ret, " %s\n", stat_buf+1);

	ret += sprintf(buf+ret, "\ttxactive=%d bh_req=%d bh_run=%d pending_bh=%x\n",
	 info->tx_active,info->bh_requested,info->bh_running,
	 info->pending_bh);

whitespace funnies.

Quoted 8 lines

static void bh_handler(void* context)

{
	SLGT_INFO *info = (SLGT_INFO*)context;
	int action;

	if (!info)
		return;
	info->bh_running = 1;

The handling of bh_running seems vague. Is some locking needed around it? Memory barriers, at least?

Quoted 14 lines

static void ri_change(SLGT_INFO *info)

{
	get_signals(info);
	DBGISR(("ri_change %s signals=%04X\n", info->device_name, info->signals));
	if ((info->ri_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
		slgt_irq_off(info, IRQ_RI);
		return;
	}
	info->icount.dcd++;
	if (info->signals & SerialSignal_RI) {
		info->input_signal_events.ri_up++;
        } else {
	        info->input_signal_events.ri_down++;
	}

whitespace

Quoted 26 lines

/* interrupt service routine
*
* irq interrupt number
* dev_id device ID supplied during interrupt registration
* regs interrupted processor context
*/
static irqreturn_t slgt_interrupt(int irq, void *dev_id, struct pt_regs * regs)

{
	SLGT_INFO *info;
	unsigned int gsr;
	unsigned int i;

	DBGISR(("slgt_interrupt irq=%d entry\n", irq));

	info = (SLGT_INFO *)dev_id;
	if (!info)
		return IRQ_NONE;

	spin_lock(&info->lock);

	while((gsr = rd_reg32(info, GSR) & 0xffffff00)) {
		DBGISR(("%s gsr=%08x\n", info->device_name, gsr));
		info->irq_occurred = 1;
		for(i=0; i < info->port_count ; i++) {
			if (info->port_array[i] == NULL)
				continue;
Quoted 8 lines
			if (gsr & (BIT8 << i))
				isr_serial(info->port_array[i]);
			if (gsr & (BIT16 << (i*2)))
				isr_rdma(info->port_array[i]);
			if (gsr & (BIT17 << (i*2)))
				isr_tdma(info->port_array[i]);
		}
	}

Many ISRs are designed to special-case an irq-pending value of 0xffffffff so they gracefully handle device unplug. I guess this device doesn't come in cardbus (yet), but maybe the same handling is needed for PCI hotplug.

Quoted 16 lines

static void shutdown(SLGT_INFO * info)

{
	unsigned long flags;

	if (!(info->flags & ASYNC_INITIALIZED))
		return;

	DBGINFO(("%s shutdown\n", info->device_name));

	/* clear status wait queue because status changes */
	/* can't happen after shutting down the hardware */
	wake_up_interruptible(&info->status_event_wait_q);
	wake_up_interruptible(&info->event_wait_q);

	del_timer(&info->tx_timer);
	del_timer(&info->rx_timer);

del_timer_sync() needed here - otherwise the timer handler could still be in progress.

Quoted 3 lines
	spin_lock_irqsave(&info->lock,flags);
 	set_signals(info);
	spin_unlock_irqrestore(&info->lock,flags);

This happens so often that it's worth creating a new function for it. Ditto get_signals().

Quoted 5 lines

static void free_tmp_rbuf(SLGT_INFO *info)

{
	if (info->tmp_rbuf)
		kfree(info->tmp_rbuf);

kfree(NULL) is legal.

Quoted 6 lines
	memset(info->bufs, 0, DESC_LIST_SIZE);

	info->rbufs = (SLGT_DESC*)info->bufs;
	info->tbufs = ((SLGT_DESC*)info->bufs) + info->rbuf_count;

	pbufs = (unsigned int)info->bufs_dma_addr;

No, it shouldn't put a dma_addr_t into an unsigned int. dma_addr_t is sometimes 64-bit on 32-bit machines. Basically, it should remain opaque.

Quoted 5 lines
		if (request_irq(port_array[0]->irq_level,
					slgt_interrupt,
					port_array[0]->irq_flags,
					port_array[0]->device_name,
					port_array[0]) < 0) {

The irq_flags seems a bit obfuscated. WHy not just remove it and hard-code SA_SHIRQ?

<attention fades>

Boy, it's a big driver...

#2 lore raw
Quoted 3 lines · Andrew Morton

There's not much point in presenting it as a patch-per-file, really - we need all four patches to have a buildable driver, so it may as well be a single patch

OK, the view on this seems to change frequently
and depends on who you are talking to, so I'm
never sure which way to go on a particular day.

My preference was to have a single patch, but the
view that patches absolutely must be broken into
tiny pieces even if the pieces make no sense
on their own seems to pop up a lot.

Quoted 3 lines

- Tx (at least) appears to DMA straight out of kmalloced memory. If I read it right, it needs to use the PCI DMA API fully - pci_map_single()
and friends. <checks> Maybe I misread it.

DMA memory (descriptors and data buffers for the rings) are allocated with pci_alloc_consistent().

Quoted 2 lines

- It seems to be both a tty driver and a net driver, or something. Can you please describe the hardware?

Yes, it is both a tty and net (generic hdlc) driver. The hardware is synchronous and asynchronous and can be used for normal async tty, hdlc tty, or hdlc networking.

Quoted 2 lines

- Be aware that the tty ldisc APIs were redone in -mm, so this driver probably won't compile in -mm and fixups will be needed sometime.

Crap

Quoted 1 line

eww. Please just open-code these.

These are left over from a vain attempt to decouple the driver from the kernel API change du jour (as per your previous comment). I will remove them.

Quoted 1 line

None of these initialisations to zero are needed.

OK

Quoted 1 line

MODULE_PARM_DESCs needed, please.

OK

Quoted 1 line

typedefs are unpopular in new code.

hmmmm, if that is the latest style then I'll change it.

Quoted 2 lines

That makes the driver harder to follow, but I guess as long as it's followed religiously then it can make working on the driver more reliable.

It enforces proper endian handling, field access, and bit positions. As you say, it is more reliable. It also makes the code more readable for me.

Quoted 3 lines

Please use the
.field = value,
form here.

OK

Quoted 1 line

Is silent droppage on overflow OK?

There is no return code in the API to indicate error. The caller should monitor the write room before calling. There is really nothing this function can do if too much data is blindly pushed at it.

Quoted 1 line

Unneeded cast from void* (through the whole file, please).

OK

Quoted 1 line

whitespace funnies.

I read them every Sunday. Ha.
I'll fix it.

Quoted 1 line

The handling of bh_running seems vague.

It is done so if the handler is already running,
no attempt is made to schedule or run it again.

Quoted 3 lines

Many ISRs are designed to special-case an irq-pending value of 0xffffffff so they gracefully handle device unplug. I guess this device doesn't come in cardbus (yet), but maybe the same handling is needed for PCI hotplug.

There probably will be a cardbus version.
The definition of that register does not guarantee that 0xffffffff won't happen in normal operation. But I can add a check to a different register as a hardware present check similar to the synclink_cs driver for the 16-bit PCMCIA card.

Quoted 2 lines

del_timer_sync() needed here - otherwise the timer handler could still be in progress.

OK

Quoted 2 lines

This happens so often that it's worth creating a new function for it. Ditto get_signals().

hmmm, I want to ponder that some and review how often it is used I've always though that get_signals_locked() type wrappers were ugly, but if it saves significant code size it make sense.

Quoted 1 line

kfree(NULL) is legal.

OK

Quoted 4 lines
Quoted 1 line
	pbufs = (unsigned int)info->bufs_dma_addr;

No, it shouldn't put a dma_addr_t into an unsigned int. dma_addr_t is sometimes 64-bit on 32-bit machines. Basically, it should remain opaque.

This is the 32 bit physical bus address passed to the hardware in the descriptor. The hardware DMA engine does not handle 64 bit addresses. The type should be changed to _u32 though.

Quoted 2 lines

The irq_flags seems a bit obfuscated. WHy not just remove it and hard-code SA_SHIRQ?

This one driver will (hopefully) handle all the variants of this family which may or may not share the same flag settings. Future kernel changes may add or remove more flags which may or may not apply to a particular variant.

I would prefer to leave it in place now rather than having to add it back in later to accommodate such changes.

Quoted 3 lines

<attention fades>

Boy, it's a big driver...

It does a lot, but I've worked to reduce the size as much as possible. It is an improvement over the original synclink driver, which is a little crufty after being dragged through the kernel over the last 8 years.

--
Paul Fulghum
Microgate Systems, Ltd.