📄 mos7720.c
字号:
/* * mos7720.c * Controls the Moschip 7720 usb to dual port serial convertor * * Copyright 2006 Moschip Semiconductor Tech. Ltd. * * 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, version 2 of the License. * * Developed by: * Vijaya Kumar <vijaykumar.gn@gmail.com> * Ajay Kumar <naanuajay@yahoo.com> * Gurudeva <ngurudeva@yahoo.com> * * Cleaned up from the original by: * Greg Kroah-Hartman <gregkh@suse.de> * * Originally based on drivers/usb/serial/io_edgeport.c which is: * Copyright (C) 2000 Inside Out Networks, All rights reserved. * Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com> */#include <linux/kernel.h>#include <linux/errno.h>#include <linux/init.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/serial.h>#include <linux/serial_reg.h>#include <linux/usb.h>#include <linux/usb/serial.h>#include <asm/uaccess.h>/* * Version Information */#define DRIVER_VERSION "1.0.0.4F"#define DRIVER_AUTHOR "Aspire Communications pvt Ltd."#define DRIVER_DESC "Moschip USB Serial Driver"/* default urb timeout */#define MOS_WDR_TIMEOUT (HZ * 5)#define MOS_PORT1 0x0200#define MOS_PORT2 0x0300#define MOS_VENREG 0x0000#define MOS_MAX_PORT 0x02#define MOS_WRITE 0x0E#define MOS_READ 0x0D/* Interrupt Rotinue Defines */#define SERIAL_IIR_RLS 0x06#define SERIAL_IIR_RDA 0x04#define SERIAL_IIR_CTI 0x0c#define SERIAL_IIR_THR 0x02#define SERIAL_IIR_MS 0x00#define NUM_URBS 16 /* URB Count */#define URB_TRANSFER_BUFFER_SIZE 32 /* URB Size *//* This structure holds all of the local port information */struct moschip_port{ __u8 shadowLCR; /* last LCR value received */ __u8 shadowMCR; /* last MCR value received */ __u8 shadowMSR; /* last MSR value received */ char open; struct async_icount icount; struct usb_serial_port *port; /* loop back to the owner */ struct urb *write_urb_pool[NUM_URBS];};/* This structure holds all of the individual serial device information */struct moschip_serial{ int interrupt_started;};static int debug;#define USB_VENDOR_ID_MOSCHIP 0x9710#define MOSCHIP_DEVICE_ID_7720 0x7720#define MOSCHIP_DEVICE_ID_7715 0x7715static struct usb_device_id moschip_port_id_table [] = { { USB_DEVICE(USB_VENDOR_ID_MOSCHIP,MOSCHIP_DEVICE_ID_7720) }, { } /* terminating entry */};MODULE_DEVICE_TABLE(usb, moschip_port_id_table);/* * mos7720_interrupt_callback * this is the callback function for when we have received data on the * interrupt endpoint. */static void mos7720_interrupt_callback(struct urb *urb){ int result; int length; int status = urb->status; __u8 *data; __u8 sp1; __u8 sp2; dbg("%s"," : Entering\n"); switch (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__, status); return; default: dbg("%s - nonzero urb status received: %d", __FUNCTION__, status); goto exit; } length = urb->actual_length; data = urb->transfer_buffer; /* Moschip get 4 bytes * Byte 1 IIR Port 1 (port.number is 0) * Byte 2 IIR Port 2 (port.number is 1) * Byte 3 -------------- * Byte 4 FIFO status for both */ /* the above description is inverted * oneukum 2007-03-14 */ if (unlikely(length != 4)) { dbg("Wrong data !!!"); return; } sp1 = data[3]; sp2 = data[2]; if ((sp1 | sp2) & 0x01) { /* No Interrupt Pending in both the ports */ dbg("No Interrupt !!!"); } else { switch (sp1 & 0x0f) { case SERIAL_IIR_RLS: dbg("Serial Port 1: Receiver status error or address " "bit detected in 9-bit mode\n"); break; case SERIAL_IIR_CTI: dbg("Serial Port 1: Receiver time out"); break; case SERIAL_IIR_MS: dbg("Serial Port 1: Modem status change"); break; } switch (sp2 & 0x0f) { case SERIAL_IIR_RLS: dbg("Serial Port 2: Receiver status error or address " "bit detected in 9-bit mode"); break; case SERIAL_IIR_CTI: dbg("Serial Port 2: Receiver time out"); break; case SERIAL_IIR_MS: dbg("Serial Port 2: Modem status change"); break; } }exit: result = usb_submit_urb(urb, GFP_ATOMIC); if (result) dev_err(&urb->dev->dev, "%s - Error %d submitting control urb\n", __FUNCTION__, result); return;}/* * mos7720_bulk_in_callback * this is the callback function for when we have received data on the * bulk in endpoint. */static void mos7720_bulk_in_callback(struct urb *urb){ int retval; unsigned char *data ; struct usb_serial_port *port; struct moschip_port *mos7720_port; struct tty_struct *tty; int status = urb->status; if (status) { dbg("nonzero read bulk status received: %d", status); return; } mos7720_port = urb->context; if (!mos7720_port) { dbg("%s","NULL mos7720_port pointer \n"); return ; } port = mos7720_port->port; dbg("Entering...%s", __FUNCTION__); data = urb->transfer_buffer; tty = port->tty; if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } if (!port->read_urb) { dbg("URB KILLED !!!"); return; } if (port->read_urb->status != -EINPROGRESS) { port->read_urb->dev = port->serial->dev; retval = usb_submit_urb(port->read_urb, GFP_ATOMIC); if (retval) dbg("usb_submit_urb(read bulk) failed, retval = %d", retval); }}/* * mos7720_bulk_out_data_callback * this is the callback function for when we have finished sending serial * data on the bulk out endpoint. */static void mos7720_bulk_out_data_callback(struct urb *urb){ struct moschip_port *mos7720_port; struct tty_struct *tty; int status = urb->status; if (status) { dbg("nonzero write bulk status received:%d", status); return; } mos7720_port = urb->context; if (!mos7720_port) { dbg("NULL mos7720_port pointer"); return ; } dbg("Entering ........."); tty = mos7720_port->port->tty; if (tty && mos7720_port->open) tty_wakeup(tty);}/* * send_mos_cmd * this function will be used for sending command to device */static int send_mos_cmd(struct usb_serial *serial, __u8 request, __u16 value, __u16 index, void *data){ int status; unsigned int pipe; u16 product = le16_to_cpu(serial->dev->descriptor.idProduct); __u8 requesttype; __u16 size = 0x0000; if (value < MOS_MAX_PORT) { if (product == MOSCHIP_DEVICE_ID_7715) { value = value*0x100+0x100; } else { value = value*0x100+0x200; } } else { value = 0x0000; if ((product == MOSCHIP_DEVICE_ID_7715) && (index != 0x08)) { dbg("serial->product== MOSCHIP_DEVICE_ID_7715"); //index = 0x01 ; } } if (request == MOS_WRITE) { request = (__u8)MOS_WRITE; requesttype = (__u8)0x40; value = value + (__u16)*((unsigned char *)data); data = NULL; pipe = usb_sndctrlpipe(serial->dev, 0); } else { request = (__u8)MOS_READ; requesttype = (__u8)0xC0; size = 0x01; pipe = usb_rcvctrlpipe(serial->dev,0); } status = usb_control_msg(serial->dev, pipe, request, requesttype, value, index, data, size, MOS_WDR_TIMEOUT); if (status < 0) dbg("Command Write failed Value %x index %x\n",value,index); return status;}static int mos7720_open(struct usb_serial_port *port, struct file * filp){ struct usb_serial *serial; struct usb_serial_port *port0; struct urb *urb; struct moschip_serial *mos7720_serial; struct moschip_port *mos7720_port; int response; int port_number; char data; int allocated_urbs = 0; int j; serial = port->serial; mos7720_port = usb_get_serial_port_data(port); if (mos7720_port == NULL) return -ENODEV; port0 = serial->port[0]; mos7720_serial = usb_get_serial_data(serial); if (mos7720_serial == NULL || port0 == NULL) return -ENODEV; usb_clear_halt(serial->dev, port->write_urb->pipe); usb_clear_halt(serial->dev, port->read_urb->pipe); /* Initialising the write urb pool */ for (j = 0; j < NUM_URBS; ++j) { urb = usb_alloc_urb(0,GFP_KERNEL); mos7720_port->write_urb_pool[j] = urb; if (urb == NULL) { err("No more urbs???"); continue; } urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL); if (!urb->transfer_buffer) { err("%s-out of memory for urb buffers.", __FUNCTION__); usb_free_urb(mos7720_port->write_urb_pool[j]); mos7720_port->write_urb_pool[j] = NULL; continue; } allocated_urbs++; } if (!allocated_urbs) return -ENOMEM; /* Initialize MCS7720 -- Write Init values to corresponding Registers * * Register Index * 1 : IER * 2 : FCR * 3 : LCR * 4 : MCR * * 0x08 : SP1/2 Control Reg */ port_number = port->number - port->serial->minor; send_mos_cmd(port->serial, MOS_READ, port_number, UART_LSR, &data); dbg("SS::%p LSR:%x\n",mos7720_port, data); dbg("Check:Sending Command .........."); data = 0x02; send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, 0x01, &data); data = 0x02; send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, 0x02, &data); data = 0x00; send_mos_cmd(serial, MOS_WRITE, port_number, 0x01, &data); data = 0x00; send_mos_cmd(serial, MOS_WRITE, port_number, 0x02, &data); data = 0xCF; send_mos_cmd(serial, MOS_WRITE, port_number, 0x02, &data); data = 0x03; mos7720_port->shadowLCR = data; send_mos_cmd(serial, MOS_WRITE, port_number, 0x03, &data); data = 0x0b; mos7720_port->shadowMCR = data; send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data); data = 0x0b; send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data); data = 0x00; send_mos_cmd(serial, MOS_READ, MOS_MAX_PORT, 0x08, &data); data = 0x00; send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, 0x08, &data);/* data = 0x00; send_mos_cmd(serial, MOS_READ, MOS_MAX_PORT, port_number + 1, &data); data = 0x03; send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, port_number + 1, &data); data = 0x00; send_mos_cmd(port->serial, MOS_WRITE, MOS_MAX_PORT, port_number + 1, &data);*/ data = 0x00; send_mos_cmd(serial, MOS_READ, MOS_MAX_PORT, 0x08, &data); data = data | (port->number - port->serial->minor + 1); send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, 0x08, &data); data = 0x83; mos7720_port->shadowLCR = data; send_mos_cmd(serial, MOS_WRITE, port_number, 0x03, &data); data = 0x0c; send_mos_cmd(serial, MOS_WRITE, port_number, 0x00, &data); data = 0x00; send_mos_cmd(serial, MOS_WRITE, port_number, 0x01, &data); data = 0x03; mos7720_port->shadowLCR = data; send_mos_cmd(serial, MOS_WRITE, port_number, 0x03, &data); data = 0x0c; send_mos_cmd(serial, MOS_WRITE, port_number, 0x01, &data); data = 0x0c; send_mos_cmd(serial, MOS_WRITE, port_number, 0x01, &data);//Matrix /* force low_latency on so that our tty_push actually forces * * the data through,otherwise it is scheduled, and with * * high data rates (like with OHCI) data can get lost. */ if (port->tty) port->tty->low_latency = 1; /* see if we've set up our endpoint info yet * * (can't set it up in mos7720_startup as the * * structures were not set up at that time.) */ if (!mos7720_serial->interrupt_started) { dbg("Interrupt buffer NULL !!!"); /* not set up yet, so do it now */ mos7720_serial->interrupt_started = 1; dbg("To Submit URB !!!"); /* set up our interrupt urb */ usb_fill_int_urb(port0->interrupt_in_urb, serial->dev, usb_rcvintpipe(serial->dev, port->interrupt_in_endpointAddress), port0->interrupt_in_buffer, port0->interrupt_in_urb->transfer_buffer_length, mos7720_interrupt_callback, mos7720_port, port0->interrupt_in_urb->interval); /* start interrupt read for this mos7720 this interrupt * * will continue as long as the mos7720 is connected */ dbg("Submit URB over !!!"); response = usb_submit_urb(port0->interrupt_in_urb, GFP_KERNEL); if (response) dev_err(&port->dev, "%s - Error %d submitting control urb\n", __FUNCTION__, response); } /* set up our bulk in urb */ usb_fill_bulk_urb(port->read_urb, serial->dev, usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), port->bulk_in_buffer, port->read_urb->transfer_buffer_length, mos7720_bulk_in_callback, mos7720_port); response = usb_submit_urb(port->read_urb, GFP_KERNEL); if (response) dev_err(&port->dev, "%s - Error %d submitting read urb\n", __FUNCTION__, response); /* initialize our icount structure */ memset(&(mos7720_port->icount), 0x00, sizeof(mos7720_port->icount)); /* initialize our port settings */ mos7720_port->shadowMCR = UART_MCR_OUT2; /* Must set to enable ints! */ /* send a open port command */ mos7720_port->open = 1; return 0;}/* * mos7720_chars_in_buffer * this function is called by the tty driver when it wants to know how many * bytes of data we currently have outstanding in the port (data that has * been written, but hasn't made it out the port yet) * If successful, we return the number of bytes left to be written in the * system, * Otherwise we return a negative error number. */static int mos7720_chars_in_buffer(struct usb_serial_port *port){ int i; int chars = 0; struct moschip_port *mos7720_port; dbg("%s:entering ...........", __FUNCTION__); mos7720_port = usb_get_serial_port_data(port); if (mos7720_port == NULL) { dbg("%s:leaving ...........", __FUNCTION__); return -ENODEV; } for (i = 0; i < NUM_URBS; ++i) { if (mos7720_port->write_urb_pool[i] && mos7720_port->write_urb_pool[i]->status == -EINPROGRESS) chars += URB_TRANSFER_BUFFER_SIZE; } dbg("%s - returns %d", __FUNCTION__, chars); return chars;}static void mos7720_close(struct usb_serial_port *port, struct file *filp){ struct usb_serial *serial; struct moschip_port *mos7720_port; char data; int j; dbg("mos7720_close:entering..."); serial = port->serial; mos7720_port = usb_get_serial_port_data(port); if (mos7720_port == NULL) return; for (j = 0; j < NUM_URBS; ++j) usb_kill_urb(mos7720_port->write_urb_pool[j]);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -