cregit-Linux how code gets into the kernel

Release 4.7 drivers/usb/serial/spcp8x5.c

/*
 * spcp8x5 USB to serial adaptor driver
 *
 * Copyright (C) 2010-2013 Johan Hovold (jhovold@gmail.com)
 * Copyright (C) 2006 Linxb (xubin.lin@worldplus.com.cn)
 * Copyright (C) 2006 S1 Corp.
 *
 * Original driver for 2.6.10 pl2303 driver by
 *   Greg Kroah-Hartman (greg@kroah.com)
 * Changes for 2.6.20 by Harald Klein <hari@vt100.at>
 *
 *      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/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>


#define DRIVER_DESC	"SPCP8x5 USB to serial adaptor driver"


#define SPCP825_QUIRK_NO_UART_STATUS	0x01

#define SPCP825_QUIRK_NO_WORK_MODE	0x02


#define SPCP8x5_007_VID		0x04FC

#define SPCP8x5_007_PID		0x0201

#define SPCP8x5_008_VID		0x04fc

#define SPCP8x5_008_PID		0x0235

#define SPCP8x5_PHILIPS_VID	0x0471

#define SPCP8x5_PHILIPS_PID	0x081e

#define SPCP8x5_INTERMATIC_VID	0x04FC

#define SPCP8x5_INTERMATIC_PID	0x0204

#define SPCP8x5_835_VID		0x04fc

#define SPCP8x5_835_PID		0x0231


static const struct usb_device_id id_table[] = {
	{ USB_DEVICE(SPCP8x5_PHILIPS_VID , SPCP8x5_PHILIPS_PID)},
	{ USB_DEVICE(SPCP8x5_INTERMATIC_VID, SPCP8x5_INTERMATIC_PID)},
	{ USB_DEVICE(SPCP8x5_835_VID, SPCP8x5_835_PID)},
	{ USB_DEVICE(SPCP8x5_008_VID, SPCP8x5_008_PID)},
	{ USB_DEVICE(SPCP8x5_007_VID, SPCP8x5_007_PID),
	  .driver_info = SPCP825_QUIRK_NO_UART_STATUS |
				SPCP825_QUIRK_NO_WORK_MODE },
	{ }					/* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, id_table);


struct spcp8x5_usb_ctrl_arg {
	
u8	type;
	
u8	cmd;
	
u8	cmd_type;
	
u16	value;
	
u16	index;
	
u16	length;
};


/* spcp8x5 spec register define */

#define MCR_CONTROL_LINE_RTS		0x02

#define MCR_CONTROL_LINE_DTR		0x01

#define MCR_DTR				0x01

#define MCR_RTS				0x02


#define MSR_STATUS_LINE_DCD		0x80

#define MSR_STATUS_LINE_RI		0x40

#define MSR_STATUS_LINE_DSR		0x20

#define MSR_STATUS_LINE_CTS		0x10

/* verdor command here , we should define myself */

#define SET_DEFAULT			0x40

#define SET_DEFAULT_TYPE		0x20


#define SET_UART_FORMAT			0x40

#define SET_UART_FORMAT_TYPE		0x21

#define SET_UART_FORMAT_SIZE_5		0x00

#define SET_UART_FORMAT_SIZE_6		0x01

#define SET_UART_FORMAT_SIZE_7		0x02

#define SET_UART_FORMAT_SIZE_8		0x03

#define SET_UART_FORMAT_STOP_1		0x00

#define SET_UART_FORMAT_STOP_2		0x04

#define SET_UART_FORMAT_PAR_NONE	0x00

#define SET_UART_FORMAT_PAR_ODD		0x10

#define SET_UART_FORMAT_PAR_EVEN	0x30

#define SET_UART_FORMAT_PAR_MASK	0xD0

#define SET_UART_FORMAT_PAR_SPACE	0x90


#define GET_UART_STATUS_TYPE		0xc0

#define GET_UART_STATUS			0x22

#define GET_UART_STATUS_MSR		0x06


#define SET_UART_STATUS			0x40

#define SET_UART_STATUS_TYPE		0x23

#define SET_UART_STATUS_MCR		0x0004

#define SET_UART_STATUS_MCR_DTR		0x01

#define SET_UART_STATUS_MCR_RTS		0x02

#define SET_UART_STATUS_MCR_LOOP	0x10


#define SET_WORKING_MODE		0x40

#define SET_WORKING_MODE_TYPE		0x24

#define SET_WORKING_MODE_U2C		0x00

#define SET_WORKING_MODE_RS485		0x01

#define SET_WORKING_MODE_PDMA		0x02

#define SET_WORKING_MODE_SPP		0x03


#define SET_FLOWCTL_CHAR		0x40

#define SET_FLOWCTL_CHAR_TYPE		0x25


#define GET_VERSION			0xc0

#define GET_VERSION_TYPE		0x26


#define SET_REGISTER			0x40

#define SET_REGISTER_TYPE		0x27


#define	GET_REGISTER			0xc0

#define GET_REGISTER_TYPE		0x28


#define SET_RAM				0x40

#define SET_RAM_TYPE			0x31


#define GET_RAM				0xc0

#define GET_RAM_TYPE			0x32

/* how come ??? */

#define UART_STATE			0x08

#define UART_STATE_TRANSIENT_MASK	0x75

#define UART_DCD			0x01

#define UART_DSR			0x02

#define UART_BREAK_ERROR		0x04

#define UART_RING			0x08

#define UART_FRAME_ERROR		0x10

#define UART_PARITY_ERROR		0x20

#define UART_OVERRUN_ERROR		0x40

#define UART_CTS			0x80


struct spcp8x5_private {
	
unsigned		quirks;
	
spinlock_t		lock;
	
u8			line_control;
};


static int spcp8x5_probe(struct usb_serial *serial, const struct usb_device_id *id) { usb_set_serial_data(serial, (void *)id); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
johan hovoldjohan hovold31100.00%1100.00%
Total31100.00%1100.00%


static int spcp8x5_port_probe(struct usb_serial_port *port) { const struct usb_device_id *id = usb_get_serial_data(port->serial); struct spcp8x5_private *priv; priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; spin_lock_init(&priv->lock); priv->quirks = id->driver_info; usb_set_serial_port_data(port, priv); port->port.drain_delay = 256; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
johan hovoldjohan hovold7183.53%480.00%
greg kroah-hartmangreg kroah-hartman1416.47%120.00%
Total85100.00%5100.00%


static int spcp8x5_port_remove(struct usb_serial_port *port) { struct spcp8x5_private *priv; priv = usb_get_serial_port_data(port); kfree(priv); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
johan hovoldjohan hovold2477.42%266.67%
greg kroah-hartmangreg kroah-hartman722.58%133.33%
Total31100.00%3100.00%


static int spcp8x5_set_ctrl_line(struct usb_serial_port *port, u8 mcr) { struct spcp8x5_private *priv = usb_get_serial_port_data(port); struct usb_device *dev = port->serial->dev; int retval; if (priv->quirks & SPCP825_QUIRK_NO_UART_STATUS) return -EPERM; retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_UART_STATUS_TYPE, SET_UART_STATUS, mcr, 0x04, NULL, 0, 100); if (retval != 0) { dev_err(&port->dev, "failed to set control lines: %d\n", retval); } return retval; }

Contributors

PersonTokensPropCommitsCommitProp
johan hovoldjohan hovold7170.30%480.00%
greg kroah-hartmangreg kroah-hartman3029.70%120.00%
Total101100.00%5100.00%


static int spcp8x5_get_msr(struct usb_serial_port *port, u8 *status) { struct spcp8x5_private *priv = usb_get_serial_port_data(port); struct usb_device *dev = port->serial->dev; u8 *buf; int ret; if (priv->quirks & SPCP825_QUIRK_NO_UART_STATUS) return -EPERM; buf = kzalloc(1, GFP_KERNEL); if (!buf) return -ENOMEM; ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_UART_STATUS, GET_UART_STATUS_TYPE, 0, GET_UART_STATUS_MSR, buf, 1, 100); if (ret < 0) dev_err(&port->dev, "failed to get modem status: %d\n", ret); dev_dbg(&port->dev, "0xc0:0x22:0:6 %d - 0x02%x\n", ret, *buf); *status = *buf; kfree(buf); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
johan hovoldjohan hovold11074.32%583.33%
greg kroah-hartmangreg kroah-hartman3825.68%116.67%
Total148100.00%6100.00%


static void spcp8x5_set_work_mode(struct usb_serial_port *port, u16 value, u16 index) { struct spcp8x5_private *priv = usb_get_serial_port_data(port); struct usb_device *dev = port->serial->dev; int ret; if (priv->quirks & SPCP825_QUIRK_NO_WORK_MODE) return; ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_WORKING_MODE_TYPE, SET_WORKING_MODE, value, index, NULL, 0, 100); dev_dbg(&port->dev, "value = %#x , index = %#x\n", value, index); if (ret < 0) dev_err(&port->dev, "failed to set work mode: %d\n", ret); }

Contributors

PersonTokensPropCommitsCommitProp
johan hovoldjohan hovold9687.27%480.00%
greg kroah-hartmangreg kroah-hartman1412.73%120.00%
Total110100.00%5100.00%


static int spcp8x5_carrier_raised(struct usb_serial_port *port) { u8 msr; int ret; ret = spcp8x5_get_msr(port, &msr); if (ret || msr & MSR_STATUS_LINE_DCD) return 1; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
johan hovoldjohan hovold3790.24%266.67%
greg kroah-hartmangreg kroah-hartman49.76%133.33%
Total41100.00%3100.00%


static void spcp8x5_dtr_rts(struct usb_serial_port *port, int on) { struct spcp8x5_private *priv = usb_get_serial_port_data(port); unsigned long flags; u8 control; spin_lock_irqsave(&priv->lock, flags); if (on) priv->line_control = MCR_CONTROL_LINE_DTR | MCR_CONTROL_LINE_RTS; else priv->line_control &= ~ (MCR_CONTROL_LINE_DTR | MCR_CONTROL_LINE_RTS); control = priv->line_control; spin_unlock_irqrestore(&priv->lock, flags); spcp8x5_set_ctrl_line(port, control); }

Contributors

PersonTokensPropCommitsCommitProp
johan hovoldjohan hovold5663.64%250.00%
greg kroah-hartmangreg kroah-hartman2831.82%125.00%
al viroal viro44.55%125.00%
Total88100.00%4100.00%


static void spcp8x5_init_termios(struct tty_struct *tty) { tty->termios = tty_std_termios; tty->termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL; tty->termios.c_ispeed = 115200; tty->termios.c_ospeed = 115200; }

Contributors

PersonTokensPropCommitsCommitProp
johan hovoldjohan hovold4489.80%133.33%
alan coxalan cox36.12%133.33%
greg kroah-hartmangreg kroah-hartman24.08%133.33%
Total49100.00%3100.00%


static void spcp8x5_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { struct usb_serial *serial = port->serial; struct spcp8x5_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned int cflag = tty->termios.c_cflag; unsigned short uartdata; unsigned char buf[2] = {0, 0}; int baud; int i; u8 control; /* check that they really want us to change something */ if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios)) return; /* set DTR/RTS active */ spin_lock_irqsave(&priv->lock, flags); control = priv->line_control; if (old_termios && (old_termios->c_cflag & CBAUD) == B0) { priv->line_control |= MCR_DTR; if (!(old_termios->c_cflag & CRTSCTS)) priv->line_control |= MCR_RTS; } if (control != priv->line_control) { control = priv->line_control; spin_unlock_irqrestore(&priv->lock, flags); spcp8x5_set_ctrl_line(port, control); } else { spin_unlock_irqrestore(&priv->lock, flags); } /* Set Baud Rate */ baud = tty_get_baud_rate(tty); switch (baud) { case 300: buf[0] = 0x00; break; case 600: buf[0] = 0x01; break; case 1200: buf[0] = 0x02; break; case 2400: buf[0] = 0x03; break; case 4800: buf[0] = 0x04; break; case 9600: buf[0] = 0x05; break; case 19200: buf[0] = 0x07; break; case 38400: buf[0] = 0x09; break; case 57600: buf[0] = 0x0a; break; case 115200: buf[0] = 0x0b; break; case 230400: buf[0] = 0x0c; break; case 460800: buf[0] = 0x0d; break; case 921600: buf[0] = 0x0e; break; /* case 1200000: buf[0] = 0x0f; break; */ /* case 2400000: buf[0] = 0x10; break; */ case 3000000: buf[0] = 0x11; break; /* case 6000000: buf[0] = 0x12; break; */ case 0: case 1000000: buf[0] = 0x0b; break; default: dev_err(&port->dev, "unsupported baudrate, using 9600\n"); } /* Set Data Length : 00:5bit, 01:6bit, 10:7bit, 11:8bit */ switch (cflag & CSIZE) { case CS5: buf[1] |= SET_UART_FORMAT_SIZE_5; break; case CS6: buf[1] |= SET_UART_FORMAT_SIZE_6; break; case CS7: buf[1] |= SET_UART_FORMAT_SIZE_7; break; default: case CS8: buf[1] |= SET_UART_FORMAT_SIZE_8; break; } /* Set Stop bit2 : 0:1bit 1:2bit */ buf[1] |= (cflag & CSTOPB) ? SET_UART_FORMAT_STOP_2 : SET_UART_FORMAT_STOP_1; /* Set Parity bit3-4 01:Odd 11:Even */ if (cflag & PARENB) { buf[1] |= (cflag & PARODD) ? SET_UART_FORMAT_PAR_ODD : SET_UART_FORMAT_PAR_EVEN ; } else { buf[1] |= SET_UART_FORMAT_PAR_NONE; } uartdata = buf[0] | buf[1]<<8; i = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), SET_UART_FORMAT_TYPE, SET_UART_FORMAT, uartdata, 0, NULL, 0, 100); if (i < 0) dev_err(&port->dev, "Set UART format %#x failed (error = %d)\n", uartdata, i); dev_dbg(&port->dev, "0x21:0x40:0:0 %d\n", i); if (cflag & CRTSCTS) { /* enable hardware flow control */ spcp8x5_set_work_mode(port, 0x000a, SET_WORKING_MODE_U2C); } }

Contributors

PersonTokensPropCommitsCommitProp
johan hovoldjohan hovold44274.29%654.55%
greg kroah-hartmangreg kroah-hartman14824.87%218.18%
alan coxalan cox50.84%327.27%
Total595100.00%11100.00%


static int spcp8x5_open(struct tty_struct *tty, struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct spcp8x5_private *priv = usb_get_serial_port_data(port); int ret; usb_clear_halt(serial->dev, port->write_urb->pipe); usb_clear_halt(serial->dev, port->read_urb->pipe); ret = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 0x09, 0x00, 0x01, 0x00, NULL, 0x00, 100); if (ret) return ret; spcp8x5_set_ctrl_line(port, priv->line_control); if (tty) spcp8x5_set_termios(tty, port, NULL); return usb_serial_generic_open(tty, port); }

Contributors

PersonTokensPropCommitsCommitProp
johan hovoldjohan hovold9067.67%480.00%
greg kroah-hartmangreg kroah-hartman4332.33%120.00%
Total133100.00%5100.00%


static int spcp8x5_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; struct spcp8x5_private *priv = usb_get_serial_port_data(port); unsigned long flags; u8 control; spin_lock_irqsave(&priv->lock, flags); if (set & TIOCM_RTS) priv->line_control |= MCR_RTS; if (set & TIOCM_DTR) priv->line_control |= MCR_DTR; if (clear & TIOCM_RTS) priv->line_control &= ~MCR_RTS; if (clear & TIOCM_DTR) priv->line_control &= ~MCR_DTR; control = priv->line_control; spin_unlock_irqrestore(&priv->lock, flags); return spcp8x5_set_ctrl_line(port, control); }

Contributors

PersonTokensPropCommitsCommitProp
greg kroah-hartmangreg kroah-hartman11790.70%133.33%
alan coxalan cox118.53%133.33%
johan hovoldjohan hovold10.78%133.33%
Total129100.00%3100.00%


static int spcp8x5_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct spcp8x5_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned int mcr; u8 status; unsigned int result; result = spcp8x5_get_msr(port, &status); if (result) return result; spin_lock_irqsave(&priv->lock, flags); mcr = priv->line_control; spin_unlock_irqrestore(&priv->lock, flags); result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) | ((mcr & MCR_RTS) ? TIOCM_RTS : 0) | ((status & MSR_STATUS_LINE_CTS) ? TIOCM_CTS : 0) | ((status & MSR_STATUS_LINE_DSR) ? TIOCM_DSR : 0) | ((status & MSR_STATUS_LINE_RI) ? TIOCM_RI : 0) | ((status & MSR_STATUS_LINE_DCD) ? TIOCM_CD : 0); return result; }

Contributors

PersonTokensPropCommitsCommitProp
greg kroah-hartmangreg kroah-hartman13682.42%133.33%
johan hovoldjohan hovold1810.91%133.33%
alan coxalan cox116.67%133.33%
Total165100.00%3100.00%

static struct usb_serial_driver spcp8x5_device = { .driver = { .owner = THIS_MODULE, .name = "SPCP8x5", }, .id_table = id_table, .num_ports = 1, .open = spcp8x5_open, .dtr_rts = spcp8x5_dtr_rts, .carrier_raised = spcp8x5_carrier_raised, .set_termios = spcp8x5_set_termios, .init_termios = spcp8x5_init_termios, .tiocmget = spcp8x5_tiocmget, .tiocmset = spcp8x5_tiocmset, .probe = spcp8x5_probe, .port_probe = spcp8x5_port_probe, .port_remove = spcp8x5_port_remove, }; static struct usb_serial_driver * const serial_drivers[] = { &spcp8x5_device, NULL }; module_usb_serial_driver(serial_drivers, id_table); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL");

Overall Contributors

PersonTokensPropCommitsCommitProp
johan hovoldjohan hovold112750.00%1144.00%
greg kroah-hartmangreg kroah-hartman106047.03%416.00%
alan coxalan cox452.00%624.00%
alan sternalan stern160.71%14.00%
al viroal viro40.18%14.00%
marton nemethmarton nemeth10.04%14.00%
libor pechaceklibor pechacek10.04%14.00%
Total2254100.00%25100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}