admin管理员组文章数量:1582706
/*
* USB Serial Converter Generic functions
*
* Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
* Copyright (C) 2009 - 2010 Liu Xiang-Shuang (13361295927@qq)
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <asm/uaccess.h>
//
#define ACM_NW 16
#define ACM_NR 16
struct acm_rb {
struct list_head list;
int size;
unsigned char *base;
dma_addr_t dma;
};
struct acm_ru {
struct list_head list;
struct acm_rb *buffer;
struct urb *urb;
struct acm *instance;
};
struct acm {
struct usb_device *dev; /* the corresponding usb device */
struct usb_interface *data; /* data interface */
struct tty_struct *tty; /* the corresponding tty */
struct acm_rb wb[ACM_NW];
struct acm_ru wu[ACM_NW];
struct list_head spare_write_urbs;
struct list_head spare_write_bufs;
struct acm_ru ru[ACM_NR];
struct acm_rb rb[ACM_NR];
int rx_buflimit;
int rx_endpoint;
spinlock_t read_lock;
struct list_head spare_read_urbs;
struct list_head spare_read_bufs;
struct list_head filled_read_bufs;
int write_ready; /* write urb is not running */
spinlock_t write_lock;
struct mutex mutex;
//struct usb_cdc_line_coding line; /* bits, stop, parity */
struct work_struct work; /* work queue entry for line discipline waking up */
struct tasklet_struct urb_task; /* rx processing */
spinlock_t throttle_lock; /* synchronize throtteling and read callback */
unsigned int writesize; /* max packet size for the output bulk endpoint */
unsigned int readsize,ctrlsize; /* buffer sizes for freeing */
unsigned int used; /* someone has this acm's device open */
unsigned char throttle; /* throttled by tty layer */
unsigned char clocal; /* termios CLOCAL */
unsigned int susp_count; /* number of suspended interfaces */
unsigned int skip;
};
#define PORT_READY(acm) (acm && acm->dev && acm->used)
//
#define DRIVER_VERSION "v0.1"
#define DRIVER_AUTHOR "Liu-xs"
#define DRIVER_DESC "TD modem usb serial driver"
//extern void userial_auto_suspend(void);
extern void userial_auto_lock_suspend(void);
static int debug;
static int wb_count_test = ACM_NW;
#define MNAME "[USB High-speed serial]"
static int usb_serial_highspeed_probe(struct usb_serial_port *port);
static void usb_serial_highspeed_read_bulk_callback(struct urb *urb);
static int generic_probe(struct usb_interface *interface,
const struct usb_device_id *id);
static int generic_suspend(struct usb_interface *intf, pm_message_t message);
static int generic_resume(struct usb_interface *intf);
static __u16 vendor = 0x1ab7;
static __u16 product = 0x6000;
module_param(vendor, ushort, 0);
MODULE_PARM_DESC(vendor, "User specified USB idVendor");
module_param(product, ushort, 0);
MODULE_PARM_DESC(product, "User specified USB idProduct");
static struct usb_device_id hs_device_ids[2]; /* Initially all zeroes. */
/* we want to look at all devices, as the vendor/product id can change
* depending on the command line argument */
static struct usb_device_id hs_serial_ids[] = {
{ USB_DEVICE(0x1ab7, 0x6000) },
{}
};
static int generic_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
const struct usb_device_id *id_pattern;
id_pattern = usb_match_id(interface, hs_device_ids);
if (id_pattern != NULL)
return usb_serial_probe(interface, id);
return -ENODEV;
}
static int generic_suspend(struct usb_interface *intf, pm_message_t message)
{
struct usb_serial *serial = usb_get_intfdata(intf);
//printk("%s\n",__FUNCTION__);
if(serial->type && serial->type->suspend)
return serial->type->suspend(serial, message);
return 0;
}
static int generic_resume(struct usb_interface *intf)
{
struct usb_serial *serial = usb_get_intfdata(intf);
//printk("%s\n",__FUNCTION__);
if(serial->type && serial->type->resume)
return serial->type->resume(serial);
return 0;
}
void usb_serial_highspeed_stop_data_traffic(struct acm *acm)
{
int i;
dbg("%s - !!!!", __FUNCTION__ );
tasklet_disable(&acm->urb_task);
for (i = 0; i < acm->rx_buflimit; i++)
usb_kill_urb(acm->ru[i].urb);
for (i = 0; i < ACM_NW; i++)
usb_kill_urb(acm->wu[i].urb);
INIT_LIST_HEAD(&acm->filled_read_bufs);
INIT_LIST_HEAD(&acm->spare_read_bufs);
tasklet_enable(&acm->urb_task);
cancel_work_sync(&acm->work);
}
static int usb_serial_highspeed_open (struct usb_serial_port *port, struct file *filp)
{
struct usb_serial *serial = port->serial;
int result = 0;
unsigned long flags;
struct acm *acm = usb_get_serial_port_data(port);
int i;
dbg("%s - port %d", __FUNCTION__, port->number);
/* 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;
else
pr_err(" tty is NULL!");
/* clear the throttle flags */
spin_lock_irqsave(&port->lock, flags);
acm->throttle = 0;
spin_unlock_irqrestore(&port->lock, flags);
/* if we have a bulk endpoint, start reading from it */
if (serial->num_bulk_in == 0) {
pr_err("++++++ num_bulk_in = 0 \n");
return result;
}
mutex_lock(&acm->mutex);
if (acm->used++) {
return result;
}
acm->tty = port->tty;
INIT_LIST_HEAD(&acm->spare_read_urbs);
INIT_LIST_HEAD(&acm->spare_read_bufs);
INIT_LIST_HEAD(&acm->filled_read_bufs);
INIT_LIST_HEAD(&acm->spare_write_urbs);
INIT_LIST_HEAD(&acm->spare_write_bufs);
for (i = 0; i < acm->rx_buflimit; i++) {
list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
}
for (i = 0; i < acm->rx_buflimit; i++) {
list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
}
for (i = 0; i < ACM_NW; i++) {
list_add(&(acm->wu[i].list), &acm->spare_write_urbs);
}
for (i = 0; i < ACM_NW; i++) {
list_add(&(acm->wb[i].list), &acm->spare_write_bufs);
}
tasklet_schedule(&acm->urb_task);
mutex_unlock(&acm->mutex);
return result;
}
static void usb_serial_highspeed_cleanup (struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
struct acm * acm =usb_get_serial_port_data(port);
dbg("%s - port %d", __FUNCTION__, port->number);
if (!acm || !acm->used)
return;
if (!--acm->used) {
if (serial->dev) {
usb_serial_highspeed_stop_data_traffic(acm);
}
}
}
int usb_serial_highspeed_suspend(struct usb_serial *serial, pm_message_t message)
{
struct usb_serial_port *port;
int i, c = 0;
#ifdef CONFIG_PM
/*
* If this is an autoresume, don't submit URBs.
* They will be submitted in the open function instead.
*/
if (serial->dev->auto_pm)
return 0;
#endif
for (i = 0; i < serial->num_ports; i++) {
struct acm *acm ;
port = serial->port[i];
acm = usb_get_serial_port_data(port);
if (acm->susp_count++)
continue;
pr_err("usb_serial_highspeed_suspend !\n");
/*
we treat opened interfaces differently,
we must guard against open
*/
mutex_lock(&acm->mutex);
if (acm->used)
usb_serial_highspeed_stop_data_traffic(acm);
mutex_unlock(&acm->mutex);
}
return c ? -EIO : 0;
}
int usb_serial_highspeed_resume(struct usb_serial *serial)
{
struct usb_serial_port *port;
int i, j,c = 0;
#ifdef CONFIG_PM
/*
* If this is an autoresume, don't submit URBs.
* They will be submitted in the open function instead.
*/
if (serial->dev->auto_pm)
return 0;
#endif
for (i = 0; i < serial->num_ports; i++) {
struct acm *acm ;
port = serial->port[i];
acm = usb_get_serial_port_data(port);
if (--acm->susp_count)
continue;
pr_err("usb_serial_highspeed_resume !\n");
mutex_lock(&acm->mutex);
if (acm->used) {
for (j = 0; j < acm->rx_buflimit; j++) {
list_add(&(acm->rb[j].list), &acm->spare_read_bufs);
}
acm->skip = 0;
tasklet_schedule(&acm->urb_task);
}
mutex_unlock(&acm->mutex);
}
return c ? -EIO : 0;
}
void usb_serial_highspeed_close (struct usb_serial_port *port, struct file * filp)
{
pr_err("%s - port %d", __FUNCTION__, port->number);
usb_serial_highspeed_cleanup (port);
}
/* Little helper: write buffers free */
static void acm_write_buffers_free(struct acm *acm)
{
int i;
for (i = 0; i < ACM_NW; i++) {
struct acm_ru *wu = &(acm->wu[i]);
struct acm_rb *wb = &(acm->wb[i]);
usb_free_urb(wu->urb);
usb_buffer_free(acm->dev, acm->writesize, wb->base, wb->dma);
}
}
static int acm_wb_is_avail(struct acm *acm)
{
int n = 0;
struct list_head *p;
spin_lock(&acm->write_lock);
list_for_each(p, &acm->spare_write_urbs)n++;
spin_unlock(&acm->write_lock);
return n;
}
/* data interface wrote those outgoing bytes */
static void acm_write_bulk(struct urb *urb)
{
struct acm_rb *buf;
struct acm_ru *wu = urb->context;
struct acm *acm = wu->instance;
int status = urb->status;
if (!PORT_READY(acm))
return;
if (status)
//dev_dbg(&acm->data->dev, "bulk tx status %d\n", status);
pr_err(" !!!bulk tx status : %d\n", status);
if (acm->susp_count)
pr_err(" acm_write_bulk :after suspend \n");
#ifdef CONFIG_ANDROID_POWER
userial_auto_lock_suspend();
#endif
buf = wu->buffer;
spin_lock(&acm->write_lock);
list_add_tail(&wu->list, &acm->spare_write_urbs);
list_add_tail(&buf->list, &acm->spare_write_bufs);
wb_count_test ++;
spin_unlock(&acm->write_lock);
if (likely(status == 0)) {
schedule_work(&acm->work);
acm->write_ready = 1;
//pr_err(" !!!bulk tx actual_length : %d\n", urb->actual_length);
} else {
/* nevertheless the tasklet must be kicked unconditionally
so the queue cannot dry up */
if(acm_wb_is_avail( acm)<ACM_NW)
acm->write_ready = 0;
else
schedule_work(&acm->work);
}
}
static void acm_softint(struct work_struct *work)
{
struct acm *acm = container_of(work, struct acm, work);
dbg("Entering acm_softint.");
if (!PORT_READY(acm))
return;
if(acm->write_ready)
tty_wakeup(acm->tty);
}
void show_usb_log (int on)
{
debug = on;
}
int usb_serial_highspeed_write(struct usb_serial_port *port, const unsigned char *buf, int count)
{
struct acm * acm =usb_get_serial_port_data(port);
int stat = 0;
unsigned long flags;
struct acm_rb *wbuf;
struct acm_ru *wu;
dbg("%s - port %d", __FUNCTION__, port->number);
if (count == 0) {
dbg("%s - write request of 0 bytes", __FUNCTION__);
return (0);
}
while(count>0) {
int writesize;
spin_lock_irqsave(&acm->write_lock, flags);
if(list_empty(&acm->spare_write_bufs) || list_empty(&acm->spare_write_urbs)){
spin_unlock_irqrestore(&acm->write_lock, flags);
break;
}
wu = list_entry(acm->spare_write_urbs.next,
struct acm_ru, list);
list_del(&wu->list);
wb_count_test --;
wbuf = list_entry(acm->spare_write_bufs.next,
struct acm_rb, list);
list_del(&wbuf->list);
spin_unlock_irqrestore(&acm->write_lock, flags);
writesize = (count > acm->writesize) ? acm->writesize : count;
memcpy(wbuf->base, buf, writesize);
wu->buffer = wbuf;
wu->urb->transfer_buffer = wbuf->base;
wu->urb->transfer_buffer_length = writesize;
wu->urb->transfer_dma = wbuf->dma;
wu->urb->context = wu;
if (usb_submit_urb(wu->urb, GFP_ATOMIC) < 0) {
spin_lock_irqsave(&acm->write_lock, flags);
list_add(&wbuf->list, &acm->spare_write_bufs);
list_add(&wu->list, &acm->spare_write_urbs);
wb_count_test ++;
spin_unlock_irqrestore(&acm->write_lock, flags);
break;
}
stat += writesize;
buf += writesize;
count -= writesize;
// acm->write_ready = 0;
}
if(debug)
pr_err(" !!!serial_write tx count : %d; stat : %d; wb_count_test: %d\n", count, stat, wb_count_test);
return stat;
/* no bulk out, so return 0 bytes written */
return 0;
}
int usb_serial_highspeed_write_room (struct usb_serial_port *port)
{
struct acm * acm =usb_get_serial_port_data(port);
dbg("%s - port %d", __FUNCTION__, port->number);
if (!PORT_READY(acm))
return -EINVAL;
/*
* Do not let the line discipline to know that we have a reserve,
* or it might get too enthusiastic.
*/
return (acm->write_ready && acm_wb_is_avail(acm)) ? acm_wb_is_avail(acm)*acm->writesize : 0;
}
int usb_serial_highspeed_chars_in_buffer (struct usb_serial_port *port)
{
struct acm * acm =usb_get_serial_port_data(port);
dbg("%s - port %d", __FUNCTION__, port->number);
if (!PORT_READY(acm))
return -EINVAL;
/*
* This is inaccurate (overcounts), but it works.
*/
return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
}
static char test[64];
static int testcount= 0;
static int recvcount= 0;
void usb_serial_highspeed_rx_tasklet(unsigned long _acm)
{
struct acm *acm = (void *)_acm;
struct acm_rb *buf;
struct tty_struct *tty = acm->tty;
struct acm_ru *rcv;
unsigned long flags;
unsigned char throttled;
static int zcount = 0;
static int ncount= 0;
dbg("Entering usb_serial_highspeed_rx_tasklet");
if (!PORT_READY(acm)){
pr_err("usb_serial_highspeed_rx_tasklet : PORT_READY =0 \n");
return;
}
spin_lock_irqsave(&acm->throttle_lock, flags);
throttled = acm->throttle;
spin_unlock_irqrestore(&acm->throttle_lock, flags);
if (throttled){
pr_err("usb_serial_highspeed_rx_tasklet : throttled\n");
return;
}
next_buffer:
spin_lock_irqsave(&acm->read_lock, flags);
if (list_empty(&acm->filled_read_bufs)) {
spin_unlock_irqrestore(&acm->read_lock, flags);
goto urbs;
}
buf = list_entry(acm->filled_read_bufs.next,
struct acm_rb, list);
list_del(&buf->list);
spin_unlock_irqrestore(&acm->read_lock, flags);
dbg("usb_serial_highspeed_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size);
// if(buf->size &&(ncount || (buf->base[0] == 1))){
if(buf->size ){
test[ncount] = buf->base[0];
ncount++;
zcount = 0;
if(ncount == 64) ncount = 0;
tty_buffer_request_room(tty, buf->size);
spin_lock_irqsave(&acm->throttle_lock, flags);
throttled = acm->throttle;
spin_unlock_irqrestore(&acm->throttle_lock, flags);
if (!throttled){
if(buf->size > tty_insert_flip_string(tty, buf->base, buf->size))
pr_err("\n===== tty_insert_flip_string ====\n");;
recvcount++;
}
tty_flip_buffer_push(tty);
if (throttled) {
dbg("Throttling noticed");
spin_lock_irqsave(&acm->read_lock, flags);
list_add(&buf->list, &acm->filled_read_bufs);
spin_unlock_irqrestore(&acm->read_lock, flags);
pr_err("usb_serial_highspeed_rx_tasklet : throttled 2!!\n");
return;
}
}
else{
zcount++;
if(ncount && ncount != 32){
#ifdef NEVER
int i;
printk("\nncount : %d\n", ncount);
for( i = 0; i <ncount; i++)
printk("ncount - test: %02x\n", test[i]);
#endif /* NEVER */
ncount = 0;
}
if(zcount >1)
pr_err("zcount : %d\n",zcount);
ncount = 0;
}
spin_lock_irqsave(&acm->read_lock, flags);
list_add(&buf->list, &acm->spare_read_bufs);
spin_unlock_irqrestore(&acm->read_lock, flags);
goto next_buffer;
urbs:
while (!list_empty(&acm->spare_read_bufs)) {
spin_lock_irqsave(&acm->read_lock, flags);
if (list_empty(&acm->spare_read_urbs)) {
spin_unlock_irqrestore(&acm->read_lock, flags);
return;
}
if(acm->skip){
acm->skip = 0;
spin_unlock_irqrestore(&acm->read_lock, flags);
pr_err("usb_serial_highspeed_rx_tasklet : skip \n");
return;
}
rcv = list_entry(acm->spare_read_urbs.next,
struct acm_ru, list);
list_del(&rcv->list);
//spin_unlock_irqrestore(&acm->read_lock, flags);
buf = list_entry(acm->spare_read_bufs.next,
struct acm_rb, list);
list_del(&buf->list);
testcount ++;
spin_unlock_irqrestore(&acm->read_lock, flags);
rcv->buffer = buf;
usb_fill_bulk_urb(rcv->urb, acm->dev,
acm->rx_endpoint,
buf->base,
acm->readsize,
usb_serial_highspeed_read_bulk_callback, rcv);
rcv->urb->transfer_dma = buf->dma;
rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
dbg("usb_serial_highspeed_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf);
/* This shouldn't kill the driver as unsuccessful URBs are returned to the
free-urbs-pool and resubmited ASAP */
if (usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
spin_lock_irqsave(&acm->read_lock, flags);
list_add(&buf->list, &acm->spare_read_bufs);
list_add(&rcv->list, &acm->spare_read_urbs);
testcount --;
spin_unlock_irqrestore(&acm->read_lock, flags);
pr_err("usb_serial_highspeed_rx_tasklet : usb_submit_urb failure \n");
return;
}
}
}
/* data interface returns incoming bytes, or we got unthrottled */
static void usb_serial_highspeed_read_bulk_callback(struct urb *urb)
{
struct acm_rb *buf;
struct acm_ru *rcv = urb->context;
struct acm *acm = rcv->instance;
int status = urb->status;
dbg("usb_serial_highspeed_read_bulk_callback with status %d", status);
if (!PORT_READY(acm))
return;
if (status)
dev_dbg(&acm->data->dev, "bulk rx status %d\n", status);
#ifdef CONFIG_ANDROID_POWER
// userial_auto_suspend();
userial_auto_lock_suspend();
#endif
buf = rcv->buffer;
buf->size = urb->actual_length;
// if (likely(status == 0)||unlikely(buf->size == 0)) {
if (likely(status == 0)) {
spin_lock(&acm->read_lock);
list_add_tail(&rcv->list, &acm->spare_read_urbs);
list_add_tail(&buf->list, &acm->filled_read_bufs);
testcount --;
spin_unlock(&acm->read_lock);
tasklet_schedule(&acm->urb_task);
} else {
/* we drop the buffer due to an error */
spin_lock(&acm->read_lock);
list_add_tail(&rcv->list, &acm->spare_read_urbs);
list_add(&buf->list, &acm->spare_read_bufs);
testcount --;
spin_unlock(&acm->read_lock);
/* nevertheless the tasklet must be kicked unconditionally
so the queue cannot dry up */
acm->skip = 1;
}
}
int usb_serial_highspeed_usb_count(void)
{
pr_err("%s - testcount %d recvcount%d \n", __FUNCTION__, testcount, recvcount);
return testcount;
}
EXPORT_SYMBOL_GPL(usb_serial_highspeed_usb_count);
void usb_serial_highspeed_throttle (struct usb_serial_port *port)
{
unsigned long flags;
struct acm * acm =usb_get_serial_port_data(port);
//dbg("%s - port %d", __FUNCTION__, port->number);
pr_err("%s - port %d", __FUNCTION__, port->number);
/* Set the throttle request flag. It will be picked up
* by usb_serial_highspeed_read_bulk_callback(). */
spin_lock_irqsave(&port->lock, flags);
acm->throttle = 1;
spin_unlock_irqrestore(&port->lock, flags);
}
void usb_serial_highspeed_unthrottle (struct usb_serial_port *port)
{
int was_throttled;
unsigned long flags;
struct acm * acm =usb_get_serial_port_data(port);
//dbg("%s - port %d", __FUNCTION__, port->number);
pr_err("%s - port %d\n", __FUNCTION__, port->number);
/* Clear the throttle flags */
spin_lock_irqsave(&port->lock, flags);
was_throttled = acm->throttle;
acm->throttle = 0;
spin_unlock_irqrestore(&port->lock, flags);
if (was_throttled) {
/* Resume reading from device */
acm->skip = 0;
tasklet_schedule(&acm->urb_task);
}
}
void usb_serial_highspeed_shutdown (struct usb_serial *serial)
{
int i;
dbg("%s\n", __FUNCTION__);
/* stop reads and writes on all ports */
for (i=0; i < serial->num_ports; ++i) {
usb_serial_highspeed_cleanup(serial->port[i]);
}
}
int usb_serial_highspeed_probe(struct usb_serial_port *port)
{
struct usb_interface *interface = port->serial->interface;
struct usb_device *dev = interface_to_usbdev (interface);
int i;
int num_bulk_in = 1;
int num_bulk_out = 1;
int num_rx_buf = ACM_NR;
struct acm* acm = usb_get_serial_port_data (port );
pr_err("%s: port data -- %d\n", __FUNCTION__, (int)acm);
if(acm){
pr_err("\n error !usb_serial_highspeed_probe call exception!!\n ");
return 0;
}
lock_kernel(); /* guard against unloading a serial driver module */
acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
if (!port){
unlock_kernel();
pr_err("\n error ! no memory !!\n ");
goto probe_error;
}
/* set up the endpoint information */
for (i = 0; i < num_bulk_in; ++i) {
int readsize;
readsize = port->bulk_in_size;
acm->data = interface;
acm->dev = dev;
acm->readsize = readsize;
acm->rx_buflimit = num_rx_buf;
acm->urb_task.func = usb_serial_highspeed_rx_tasklet;
acm->urb_task.data = (unsigned long) acm;
spin_lock_init(&acm->throttle_lock);
spin_lock_init(&acm->read_lock);
mutex_init(&acm->mutex);
acm->rx_endpoint = usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress);
for (i = 0; i < num_rx_buf; i++) {
struct acm_ru *rcv = &(acm->ru[i]);
if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) {
dev_dbg(&interface->dev, "out of memory (read urbs usb_alloc_urb)\n");
unlock_kernel();
goto alloc_fail7;
}
rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
rcv->instance = acm;
}
for (i = 0; i < num_rx_buf; i++) {
struct acm_rb *buf = &(acm->rb[i]);
if (!(buf->base = usb_buffer_alloc(acm->dev, readsize, GFP_KERNEL, &buf->dma))) {
dev_dbg(&interface->dev, "out of memory (read bufs usb_buffer_alloc)\n");
unlock_kernel();
goto alloc_fail7;
}
}
}
for (i = 0; i < num_bulk_out; ++i) {
acm->writesize = le16_to_cpu(port->bulk_out_size);
INIT_WORK(&acm->work, acm_softint);
spin_lock_init(&acm->write_lock);
acm->write_ready = 1;
for (i = 0; i < ACM_NW; i++) {
struct acm_ru *wu = &(acm->wu[i]);
if (!(wu->urb = usb_alloc_urb(0, GFP_KERNEL))) {
dev_dbg(&interface->dev, "out of memory (read urbs usb_alloc_urb)\n");
unlock_kernel();
goto alloc_fail7;
}
usb_fill_bulk_urb(wu->urb, dev, usb_sndbulkpipe(dev, port->bulk_out_endpointAddress),
NULL, acm->writesize, acm_write_bulk, acm);
wu->urb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP;
wu->instance = acm;
}
for (i = 0; i < ACM_NW; i++) {
struct acm_rb *rbuf = &(acm->wb[i]);
if (!(rbuf->base = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL, &rbuf->dma))) {
dev_dbg(&interface->dev, "out of memory (read bufs usb_buffer_alloc)\n");
unlock_kernel();
goto alloc_fail8;
}
}
}
unlock_kernel();
/* success */
usb_set_serial_port_data (port, acm);
return 0;
alloc_fail8:
for (i = 0; i < ACM_NW; i++) {
struct acm_ru *wu = &(acm->wu[i]);
usb_free_urb(wu->urb);
}
acm_write_buffers_free(acm);
alloc_fail7:
for (i = 0; i < num_rx_buf; i++)
usb_buffer_free(acm->dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
for (i = 0; i < num_rx_buf; i++)
usb_free_urb(acm->ru[i].urb);
probe_error:
return -EIO;
}
int usb_serial_highspeed_disconnect(struct usb_serial_port *port)
{
int i;
int num_rx_buf = ACM_NR;
struct acm* acm = usb_get_serial_port_data(port);
pr_err("%s\n", __FUNCTION__);
lock_kernel(); /* guard against unloading a serial driver module */
for (i = 0; i < ACM_NW; i++) {
struct acm_ru *wu = &(acm->wu[i]);
usb_free_urb(wu->urb);
}
for (i = 0; i < num_rx_buf; i++)
usb_buffer_free(acm->dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
for (i = 0; i < num_rx_buf; i++)
usb_free_urb(acm->ru[i].urb);
acm_write_buffers_free(acm);
kfree(acm);
/* success */
usb_set_serial_port_data (port, NULL);
unlock_kernel();
return 0;
}
static struct usb_driver generic_high_driver = {
.name = "usbserial_high",
.probe = generic_probe,
.suspend = generic_suspend,
.resume = generic_resume,
.disconnect = usb_serial_disconnect,
.id_table = hs_serial_ids,
.no_dynamic_id = 1,
};
/* All of the device info needed for the Generic Serial Converter */
struct usb_serial_driver usb_serial_highspeed_device = {
.driver = {
.owner = THIS_MODULE,
.name = "high-speed",
},
.id_table = hs_device_ids,
.usb_driver = &generic_high_driver,
.num_interrupt_in = NUM_DONT_CARE,
.num_bulk_in = NUM_DONT_CARE,
.num_bulk_out = NUM_DONT_CARE,
.num_ports = 1,
.port_probe = usb_serial_highspeed_probe,
.port_remove = usb_serial_highspeed_disconnect,
.open = usb_serial_highspeed_open,
.write = usb_serial_highspeed_write,
.close = usb_serial_highspeed_close,
.write_room = usb_serial_highspeed_write_room,
.chars_in_buffer = usb_serial_highspeed_chars_in_buffer,
.shutdown = usb_serial_highspeed_shutdown,
.throttle = usb_serial_highspeed_throttle,
.unthrottle = usb_serial_highspeed_unthrottle,
.suspend = usb_serial_highspeed_suspend,
.resume = usb_serial_highspeed_resume,
};
static int __init usb_serial_highspeed_init(void)
{
int retval = 0;
hs_device_ids[0].idVendor = vendor;
hs_device_ids[0].idProduct = product;
hs_device_ids[0].match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT;
/* register our generic driver with ourselves */
retval = usb_serial_register (&usb_serial_highspeed_device);
if (retval)
goto exit;
retval = usb_register(&generic_high_driver);
if (retval)
usb_serial_deregister(&usb_serial_highspeed_device);
exit:
info(DRIVER_DESC " " DRIVER_VERSION);
return retval;
}
static void __exit usb_serial_highspeed_exit(void)
{
usb_deregister(&generic_high_driver);
usb_serial_deregister (&usb_serial_highspeed_device);
}
module_init(usb_serial_highspeed_init);
module_exit(usb_serial_highspeed_exit);
/* Module information */
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_LICENSE("GPL");
本文标签:
版权声明:本文标题:2021-01-22 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dianzi/1727896380a1136840.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论