Re: [PATCH] new driver synclink_gt

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Paul Fulghum <[email protected]> wrote:
>
> 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.

> 
> /*
>  * 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.

> /*
>  * module configuration and status
>  */
> static int pci_registered = 0;
> ...
> static struct _slgt_info *slgt_device_list = NULL;
> static int slgt_device_count = 0;
> 
> 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.

> 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.

> 
> /*
>  * device specific structures, macros and functions
>  */
> 
> #define SLGT_MAX_PORTS 4
> #define SLGT_REG_SIZE  256
> 
> /*
>  * DMA buffer descriptor and access macros
>  */
> typedef struct
> {
> 	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.

> #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.

> typedef struct _slgt_info {

Another typedef to remove, please.

> #ifdef CONFIG_HDLC
> 	struct net_device *netdev;
> #endif
> 
> } SLGT_INFO;

Ah, so the netdev is for hdlc transport.

> 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; */
> 	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.

> 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.

> 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?

> /*
>  * 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)
> {
> 	SLGT_INFO *info = (SLGT_INFO *)tty->driver_data;

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

> 	/* 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.

> 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?

> 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

> /* 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;
> 			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.

> 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.

> 	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().

> 
> static void free_tmp_rbuf(SLGT_INFO *info)
> {
> 	if (info->tmp_rbuf)
> 		kfree(info->tmp_rbuf);

kfree(NULL) is legal.

> 	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.

> 		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...
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux