Prev: [PATCH] rcu: more careful check for the last non-dyntick-idle CPU
Next: [PATCH -v10 00/29] use lmb with x86
From: Pavan Savoy on 1 Apr 2010 18:50 --- On Thu, 1/4/10, Pavan Savoy <pavan_savoy(a)ti.com> wrote: > From: Pavan Savoy <pavan_savoy(a)ti.com> > Subject: Re: [PATCH] drivers:staging: sources for ST core > To: "Alan Cox" <alan(a)lxorguk.ukuu.org.uk> > Cc: "Greg KH" <gregkh(a)suse.de>, "Marcel Holtmann" <marcel(a)holtmann.org>, linux-kernel(a)vger.kernel.org > Date: Thursday, 1 April, 2010, 10:50 PM > --- On Thu, 1/4/10, Alan Cox <alan(a)lxorguk.ukuu.org.uk> > wrote: > > > From: Alan Cox <alan(a)lxorguk.ukuu.org.uk> > > Subject: Re: [PATCH] drivers:staging: sources for ST > core > > To: pavan_savoy(a)ti.com > > Cc: "Greg KH" <gregkh(a)suse.de>, > "Marcel Holtmann" <marcel(a)holtmann.org>, > linux-kernel(a)vger.kernel.org > > Date: Thursday, 1 April, 2010, 2:50 PM > > > +/* > > > + * function to return whether the firmware > response > > was proper > > > + * in case of error don't complete so that > waiting > > for proper > > > + * response times out > > > + */ > > > +void validate_firmware_response(struct sk_buff > *skb) > > > +{ > > > +��� if (unlikely(skb->data[5] != > > 0)) { > > > +��� ��� pr_err("no > > proper response during fw download"); > > > +��� ��� pr_err("data6 > > %x", skb->data[5]); > > > > In this driver you do know the device so you need to > be > > using dev_ and > > passing around dev (or something that gives you dev). > > > > > +static int kim_probe(struct platform_device > *pdev) > > > +{ > > > +��� long status; > > > +��� long proto; > > > +��� long *gpios = > > pdev->dev.platform_data; > > > + > > > +��� status = > > st_core_init(&kim_gdata->core_data); > > > > I would expect any truely global data to be configured > in > > the module init > > and then device specific data you want to do something > like > > this > > > > ��� kim_data = kzalloc(sizeof(something), > > GFP_KERNEL); > > > > ��� .. > > > > ��� kim_data_init(&pdev->dev, > > kim_data); > > ��� dev_set_drvdata(&pdev->dev, > > kim_data); > > > > Elsewhere you can now do > > > > ��� kim_data = > > dev_get_drvdata(&pdev->dev); > > > > to get it back > > There are 2 sets of data structures here (after removing > the un-necessary 3rd one), > 1. st_gdata - which I would want to tie to tty > 2. kim_gdata - which I "would" like to tie to the pdev. > > Now the problem being, I have reference of st_gdata in > kim_gdata, and there are about 4 functions in the st_core > where I would need the st_gdata, as in, > > EXPORTED symbol st_register - because I need to add in > entries as to who registered, > +long st_register(struct st_proto_s *new_proto) > +{ > +��� struct st_data_s��� *st_gdata; > +��� long err = ST_SUCCESS; > +��� unsigned long flags = 0; > + > +��� st_kim_ref(&st_gdata); > +��� pr_info("%s(%d) ", __func__, new_proto->type); > +��� if (st_gdata == NULL || new_proto == NULL || > new_proto->recv == NULL > +��� � � || new_proto->reg_complete_cb == NULL) { > +��� ��� pr_err("gdata/new_proto/recv or > reg_complete_cb not ready"); > +��� ��� return ST_ERR_FAILURE; > +��� } > Also in st_unregister and st_write for similar purposes, > But I also need it in tty_open, to link it to the > disc_data, > > +static int st_tty_open(struct tty_struct *tty) > +{ > +��� int err = ST_SUCCESS; > +��� struct st_data_s *st_gdata; > +��� pr_info("%s ", __func__); > + > +��� st_kim_ref(&st_gdata); > +��� st_gdata->tty = tty; > +��� tty->disc_data = st_gdata; > > So, shouldn't some function like st_kim_ref be enough ? > > +void st_kim_ref(struct st_data_s **core_data) > +{ > +��� *core_data = kim_gdata->core_data; > +} > > So Now st_gdata is tied to tty, and kim_gdata being purely > global, as in only 1 platform device of such kind can > exist. > > Suppose 2 platform devices want to exist - then who's > EXPORT of st_register is considered ? > And If bluetooth or FM wants to use this transport then, > how would it tell onto which platform device it wants to > attach to ? > > Why should I tie kim_gdata to a pdev ? Well, because of the comments on the architecture of this driver, I tried out the bus_ driver method, where I register a new bus type as ST (which also registers the N_TI_WL line discipline) and the different protocols on it, are registered to as devices with the N_TI_WL as bus type. However, I ended up in some similar mess there on too, where during an st_register when I need to register devices to the bus, status-es like how many devices currently registered, and allowing other devices to register during firmware download all would then on require a data which isn't bound to any device ... So any ideas ? What should be done ? Find below the test patch... --- drivers/staging/ti-st/bus_drv/bt_test_bus.c | 55 +++++++++++ drivers/staging/ti-st/bus_drv/fm_test_bus.c | 55 +++++++++++ drivers/staging/ti-st/bus_drv/test_bus.c | 134 +++++++++++++++++++++++++++ 3 files changed, 244 insertions(+), 0 deletions(-) create mode 100644 drivers/staging/ti-st/bus_drv/bt_test_bus.c create mode 100644 drivers/staging/ti-st/bus_drv/fm_test_bus.c create mode 100644 drivers/staging/ti-st/bus_drv/test_bus.c diff --git a/drivers/staging/ti-st/bus_drv/bt_test_bus.c b/drivers/staging/ti-st/bus_drv/bt_test_bus.c new file mode 100644 index 0000000..02ae812 --- /dev/null +++ b/drivers/staging/ti-st/bus_drv/bt_test_bus.c @@ -0,0 +1,55 @@ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/device.h> + +#include "st_bus.h" + +static int __init st_user_probe(struct device *dev, int cur_bus, int cur_slot) +{ + pr_info("%s\n", __func__); + return 0; +} +static int __exit st_user_remove(struct device *dev, int cur_bus, int cur_slot) +{ + pr_info("%s\n", __func__); + return 0; +} + +void bt_release(struct device *dev) +{ + pr_info("%s\n", __func__); +} + +static struct st_driver st_user_driver = { + .dev = { + .release = bt_release, + }, + .id = ST_BT, + .name = "bluetooth", +}; + +static int __init st_user_init(void) +{ + int retval; + retval = st_register(&st_user_driver); + if (retval != 0) + pr_err("%s: error registering driver\n", __func__); + + return retval; + +} + +static void __exit st_user_exit(void) +{ + pr_info("%s\n", __func__); + st_unregister(&st_user_driver); +} + + + +MODULE_LICENSE("GPL"); + +module_init(st_user_init); +module_exit(st_user_exit); + diff --git a/drivers/staging/ti-st/bus_drv/fm_test_bus.c b/drivers/staging/ti-st/bus_drv/fm_test_bus.c new file mode 100644 index 0000000..75d9980 --- /dev/null +++ b/drivers/staging/ti-st/bus_drv/fm_test_bus.c @@ -0,0 +1,55 @@ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/device.h> + +#include "st_bus.h" + +static int __init st_user_probe(struct device *dev, int cur_bus, int cur_slot) +{ + pr_info("%s\n", __func__); + return 0; +} +static int __exit st_user_remove(struct device *dev, int cur_bus, int cur_slot) +{ + pr_info("%s\n", __func__); + return 0; +} + +void fm_release(struct device *dev) +{ + pr_info("%s\n", __func__); +} + +static struct st_driver st_user_driver = { + .dev = { + .release = fm_release, + }, + .id = ST_FM, + .name = "fm", +}; + +static int __init st_user_init(void) +{ + int retval; + retval = st_register(&st_user_driver); + if (retval != 0) + pr_err("%s: error registering driver\n", __func__); + + return retval; + +} + +static void __exit st_user_exit(void) +{ + pr_info("%s\n", __func__); + st_unregister(&st_user_driver); +} + + + +MODULE_LICENSE("GPL"); + +module_init(st_user_init); +module_exit(st_user_exit); + diff --git a/drivers/staging/ti-st/bus_drv/test_bus.c b/drivers/staging/ti-st/bus_drv/test_bus.c new file mode 100644 index 0000000..87aaa94 --- /dev/null +++ b/drivers/staging/ti-st/bus_drv/test_bus.c @@ -0,0 +1,134 @@ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/platform_device.h> + +#include "st_bus.h" +#include "st_core.h" + +struct bus_type st_bus_type; + +static int check_if_registered(struct device *dev, void *data) +{ + struct st_driver *drv; + + drv = dev_get_drvdata(dev); + pr_info("device is %s, type %d\n", drv->name, drv->id); + return 1; +} + +int st_register(struct st_driver *drv) +{ + int retval; + struct device *dev = &drv->dev; + + if (bus_find_device_by_name(&st_bus_type, NULL, + drv->name) != NULL) { + pr_err("already registered\n"); + } + + retval = bus_for_each_dev(&st_bus_type, NULL, NULL, + check_if_registered); + if (retval != 1) { /* as returned by check_if_registered */ + /* download_firmware here */ + pr_info("downloading firmware..\n"); + /* HOWTO carry on with device_register ? */ + } This is 1 place, I would be totally lost because, I should return the flow back to the driver which called the st_register, and allow other driver to do an st_register and return them as pending - and when firmware download completes (takes around 10secs - worst case), I then have to notify all the drivers(devices) registered about the completion of firmware download. + dev->bus = &(st_bus_type); + dev_set_name(dev, drv->name); + dev_set_drvdata(dev, drv); + + /* add device here */ + retval = device_register(dev); + if (retval) { + pr_err("device register failed\n"); + return -1; + } + + pr_info("%s: %s\n", __func__, drv->name); + return 0; +} +EXPORT_SYMBOL(st_register); + +void st_unregister(struct st_driver *drv) +{ + pr_info("%s\n", __func__); + device_unregister(&drv->dev); +} +EXPORT_SYMBOL(st_unregister); + + +static int st_bus_probe(struct platform_device *pdev) +{ + struct st_data_s *core_data; + pr_info("%s\n", __func__); + bus_register(&st_bus_type); + + st_core_init(&core_data); + dev_set_drvdata(&pdev->dev, core_data); + return 0; +} + +static int st_bus_remove(struct platform_device *pdev) +{ + struct st_data_s *core_data; + pr_info("%s\n", __func__); + + core_data = dev_get_drvdata(&pdev->dev); + st_core_exit(core_data); + bus_unregister(&st_bus_type); + return 0; +} + +#if 0 +static int st_bus_match(struct device *dev, struct device_driver *drv) +{ + return 1; +} + +int st_notify(struct notifier_block *notify, unsigned long a, void *b) +{ + pr_info("%s \n", __func__); + return 0; +} +#endif + +struct bus_type st_bus_type = { + .name = "ti-st", +}; +EXPORT_SYMBOL(st_bus_type); + +struct platform_driver st_bus_driver = { + .driver = { + .name = "kim", + .owner = THIS_MODULE, + }, + .probe = st_bus_probe, + .remove = st_bus_remove, +}; + +#if 0 +struct notifier_block st_bus_notify = { + .notifier_call = st_notify, +}; +#endif + +static int __init test_bus_init(void) +{ + platform_driver_register(&st_bus_driver); + return 0; +} + +static void __exit test_bus_exit(void) +{ + pr_info("%s\n", __func__); + platform_driver_unregister(&st_bus_driver); +} + +module_init(test_bus_init); +module_exit(test_bus_exit); +MODULE_AUTHOR("Pavan Savoy <pavan_savoy(a)ti.com>"); +MODULE_DESCRIPTION("Shared Transport Driver for TI BT/FM/GPS combo chips "); +MODULE_LICENSE("GPL"); -- 1.5.4.3 > > -- > > To unsubscribe from this list: send the line > "unsubscribe > > linux-kernel" in > > the body of a message to majordomo(a)vger.kernel.org > > More majordomo info at� http://vger.kernel.org/majordomo-info.html > > Please read the FAQ at� http://www.tux.org/lkml/ > > > > > > � � � The INTERNET now has a personality. > YOURS! See your Yahoo! Homepage. http://in.yahoo.com/ > -- > To unsubscribe from this list: send the line "unsubscribe > linux-kernel" in > the body of a message to majordomo(a)vger.kernel.org > More majordomo info at� http://vger.kernel.org/majordomo-info.html > Please read the FAQ at� http://www.tux.org/lkml/ > Your Mail works best with the New Yahoo Optimized IE8. Get it NOW! http://downloads.yahoo.com/in/internetexplorer/ -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo(a)vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
From: Alan Cox on 1 Apr 2010 19:30 Sorry but I can't make head or tail of this and the code flow you are trying to achieve. The usual way you do stuff is to put per device stuff in a per device struct, driver wide stuff in a driver struct (or static variables) and then run everything from the device end. I'd expect a low level driver to do something like probe() create device entry initialise device (eg download firmware) register itself with anything higher level -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo(a)vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
From: Pavan Savoy on 5 Apr 2010 12:20 --- On Fri, 2/4/10, Alan Cox <alan(a)lxorguk.ukuu.org.uk> wrote: > From: Alan Cox <alan(a)lxorguk.ukuu.org.uk> > Subject: Re: [PATCH] drivers:staging: sources for ST core > To: "Pavan Savoy" <pavan_savoy(a)yahoo.co.in> > Cc: "Greg KH" <gregkh(a)suse.de>, "Marcel Holtmann" <marcel(a)holtmann.org>, linux-kernel(a)vger.kernel.org > Date: Friday, 2 April, 2010, 4:57 AM > Sorry but I can't make head or tail > of this and the code flow you are > trying to achieve. > > The usual way you do stuff is to put per device stuff in a > per device > struct, driver wide stuff in a driver struct (or static > variables) and > then run everything from the device end. > > I'd expect a low level driver to do something like > > > ��� probe() > ��� ��� create device entry > ��� ��� initialise device (eg > download firmware) > ��� ��� register itself with > anything higher level > What I am trying to achieve is something like this, HCI-core V4L2-radio Char-device=/dev/tigps for fops ^ ^ ^ | | | | | | BT FM GPS [these register themselves to ST] \ | / \ | / \ | / Shared Transport Ldisc driver | TTY Layer <-- UART driver has already registered. So, when a BT device try and registers itself to ST (shared transport) driver, I need to toggle chip enable line and 'download_firmware', and in case another _register from FM or GPS happens at the same time, I need to signal it pending, and call a callback upon completion of firmware download. Now which are to be identified as per-device or bus or driver ? Because when an st_register is called, I need to do some tty_* operations from the ldisc driver itself - i.e I cannot embed tty into any of BT, FM or GPS per-device structures. I also expose a st_write function, where in any of BT, FM and GPS driver upon being ready (fw download completed) sends across an SKB, which I queue up and write. All of what I wanted to do, could not be done when I tried ST ldisc as a bus and each of BT, FM and GPS as per-device structs registering with type ST as bus. So again, Now which are to be identified as per-device or bus or driver ? The INTERNET now has a personality. YOURS! See your Yahoo! Homepage. http://in.yahoo.com/ -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo(a)vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
From: Alan Cox on 13 Apr 2010 11:10 > Any comments on these patches ? Still on my TODO list, busy trying to beat my own employers code into sanity for submission right now ;) Alan -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo(a)vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
From: Pavan Savoy on 13 Apr 2010 11:10 Alan, --- On Thu, 8/4/10, pavan_savoy(a)ti.com <pavan_savoy(a)ti.com> wrote: > From: pavan_savoy(a)ti.com <pavan_savoy(a)ti.com> > Subject: [PATCH] drivers:staging: sources for ST core > To: gregkh(a)suse.de, alan(a)lxorguk.ukuu.org.uk > Cc: linux-kernel(a)vger.kernel.org, npelly(a)google.com, pavan_savoy(a)yahoo.co.in, "Pavan Savoy" <pavan_savoy(a)ti.com> > Date: Thursday, 8 April, 2010, 11:46 PM > From: Pavan Savoy <pavan_savoy(a)ti.com> > > Texas Instruments BT, FM and GPS combo chips/drivers > make use of a single TTY to communicate with the chip. > This module constitutes the core logic, TTY ldisc driver > and the exported symbols for registering/unregistering of > the protocol drivers such as BT/FM/GPS. > > Signed-off-by: Pavan Savoy <pavan_savoy(a)ti.com> > --- > drivers/staging/ti-st/st_core.c | 1062 > +++++++++++++++++++++++++++++++++++++++ > drivers/staging/ti-st/st_core.h |���98 > ++++ > 2 files changed, 1160 insertions(+), 0 deletions(-) > create mode 100644 drivers/staging/ti-st/st_core.c > create mode 100644 drivers/staging/ti-st/st_core.h > > diff --git a/drivers/staging/ti-st/st_core.c > b/drivers/staging/ti-st/st_core.c > new file mode 100644 > index 0000000..4e93694 > --- /dev/null > +++ b/drivers/staging/ti-st/st_core.c > @@ -0,0 +1,1062 @@ > +/* > + *� Shared Transport Line discipline driver Core > + *��� This hooks up ST KIM driver and ST LL > driver > + *� Copyright (C) 2009 Texas Instruments > + * > + *� 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. > + * > + *� This program is distributed in the hope that it > will be useful, > + *� but WITHOUT ANY WARRANTY; without even the > implied warranty of > + *� MERCHANTABILITY or FITNESS FOR A PARTICULAR > PURPOSE.� See the > + *� GNU General Public License for more details. > + * > + *� You should have received a copy of the GNU > General Public License > + *� along with this program; if not, write to the > Free Software > + *� Foundation, Inc., 59 Temple Place, Suite 330, > Boston, MA� 02111-1307� USA > + * > + */ > + > +#define pr_fmt(fmt)��� "(stc): " fmt > +#include <linux/module.h> > +#include <linux/kernel.h> > +#include <linux/init.h> > +#include <linux/tty.h> > + > +/* understand BT, FM and GPS for now */ > +#include <net/bluetooth/bluetooth.h> > +#include <net/bluetooth/hci_core.h> > +#include <net/bluetooth/hci.h> > +#include "fm.h" > +/* > + * packet formats for fm and gps > + * #include "gps.h" > + */ > +#include "st_core.h" > +#include "st_kim.h" > +#include "st_ll.h" > +#include "st.h" > + > +#ifdef DEBUG > +/* strings to be used for rfkill entries and by > + * ST Core to be used for sysfs debug entry > + */ > +#define PROTO_ENTRY(type, name)��� name > +const unsigned char *protocol_strngs[] = { > +��� PROTO_ENTRY(ST_BT, "Bluetooth"), > +��� PROTO_ENTRY(ST_FM, "FM"), > +��� PROTO_ENTRY(ST_GPS, "GPS"), > +}; > +#endif > +/* function pointer pointing to either, > + * st_kim_recv during registration to receive fw download > responses > + * st_int_recv after registration to receive proto stack > responses > + */ > +void (*st_recv) (void*, const unsigned char*, long); > + > +/********************************************************************/ > +#if 0 > +/* internal misc functions */ > +bool is_protocol_list_empty(void) > +{ > +��� unsigned char i = 0; > +��� pr_info(" %s ", __func__); > +��� for (i = 0; i < ST_MAX; i++) { > +��� ��� if > (st_gdata->list[i] != NULL) > +��� ��� ��� > return ST_NOTEMPTY; > +��� ��� /* not empty */ > +��� } > +��� /* list empty */ > +��� return ST_EMPTY; > +} > +#endif > +/* can be called in from > + * -- KIM (during fw download) > + * -- ST Core (during st_write) > + * > + *� This is the internal write function - a wrapper > + *� to tty->ops->write > + */ > +int st_int_write(struct st_data_s *st_gdata, > +��� const unsigned char *data, int count) > +{ > +#ifdef VERBOSE��� ��� > ��� /* for debug */ > +��� int i; > +#endif > +��� struct tty_struct *tty; > +��� if (unlikely(st_gdata == NULL || > st_gdata->tty == NULL)) { > +��� ��� pr_err("tty > unavailable to perform write"); > +��� ��� return > ST_ERR_FAILURE; > +��� } > +��� tty = st_gdata->tty; > +#ifdef VERBOSE > +��� printk(KERN_ERR "start data..\n"); > +��� for (i = 0; i < count; > i++)��� /* no newlines for each datum */ > +��� ��� printk(" %x", > data[i]); > +��� printk(KERN_ERR "\n ..end data\n"); > +#endif > +��� return tty->ops->write(tty, data, > count); > + > +} > + > +/* > + * push the skb received to relevant > + * protocol stacks > + */ > +void st_send_frame(enum proto_type protoid, struct > st_data_s *st_gdata) > +{ > +��� pr_info(" %s(prot:%d) ", __func__, > protoid); > + > +��� if (unlikely > +��� � � (st_gdata == NULL || > st_gdata->rx_skb == NULL > +��� � ���|| > st_gdata->list[protoid] == NULL)) { > +��� ��� pr_err("protocol %d > not registered, no data to send?", > +��� ��� ��� > ���protoid); > +��� ��� > kfree_skb(st_gdata->rx_skb); > +��� ��� return; > +��� } > +��� /* this cannot fail > +�����* this shouldn't take long > +�����* - should be just > skb_queue_tail for the > +�����*���protocol > stack driver > +�����*/ > +��� if > (likely(st_gdata->list[protoid]->recv != NULL)) { > +��� ��� if > (unlikely(st_gdata->list[protoid]->recv(st_gdata->rx_skb) > +��� ��� ��� > � ���!= ST_SUCCESS)) { > +��� ��� ��� > pr_err(" proto stack %d's ->recv failed", protoid); > +��� ��� ��� > kfree_skb(st_gdata->rx_skb); > +��� ��� ��� > return; > +��� ��� } > +��� } else { > +��� ��� pr_err(" proto stack > %d's ->recv null", protoid); > +��� ��� > kfree_skb(st_gdata->rx_skb); > +��� } > +��� pr_info(" done %s", __func__); > +��� return; > +} > + > +/* > + * to call registration complete callbacks > + * of all protocol stack drivers > + */ > +void st_reg_complete(struct st_data_s *st_gdata, char > err) > +{ > +��� unsigned char i = 0; > +��� pr_info(" %s ", __func__); > +��� for (i = 0; i < ST_MAX; i++) { > +��� ��� if (likely(st_gdata > != NULL && st_gdata->list[i] != NULL && > +��� ��� ��� > ���st_gdata->list[i]->reg_complete_cb > != NULL)) > +��� ��� ��� > st_gdata->list[i]->reg_complete_cb(err); > +��� } > +} > + > +static inline int st_check_data_len(struct st_data_s > *st_gdata, > +��� int protoid, int len) > +{ > +��� register int room = > skb_tailroom(st_gdata->rx_skb); > + > +��� pr_info("len %d room %d", len, room); > + > +��� if (!len) { > +��� ��� /* Received packet > has only packet header and > +��� �����* has > zero length payload. So, ask ST CORE to > +��� �����* forward > the packet to protocol driver (BT/FM/GPS) > +��� �����*/ > +��� ��� > st_send_frame(protoid, st_gdata); > + > +��� } else if (len > room) { > +��� ��� /* Received packet's > payload length is larger. > +��� �����* We > can't accommodate it in created skb. > +��� �����*/ > +��� ��� pr_err("Data length > is too large len %d room %d", len, > +��� ��� ��� > ���room); > +��� ��� > kfree_skb(st_gdata->rx_skb); > +��� } else { > +��� ��� /* Packet header has > non-zero payload length and > +��� �����* we have > enough space in created skb. Lets read > +��� �����* payload > data */ > +��� ��� > st_gdata->rx_state = ST_BT_W4_DATA; > +��� ��� > st_gdata->rx_count = len; > +��� ��� return len; > +��� } > + > +��� /* Change ST state to continue to > process next > +�����* packet */ > +��� st_gdata->rx_state = > ST_W4_PACKET_TYPE; > +��� st_gdata->rx_skb = NULL; > +��� st_gdata->rx_count = 0; > + > +��� return 0; > +} > + > +/* internal function for action when wake-up ack > + * received > + */ > +static inline void st_wakeup_ack(struct st_data_s > *st_gdata, > +��� unsigned char cmd) > +{ > +��� register struct sk_buff *waiting_skb; > +��� unsigned long flags = 0; > + > +��� > spin_lock_irqsave(&st_gdata->lock, flags); > +��� /* de-Q from waitQ and Q in txQ now > that the > +�����* chip is awake > +�����*/ > +��� while ((waiting_skb = > skb_dequeue(&st_gdata->tx_waitq))) > +��� ��� > skb_queue_tail(&st_gdata->txq, waiting_skb); > + > +��� /* state forwarded to ST LL */ > +��� st_ll_sleep_state(st_gdata, (unsigned > long)cmd); > +��� > spin_unlock_irqrestore(&st_gdata->lock, flags); > + > +��� /* wake up to send the recently copied > skbs from waitQ */ > +��� st_tx_wakeup(st_gdata); > +} > + > +/* Decodes received RAW data and forwards to > corresponding > + * client drivers (Bluetooth,FM,GPS..etc). > + * > + */ > +void st_int_recv(void *disc_data, > +��� const unsigned char *data, long count) > +{ > +��� register char *ptr; > +��� struct hci_event_hdr *eh; > +��� struct hci_acl_hdr *ah; > +��� struct hci_sco_hdr *sh; > +��� struct fm_event_hdr *fm; > +��� struct gps_event_hdr *gps; > +��� register int len = 0, type = 0, dlen = > 0; > +��� static enum proto_type protoid = > ST_MAX; > +��� struct st_data_s *st_gdata = (struct > st_data_s *)disc_data; > + > +��� ptr = (char *)data; > +��� /* tty_receive sent null ? */ > +��� if (unlikely(ptr == NULL) || (st_gdata > == NULL)) { > +��� ��� pr_err(" received > null from TTY "); > +��� ��� return; > +��� } > + > +��� pr_info("count %ld rx_state %ld" > +��� ��� > ���"rx_count %ld", count, > st_gdata->rx_state, > +��� ��� > ���st_gdata->rx_count); > + > +��� /* Decode received bytes here */ > +��� while (count) { > +��� ��� if > (st_gdata->rx_count) { > +��� ��� ��� > len = min_t(unsigned int, st_gdata->rx_count, count); > +��� ��� ��� > memcpy(skb_put(st_gdata->rx_skb, len), ptr, len); > +��� ��� ��� > st_gdata->rx_count -= len; > +��� ��� ��� > count -= len; > +��� ��� ��� > ptr += len; > + > +��� ��� ��� > if (st_gdata->rx_count) > +��� ��� ��� > ��� continue; > + > +��� ��� ��� > /* Check ST RX state machine , where are we? */ > +��� ��� ��� > switch (st_gdata->rx_state) { > + > +��� ��� ��� > ��� /* Waiting for complete packet ? */ > +��� ��� ��� > case ST_BT_W4_DATA: > +��� ��� ��� > ��� pr_info("Complete pkt received"); > + > +��� ��� ��� > ��� /* Ask ST CORE to forward > +��� ��� ��� > �����* the packet to protocol > driver */ > +��� ��� ��� > ��� st_send_frame(protoid, st_gdata); > + > +��� ��� ��� > ��� st_gdata->rx_state = > ST_W4_PACKET_TYPE; > +��� ��� ��� > ��� st_gdata->rx_skb = NULL; > +��� ��� ��� > ��� protoid = ST_MAX;��� /* is > this required ? */ > +��� ��� ��� > ��� continue; > + > +��� ��� ��� > ��� /* Waiting for Bluetooth event header ? > */ > +��� ��� ��� > case ST_BT_W4_EVENT_HDR: > +��� ��� ��� > ��� eh = (struct hci_event_hdr > *)st_gdata->rx_skb-> > +��� ��� ��� > ��� � � data; > + > +��� ��� ��� > ��� pr_info("Event header: evt 0x%2.2x" > +��� ��� ��� > ��� ��� > ���"plen %d", eh->evt, eh->plen); > + > +��� ��� ��� > ��� st_check_data_len(st_gdata, protoid, > eh->plen); > +��� ��� ��� > ��� continue; > + > +��� ��� ��� > ��� /* Waiting for Bluetooth acl header ? */ > +��� ��� ��� > case ST_BT_W4_ACL_HDR: > +��� ��� ��� > ��� ah = (struct hci_acl_hdr > *)st_gdata->rx_skb-> > +��� ��� ��� > ��� � � data; > +��� ��� ��� > ��� dlen = __le16_to_cpu(ah->dlen); > + > +��� ��� ��� > ��� pr_info("ACL header: dlen %d", dlen); > + > +��� ��� ��� > ��� st_check_data_len(st_gdata, protoid, > dlen); > +��� ��� ��� > ��� continue; > + > +��� ��� ��� > ��� /* Waiting for Bluetooth sco header ? */ > +��� ��� ��� > case ST_BT_W4_SCO_HDR: > +��� ��� ��� > ��� sh = (struct hci_sco_hdr > *)st_gdata->rx_skb-> > +��� ��� ��� > ��� � � data; > + > +��� ��� ��� > ��� pr_info("SCO header: dlen %d", > sh->dlen); > + > +��� ��� ��� > ��� st_check_data_len(st_gdata, protoid, > sh->dlen); > +��� ��� ��� > ��� continue; > +��� ��� ��� > case ST_FM_W4_EVENT_HDR: > +��� ��� ��� > ��� fm = (struct fm_event_hdr > *)st_gdata->rx_skb-> > +��� ��� ��� > ��� � � data; > +��� ��� ��� > ��� pr_info("FM Header: "); > +��� ��� ��� > ��� st_check_data_len(st_gdata, ST_FM, > fm->plen); > +��� ��� ��� > ��� continue; > +��� ��� ��� > ��� /* TODO : Add GPS packet machine logic > here */ > +��� ��� ��� > case ST_GPS_W4_EVENT_HDR: > +��� ��� ��� > ��� /* [0x09 pkt hdr][R/W byte][2 byte len] > */ > +��� ��� ��� > ��� gps = (struct gps_event_hdr > *)st_gdata->rx_skb-> > +��� ��� ��� > ��� � ���data; > +��� ��� ��� > ��� pr_info("GPS Header: "); > +��� ��� ��� > ��� st_check_data_len(st_gdata, ST_GPS, > gps->plen); > +��� ��� ��� > ��� continue; > +��� ��� ��� > }��� /* end of switch rx_state */ > +��� ��� } > + > +��� ��� /* end of if > rx_count */ > +��� ��� /* Check first byte > of packet and identify module > +��� �����* owner > (BT/FM/GPS) */ > +��� ��� switch (*ptr) { > + > +��� ��� ��� > /* Bluetooth event packet? */ > +��� ��� case HCI_EVENT_PKT: > +��� ��� ��� > pr_info("Event packet"); > +��� ��� ��� > st_gdata->rx_state = ST_BT_W4_EVENT_HDR; > +��� ��� ��� > st_gdata->rx_count = HCI_EVENT_HDR_SIZE; > +��� ��� ��� > type = HCI_EVENT_PKT; > +��� ��� ��� > protoid = ST_BT; > +��� ��� ��� > break; > + > +��� ��� ��� > /* Bluetooth acl packet? */ > +��� ��� case > HCI_ACLDATA_PKT: > +��� ��� ��� > pr_info("ACL packet"); > +��� ��� ��� > st_gdata->rx_state = ST_BT_W4_ACL_HDR; > +��� ��� ��� > st_gdata->rx_count = HCI_ACL_HDR_SIZE; > +��� ��� ��� > type = HCI_ACLDATA_PKT; > +��� ��� ��� > protoid = ST_BT; > +��� ��� ��� > break; > + > +��� ��� ��� > /* Bluetooth sco packet? */ > +��� ��� case > HCI_SCODATA_PKT: > +��� ��� ��� > pr_info("SCO packet"); > +��� ��� ��� > st_gdata->rx_state = ST_BT_W4_SCO_HDR; > +��� ��� ��� > st_gdata->rx_count = HCI_SCO_HDR_SIZE; > +��� ��� ��� > type = HCI_SCODATA_PKT; > +��� ��� ��� > protoid = ST_BT; > +��� ��� ��� > break; > + > +��� ��� ��� > /* Channel 8(FM) packet? */ > +��� ��� case ST_FM_CH8_PKT: > +��� ��� ��� > pr_info("FM CH8 packet"); > +��� ��� ��� > type = ST_FM_CH8_PKT; > +��� ��� ��� > st_gdata->rx_state = ST_FM_W4_EVENT_HDR; > +��� ��� ��� > st_gdata->rx_count = FM_EVENT_HDR_SIZE; > +��� ��� ��� > protoid = ST_FM; > +��� ��� ��� > break; > + > +��� ��� ��� > /* Channel 9(GPS) packet? */ > +��� ��� case > 0x9:��� /*ST_LL_GPS_CH9_PKT */ > +��� ��� ��� > pr_info("GPS CH9 packet"); > +��� ��� ��� > type = 0x9;��� /* ST_LL_GPS_CH9_PKT; */ > +��� ��� ��� > protoid = ST_GPS; > +��� ��� ��� > st_gdata->rx_state = ST_GPS_W4_EVENT_HDR; > +��� ��� ��� > st_gdata->rx_count = 3;��� /* > GPS_EVENT_HDR_SIZE -1*/ > +��� ��� ��� > break; > +��� ��� case LL_SLEEP_IND: > +��� ��� case LL_SLEEP_ACK: > +��� ��� case > LL_WAKE_UP_IND: > +��� ��� ��� > pr_info("PM packet"); > +��� ��� ��� > /* this takes appropriate action based on > +��� ��� > �����* sleep state received -- > +��� ��� > �����*/ > +��� ��� ��� > st_ll_sleep_state(st_gdata, *ptr); > +��� ��� ��� > ptr++; > +��� ��� ��� > count--; > +��� ��� ��� > continue; > +��� ��� case > LL_WAKE_UP_ACK: > +��� ��� ��� > pr_info("PM packet"); > +��� ��� ��� > /* wake up ack received */ > +��� ��� ��� > st_wakeup_ack(st_gdata, *ptr); > +��� ��� ��� > ptr++; > +��� ��� ��� > count--; > +��� ��� ��� > continue; > +��� ��� ��� > /* Unknow packet? */ > +��� ��� default: > +��� ��� ��� > pr_err("Unknown packet type %2.2x", (__u8) *ptr); > +��� ��� ��� > ptr++; > +��� ��� ��� > count--; > +��� ��� ��� > continue; > +��� ��� }; > +��� ��� ptr++; > +��� ��� count--; > + > +��� ��� switch (protoid) { > +��� ��� case ST_BT: > +��� ��� ��� > /* Allocate new packet to hold received data */ > +��� ��� ��� > st_gdata->rx_skb = > +��� ��� ��� > � � bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); > +��� ��� ��� > if (!st_gdata->rx_skb) { > +��� ��� ��� > ��� pr_err("Can't allocate mem for new > packet"); > +��� ��� ��� > ��� st_gdata->rx_state = > ST_W4_PACKET_TYPE; > +��� ��� ��� > ��� st_gdata->rx_count = 0; > +��� ��� ��� > ��� return; > +��� ��� ��� > } > +��� ��� ��� > bt_cb(st_gdata->rx_skb)->pkt_type = type; > +��� ��� ��� > break; > +��� ��� case > ST_FM:��� /* for FM */ > +��� ��� ��� > st_gdata->rx_skb = > +��� ��� ��� > � � alloc_skb(FM_MAX_FRAME_SIZE, GFP_ATOMIC); > +��� ��� ��� > if (!st_gdata->rx_skb) { > +��� ��� ��� > ��� pr_err("Can't allocate mem for new > packet"); > +��� ��� ��� > ��� st_gdata->rx_state = > ST_W4_PACKET_TYPE; > +��� ��� ��� > ��� st_gdata->rx_count = 0; > +��� ��� ��� > ��� return; > +��� ��� ��� > } > +��� ��� ��� > /* place holder 0x08 */ > +��� ��� ��� > skb_reserve(st_gdata->rx_skb, 1); > +��� ��� ��� > st_gdata->rx_skb->cb[0] = ST_FM_CH8_PKT; > +��� ��� ��� > break; > +��� ��� case ST_GPS: > +��� ��� ��� > /* for GPS */ > +��� ��� ��� > st_gdata->rx_skb = > +��� ��� ��� > � � alloc_skb(100 /*GPS_MAX_FRAME_SIZE */ , > GFP_ATOMIC); > +��� ��� ��� > if (!st_gdata->rx_skb) { > +��� ��� ��� > ��� pr_err("Can't allocate mem for new > packet"); > +��� ��� ��� > ��� st_gdata->rx_state = > ST_W4_PACKET_TYPE; > +��� ��� ��� > ��� st_gdata->rx_count = 0; > +��� ��� ��� > ��� return; > +��� ��� ��� > } > +��� ��� ��� > /* place holder 0x09 */ > +��� ��� ��� > skb_reserve(st_gdata->rx_skb, 1); > +��� ��� ��� > st_gdata->rx_skb->cb[0] = 0x09;��� > /*ST_GPS_CH9_PKT; */ > +��� ��� ��� > break; > +��� ��� case ST_MAX: > +��� ��� ��� > break; > +��� ��� } > +��� } > +��� pr_info("done %s", __func__); > +��� return; > +} > + > +/* internal de-Q function > + * -- return previous in-completely written skb > + *� or return the skb in the txQ > + */ > +struct sk_buff *st_int_dequeue(struct st_data_s > *st_gdata) > +{ > +��� struct sk_buff *returning_skb; > + > +��� pr_info("%s", __func__); > +��� /* if the previous skb wasn't written > completely > +�����*/ > +��� if (st_gdata->tx_skb != NULL) { > +��� ��� returning_skb = > st_gdata->tx_skb; > +��� ��� st_gdata->tx_skb > = NULL; > +��� ��� return > returning_skb; > +��� } > + > +��� /* de-Q from the txQ always if previous > write is complete */ > +��� return > skb_dequeue(&st_gdata->txq); > +} > + > +/* internal Q-ing function > + * will either Q the skb to txq or the tx_waitq > + * depending on the ST LL state > + * > + * lock the whole func - since ll_getstate and Q-ing > should happen > + * in one-shot > + */ > +void st_int_enqueue(struct st_data_s *st_gdata, struct > sk_buff *skb) > +{ > +��� unsigned long flags = 0; > + > +��� pr_info("%s", __func__); > +��� /* this function can be invoked in more > then one context. > +�����* so have a lock */ > +��� > spin_lock_irqsave(&st_gdata->lock, flags); > + > +��� switch (st_ll_getstate(st_gdata)) { > +��� case ST_LL_AWAKE: > +��� ��� pr_info("ST LL is > AWAKE, sending normally"); > +��� ��� > skb_queue_tail(&st_gdata->txq, skb); > +��� ��� break; > +��� case ST_LL_ASLEEP_TO_AWAKE: > +��� ��� > skb_queue_tail(&st_gdata->tx_waitq, skb); > +��� ��� break; > +��� case > ST_LL_AWAKE_TO_ASLEEP:��� /* host cannot be > in this state */ > +��� ��� pr_err("ST LL is > illegal state(%ld)," > +��� ��� ��� > ���"purging received skb.", > st_ll_getstate(st_gdata)); > +��� ��� kfree_skb(skb); > +��� ��� break; > + > +��� case ST_LL_ASLEEP: > +��� ��� /* call a function > of ST LL to put data > +��� �����* in > tx_waitQ and wake_ind in txQ > +��� �����*/ > +��� ��� > skb_queue_tail(&st_gdata->tx_waitq, skb); > +��� ��� > st_ll_wakeup(st_gdata); > +��� ��� break; > +��� default: > +��� ��� pr_err("ST LL is > illegal state(%ld)," > +��� ��� ��� > ���"purging received skb.", > st_ll_getstate(st_gdata)); > +��� ��� kfree_skb(skb); > +��� ��� break; > +��� } > +��� > spin_unlock_irqrestore(&st_gdata->lock, flags); > +��� pr_info("done %s", __func__); > +��� return; > +} > + > +/* > + * internal wakeup function > + * called from either > + * - TTY layer when write's finished > + * - st_write (in context of the protocol stack) > + */ > +void st_tx_wakeup(struct st_data_s *st_data) > +{ > +��� struct sk_buff *skb; > +��� unsigned long flags;��� > /* for irq save flags */ > +��� pr_info("%s", __func__); > +��� /* check for sending & set flag > sending here */ > +��� if (test_and_set_bit(ST_TX_SENDING, > &st_data->tx_state)) { > +��� ��� pr_info("ST already > sending"); > +��� ��� /* keep sending */ > +��� ��� > set_bit(ST_TX_WAKEUP, &st_data->tx_state); > +��� ��� return; > +��� ��� /* TX_WAKEUP will be > checked in another > +��� �����* > context > +��� �����*/ > +��� } > +��� do {��� > ��� ��� /* come back if > st_tx_wakeup is set */ > +��� ��� /* woke-up to write > */ > +��� ��� > clear_bit(ST_TX_WAKEUP, &st_data->tx_state); > +��� ��� while ((skb = > st_int_dequeue(st_data))) { > +��� ��� ��� > int len; > +��� ��� ��� > spin_lock_irqsave(&st_data->lock, flags); > +��� ��� ��� > /* enable wake-up from TTY */ > +��� ��� ��� > set_bit(TTY_DO_WRITE_WAKEUP, > &st_data->tty->flags); > +��� ��� ��� > len = st_int_write(st_data, skb->data, skb->len); > +��� ��� ��� > skb_pull(skb, len); > +��� ��� ��� > /* if skb->len = len as expected, skb->len=0 */ > +��� ��� ��� > if (skb->len) { > +��� ��� ��� > ��� /* would be the next skb to be sent */ > +��� ��� ��� > ��� st_data->tx_skb = skb; > +��� ��� ��� > ��� > spin_unlock_irqrestore(&st_data->lock, flags); > +��� ��� ��� > ��� break; > +��� ��� ��� > } > +��� ��� ��� > kfree_skb(skb); > +��� ��� ��� > spin_unlock_irqrestore(&st_data->lock, flags); > +��� ��� } > +��� ��� /* if wake-up is set > in another context- restart sending */ > +��� } while (test_bit(ST_TX_WAKEUP, > &st_data->tx_state)); > + > +��� /* clear flag sending */ > +��� clear_bit(ST_TX_SENDING, > &st_data->tx_state); > +} > + > +/********************************************************************/ > +/* functions called from ST KIM > +*/ > +void kim_st_list_protocols(struct st_data_s *st_gdata, > char *buf) > +{ > +��� unsigned long flags = 0; > +#ifdef DEBUG > +��� unsigned char i = ST_MAX; > +#endif > +��� > spin_lock_irqsave(&st_gdata->lock, flags); > +#ifdef DEBUG��� ��� > ��� /* more detailed log */ > +��� for (i = 0; i < ST_MAX; i++) { > +��� ��� if (i == 0) { > +��� ��� ��� > sprintf(buf, "%s is %s", protocol_strngs[i], > +��� ��� ��� > ��� st_gdata->list[i] != > +��� ��� ��� > ��� NULL ? "Registered" : "Unregistered"); > +��� ��� } else { > +��� ��� ��� > sprintf(buf, "%s\n%s is %s", buf, protocol_strngs[i], > +��� ��� ��� > ��� st_gdata->list[i] != > +��� ��� ��� > ��� NULL ? "Registered" : "Unregistered"); > +��� ��� } > +��� } > +��� sprintf(buf, "%s\n", buf); > +#else /* limited info */ > +��� sprintf(buf, "BT=%c\nFM=%c\nGPS=%c\n", > +��� ��� > st_gdata->list[ST_BT] != NULL ? 'R' : 'U', > +��� ��� > st_gdata->list[ST_FM] != NULL ? 'R' : 'U', > +��� ��� > st_gdata->list[ST_GPS] != NULL ? 'R' : 'U'); > +#endif > +��� > spin_unlock_irqrestore(&st_gdata->lock, flags); > +} > + > +/********************************************************************/ > +/* > + * functions called from protocol stack drivers > + * to be EXPORT-ed > + */ > +long st_register(struct st_proto_s *new_proto) > +{ > +��� struct st_data_s��� > *st_gdata; > +��� long err = ST_SUCCESS; > +��� unsigned long flags = 0; > + > +��� st_kim_ref(&st_gdata); > +��� pr_info("%s(%d) ", __func__, > new_proto->type); > +��� if (st_gdata == NULL || new_proto == > NULL || new_proto->recv == NULL > +��� � � || > new_proto->reg_complete_cb == NULL) { > +��� ��� > pr_err("gdata/new_proto/recv or reg_complete_cb not > ready"); > +��� ��� return > ST_ERR_FAILURE; > +��� } > + > +��� if (new_proto->type < ST_BT || > new_proto->type >= ST_MAX) { > +��� ��� pr_err("protocol %d > not supported", new_proto->type); > +��� ��� return > ST_ERR_NOPROTO; > +��� } > + > +��� if > (st_gdata->list[new_proto->type] != NULL) { > +��� ��� pr_err("protocol %d > already registered", new_proto->type); > +��� ��� return > ST_ERR_ALREADY; > +��� } > + > +��� /* can be from process context only */ > +��� > spin_lock_irqsave(&st_gdata->lock, flags); > + > +��� if (test_bit(ST_REG_IN_PROGRESS, > &st_gdata->st_state)) { > +��� ��� pr_info(" > ST_REG_IN_PROGRESS:%d ", new_proto->type); > +��� ��� /* fw download in > progress */ > +��� ��� > st_kim_chip_toggle(new_proto->type, KIM_GPIO_ACTIVE); > + > +��� ��� > st_gdata->list[new_proto->type] = new_proto; > +��� ��� new_proto->write > = st_write; > + > +��� ��� > set_bit(ST_REG_PENDING, &st_gdata->st_state); > +��� ��� > spin_unlock_irqrestore(&st_gdata->lock, flags); > +��� ��� return > ST_ERR_PENDING; > +��� } else if > (st_gdata->protos_registered == ST_EMPTY) { > +��� ��� pr_info(" protocol > list empty :%d ", new_proto->type); > +��� ��� > set_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state); > +��� ��� st_recv = > st_kim_recv; > + > +��� ��� /* release lock > previously held - re-locked below */ > +��� ��� > spin_unlock_irqrestore(&st_gdata->lock, flags); > + > +��� ��� /* enable the ST LL > - to set default chip state */ > +��� ��� > st_ll_enable(st_gdata); > +��� ��� /* this may take a > while to complete > +��� �����* since > it involves BT fw download > +��� �����*/ > +��� ��� err = > st_kim_start(); > +��� ��� if (err != > ST_SUCCESS) { > +��� ��� ��� > clear_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state); > +��� ��� ��� > if ((st_gdata->protos_registered != ST_EMPTY) && > +��� ��� ��� > � � (test_bit(ST_REG_PENDING, > &st_gdata->st_state))) { > +��� ��� ��� > ��� pr_err(" KIM failure complete callback > "); > +��� ��� ��� > ��� st_reg_complete(st_gdata, > ST_ERR_FAILURE); > +��� ��� ��� > } > + > +��� ��� ��� > return ST_ERR_FAILURE; > +��� ��� } > + > +��� ��� /* the protocol > might require other gpios to be toggled > +��� �����*/ > +��� ��� > st_kim_chip_toggle(new_proto->type, KIM_GPIO_ACTIVE); > + > +��� ��� > clear_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state); > +��� ��� st_recv = > st_int_recv; > + > +��� ��� /* this is where all > pending registration > +��� �����* are > signalled to be complete by calling callback functions > +��� �����*/ > +��� ��� if > ((st_gdata->protos_registered != ST_EMPTY) && > +��� ��� � � > (test_bit(ST_REG_PENDING, &st_gdata->st_state))) { > +��� ��� ��� > pr_info(" call reg complete callback "); > +��� ��� ��� > st_gdata->protos_registered++; > +��� ��� ��� > st_reg_complete(st_gdata, ST_SUCCESS); > +��� ��� } > +��� ��� > clear_bit(ST_REG_PENDING, &st_gdata->st_state); > + > +��� ��� /* check for already > registered once more, > +��� �����* since > the above check is old > +��� �����*/ > +��� ��� if > (st_gdata->list[new_proto->type] != NULL) { > +��� ��� ��� > pr_err(" proto %d already registered ", > +��� ��� ��� > ��� ���new_proto->type); > +��� ��� ��� > return ST_ERR_ALREADY; > +��� ��� } > + > +��� ��� > spin_lock_irqsave(&st_gdata->lock, flags); > +��� ��� > st_gdata->list[new_proto->type] = new_proto; > +��� ��� new_proto->write > = st_write; > +��� ��� > spin_unlock_irqrestore(&st_gdata->lock, flags); > +��� ��� return err; > +��� } > +��� /* if fw is already downloaded & > new stack registers protocol */ > +��� else { > +��� ��� switch > (new_proto->type) { > +��� ��� case ST_BT: > +��� ��� ��� > /* do nothing */ > +��� ��� ��� > break; > +��� ��� case ST_FM: > +��� ��� case ST_GPS: > +��� ��� ��� > st_kim_chip_toggle(new_proto->type, KIM_GPIO_ACTIVE); > +��� ��� ��� > break; > +��� ��� case ST_MAX: > +��� ��� default: > +��� ��� ��� > pr_err("%d protocol not supported", > +��� ��� ��� > ��� ���new_proto->type); > +��� ��� ��� > err = ST_ERR_NOPROTO; > +��� ��� ��� > /* something wrong */ > +��� ��� ��� > break; > +��� ��� } > +��� ��� > st_gdata->list[new_proto->type] = new_proto; > +��� ��� new_proto->write > = st_write; > + > +��� ��� /* lock already held > before entering else */ > +��� ��� > spin_unlock_irqrestore(&st_gdata->lock, flags); > +��� ��� return err; > +��� } > +��� pr_info("done %s(%d) ", __func__, > new_proto->type); > +} > +EXPORT_SYMBOL_GPL(st_register); > + > +/* to unregister a protocol - > + * to be called from protocol stack driver > + */ > +long st_unregister(enum proto_type type) > +{ > +��� long err = ST_SUCCESS; > +��� unsigned long flags = 0; > +��� struct st_data_s��� > *st_gdata; > + > +��� pr_info("%s: %d ", __func__, type); > + > +��� st_kim_ref(&st_gdata); > +��� if (type < ST_BT || type >= > ST_MAX) { > +��� ��� pr_err(" protocol %d > not supported", type); > +��� ��� return > ST_ERR_NOPROTO; > +��� } > + > +��� > spin_lock_irqsave(&st_gdata->lock, flags); > + > +��� if (st_gdata->list[type] == NULL) { > +��� ��� pr_err(" protocol %d > not registered", type); > +��� ��� > spin_unlock_irqrestore(&st_gdata->lock, flags); > +��� ��� return > ST_ERR_NOPROTO; > +��� } > + > +��� st_gdata->protos_registered--; > +��� st_gdata->list[type] = NULL; > + > +��� /* kim ignores BT in the below > function > +�����* and handles the rest, BT > is toggled > +�����* only in kim_start and > kim_stop > +�����*/ > +��� st_kim_chip_toggle(type, > KIM_GPIO_INACTIVE); > +��� > spin_unlock_irqrestore(&st_gdata->lock, flags); > + > +��� if ((st_gdata->protos_registered == > ST_EMPTY) && > +��� � � > (!test_bit(ST_REG_PENDING, &st_gdata->st_state))) { > +��� ��� pr_info(" all > protocols unregistered "); > + > +��� ��� /* stop traffic on > tty */ > +��� ��� if > (st_gdata->tty) { > +��� ��� ��� > tty_ldisc_flush(st_gdata->tty); > +��� ��� ��� > stop_tty(st_gdata->tty); > +��� ��� } > + > +��� ��� /* all protocols now > unregistered */ > +��� ��� st_kim_stop(); > +��� ��� /* disable ST LL */ > +��� ��� > st_ll_disable(st_gdata); > +��� } > +��� return err; > +} > + > +/* > + * called in protocol stack drivers > + * via the write function pointer > + */ > +long st_write(struct sk_buff *skb) > +{ > +��� struct st_data_s *st_gdata; > +#ifdef DEBUG > +��� enum proto_type protoid = ST_MAX; > +#endif > +��� long len; > + > +��� st_kim_ref(&st_gdata); > +��� if (unlikely(skb == NULL || st_gdata == > NULL > +��� ��� || st_gdata->tty > == NULL)) { > +��� ��� pr_err("data/tty > unavailable to perform write"); > +��� ��� return > ST_ERR_FAILURE; > +��� } > +#ifdef DEBUG��� ��� > ��� /* open-up skb to read the 1st byte */ > +��� switch (skb->data[0]) { > +��� case HCI_COMMAND_PKT: > +��� case HCI_ACLDATA_PKT: > +��� case HCI_SCODATA_PKT: > +��� ��� protoid = ST_BT; > +��� ��� break; > +��� case ST_FM_CH8_PKT: > +��� ��� protoid = ST_FM; > +��� ��� break; > +��� case 0x09: > +��� ��� protoid = ST_GPS; > +��� ��� break; > +��� } > +��� if (unlikely(st_gdata->list[protoid] > == NULL)) { > +��� ��� pr_err(" protocol %d > not registered, and writing? ", > +��� ��� ��� > ���protoid); > +��� ��� return > ST_ERR_FAILURE; > +��� } > +#endif > +��� pr_info("%d to be written", > skb->len); > +��� len = skb->len; > + > +��� /* st_ll to decide where to enqueue the > skb */ > +��� st_int_enqueue(st_gdata, skb); > +��� /* wake up */ > +��� st_tx_wakeup(st_gdata); > + > +��� /* return number of bytes written */ > +��� return len; > +} > + > +/* for protocols making use of shared transport */ > +EXPORT_SYMBOL_GPL(st_unregister); > + > +/********************************************************************/ > +/* > + * functions called from TTY layer > + */ > +static int st_tty_open(struct tty_struct *tty) > +{ > +��� int err = ST_SUCCESS; > +��� struct st_data_s *st_gdata; > +��� pr_info("%s ", __func__); > + > +��� st_kim_ref(&st_gdata); > +��� st_gdata->tty = tty; > +��� tty->disc_data = st_gdata; > + > +��� /* don't do an wakeup for now */ > +��� clear_bit(TTY_DO_WRITE_WAKEUP, > &tty->flags); > + > +��� /* mem already allocated > +�����*/ > +��� tty->receive_room = 65536; > +��� /* Flush any pending characters in the > driver and discipline. */ > +��� tty_ldisc_flush(tty); > +��� tty_driver_flush_buffer(tty); > +��� /* > +�����* signal to UIM via KIM that > - > +�����* installation of N_TI_WL > ldisc is complete > +�����*/ > +��� st_kim_complete(); > +��� pr_info("done %s", __func__); > +��� return err; > +} > + > +static void st_tty_close(struct tty_struct *tty) > +{ > +��� unsigned char i = ST_MAX; > +��� unsigned long flags = 0; > +��� struct��� st_data_s > *st_gdata = tty->disc_data; > + > +��� pr_info("%s ", __func__); > + > +��� /* TODO: > +�����* if a protocol has been > registered & line discipline > +�����* un-installed for some > reason - what should be done ? > +�����*/ > +��� > spin_lock_irqsave(&st_gdata->lock, flags); > +��� for (i = ST_BT; i < ST_MAX; i++) { > +��� ��� if > (st_gdata->list[i] != NULL) > +��� ��� ��� > pr_err("%d not un-registered", i); > +��� ��� st_gdata->list[i] > = NULL; > +��� } > +��� > spin_unlock_irqrestore(&st_gdata->lock, flags); > +��� /* > +�����* signal to UIM via KIM that > - > +�����* N_TI_WL ldisc is > un-installed > +�����*/ > +��� st_kim_complete(); > +��� st_gdata->tty = NULL; > +��� /* Flush any pending characters in the > driver and discipline. */ > +��� tty_ldisc_flush(tty); > +��� tty_driver_flush_buffer(tty); > + > +��� > spin_lock_irqsave(&st_gdata->lock, flags); > +��� /* empty out txq and tx_waitq */ > +��� > skb_queue_purge(&st_gdata->txq); > +��� > skb_queue_purge(&st_gdata->tx_waitq); > +��� /* reset the TTY Rx states of ST */ > +��� st_gdata->rx_count = 0; > +��� st_gdata->rx_state = > ST_W4_PACKET_TYPE; > +��� kfree_skb(st_gdata->rx_skb); > +��� st_gdata->rx_skb = NULL; > +��� > spin_unlock_irqrestore(&st_gdata->lock, flags); > + > +��� pr_info("%s: done ", __func__); > +} > + > +static void st_tty_receive(struct tty_struct *tty, const > unsigned char *data, > +��� ��� ��� > ���char *tty_flags, int count) > +{ > + > +#ifdef VERBOSE > +��� long i; > +��� printk(KERN_ERR "incoming data...\n"); > +��� for (i = 0; i < count; i++) > +��� ��� printk(" %x", > data[i]); > +��� printk(KERN_ERR "\n.. data end\n"); > +#endif > + > +��� /* > +�����* if fw download is in > progress then route incoming data > +�����* to KIM for validation > +�����*/ > +��� st_recv(tty->disc_data, data, > count); > +��� pr_info("done %s", __func__); > +} > + > +/* wake-up function called in from the TTY layer > + * inside the internal wakeup function will be called > + */ > +static void st_tty_wakeup(struct tty_struct *tty) > +{ > +��� struct��� st_data_s > *st_gdata = tty->disc_data; > +��� pr_info("%s ", __func__); > +��� /* don't do an wakeup for now */ > +��� clear_bit(TTY_DO_WRITE_WAKEUP, > &tty->flags); > + > +��� /* call our internal wakeup */ > +��� st_tx_wakeup((void *)st_gdata); > +} > + > +static void st_tty_flush_buffer(struct tty_struct *tty) > +{ > +��� struct��� st_data_s > *st_gdata = tty->disc_data; > +��� pr_info("%s ", __func__); > + > +��� kfree_skb(st_gdata->tx_skb); > +��� st_gdata->tx_skb = NULL; > + > +��� tty->ops->flush_buffer(tty); > +��� return; > +} > + > +/********************************************************************/ > +int st_core_init(struct st_data_s **core_data) > +{ > +��� struct st_data_s *st_gdata; > +��� long err; > +��� static struct tty_ldisc_ops > *st_ldisc_ops; > + > +��� /* populate and register to TTY line > discipline */ > +��� st_ldisc_ops = > kzalloc(sizeof(*st_ldisc_ops), GFP_KERNEL); > +��� if (!st_ldisc_ops) { > +��� ��� pr_err("no mem to > allocate"); > +��� ��� return -ENOMEM; > +��� } > + > +��� st_ldisc_ops->magic = > TTY_LDISC_MAGIC; > +��� st_ldisc_ops->name = > "n_st";��� /*"n_hci"; */ > +��� st_ldisc_ops->open = st_tty_open; > +��� st_ldisc_ops->close = st_tty_close; > +��� st_ldisc_ops->receive_buf = > st_tty_receive; > +��� st_ldisc_ops->write_wakeup = > st_tty_wakeup; > +��� st_ldisc_ops->flush_buffer = > st_tty_flush_buffer; > +��� st_ldisc_ops->owner = THIS_MODULE; > + > +��� err = tty_register_ldisc(N_TI_WL, > st_ldisc_ops); > +��� if (err) { > +��� ��� pr_err("error > registering %d line discipline %ld", > +��� ��� ��� > ���N_TI_WL, err); > +��� ��� > kfree(st_ldisc_ops); > +��� ��� return err; > +��� } > +��� pr_info("registered n_shared line > discipline"); > + > +��� st_gdata = kzalloc(sizeof(struct > st_data_s), GFP_KERNEL); > +��� if (!st_gdata) { > +��� ��� pr_err("memory > allocation failed"); > +��� ��� err = > tty_unregister_ldisc(N_TI_WL); > +��� ��� if (err) > +��� ��� ��� > pr_err("unable to un-register ldisc %ld", err); > +��� ��� > kfree(st_ldisc_ops); > +��� ��� err = -ENOMEM; > +��� ��� return err; > +��� } > + > +��� /* Initialize ST TxQ and Tx waitQ queue > head. All BT/FM/GPS module skb's > +�����* will be pushed in this > queue for actual transmission. > +�����*/ > +��� > skb_queue_head_init(&st_gdata->txq); > +��� > skb_queue_head_init(&st_gdata->tx_waitq); > + > +��� /* Locking used in st_int_enqueue() to > avoid multiple execution */ > +��� > spin_lock_init(&st_gdata->lock); > + > +��� /* ldisc_ops ref to be only used in > __exit of module */ > +��� st_gdata->ldisc_ops = st_ldisc_ops; > + > +#if 0 > +��� err = st_kim_init(); > +��� if (err) { > +��� ��� pr_err("error during > kim initialization(%ld)", err); > +��� ��� kfree(st_gdata); > +��� ��� err = > tty_unregister_ldisc(N_TI_WL); > +��� ��� if (err) > +��� ��� ��� > pr_err("unable to un-register ldisc"); > +��� ��� > kfree(st_ldisc_ops); > +��� ��� return -1; > +��� } > +#endif > + > +��� err = st_ll_init(st_gdata); > +��� if (err) { > +��� ��� pr_err("error during > st_ll initialization(%ld)", err); > +��� ��� kfree(st_gdata); > +��� ��� err = > tty_unregister_ldisc(N_TI_WL); > +��� ��� if (err) > +��� ��� ��� > pr_err("unable to un-register ldisc"); > +��� ��� > kfree(st_ldisc_ops); > +��� ��� return -1; > +��� } > +��� *core_data = st_gdata; > +��� return 0; > +} > + > +void st_core_exit(struct st_data_s *st_gdata) > +{ > +��� long err; > +��� /* internal module cleanup */ > +��� err = st_ll_deinit(st_gdata); > +��� if (err) > +��� ��� pr_err("error during > deinit of ST LL %ld", err); > +#if 0 > +��� err = st_kim_deinit(); > +��� if (err) > +��� ��� pr_err("error during > deinit of ST KIM %ld", err); > +#endif > +��� if (st_gdata != NULL) { > +��� ��� /* Free ST Tx Qs and > skbs */ > +��� ��� > skb_queue_purge(&st_gdata->txq); > +��� ��� > skb_queue_purge(&st_gdata->tx_waitq); > +��� ��� > kfree_skb(st_gdata->rx_skb); > +��� ��� > kfree_skb(st_gdata->tx_skb); > +��� ��� /* TTY ldisc cleanup > */ > +��� ��� err = > tty_unregister_ldisc(N_TI_WL); > +��� ��� if (err) > +��� ��� ��� > pr_err("unable to un-register ldisc %ld", err); > +��� ��� > kfree(st_gdata->ldisc_ops); > +��� ��� /* free the global > data pointer */ > +��� ��� kfree(st_gdata); > +��� } > +} > + > + > diff --git a/drivers/staging/ti-st/st_core.h > b/drivers/staging/ti-st/st_core.h > new file mode 100644 > index 0000000..f271c88 > --- /dev/null > +++ b/drivers/staging/ti-st/st_core.h > @@ -0,0 +1,98 @@ > +/* > + *� Shared Transport Core header file > + * > + *� Copyright (C) 2009 Texas Instruments > + * > + *� 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. > + * > + *� This program is distributed in the hope that it > will be useful, > + *� but WITHOUT ANY WARRANTY; without even the > implied warranty of > + *� MERCHANTABILITY or FITNESS FOR A PARTICULAR > PURPOSE.� See the > + *� GNU General Public License for more details. > + * > + *� You should have received a copy of the GNU > General Public License > + *� along with this program; if not, write to the > Free Software > + *� Foundation, Inc., 59 Temple Place, Suite 330, > Boston, MA� 02111-1307� USA > + * > + */ > + > +#ifndef ST_CORE_H > +#define ST_CORE_H > + > +#include <linux/skbuff.h> > +#include "st.h" > + > +/* states of protocol list */ > +#define ST_NOTEMPTY��� 1 > +#define ST_EMPTY��� 0 > + > +/* > + * possible st_states > + */ > +#define ST_INITIALIZING��� > ��� 1 > +#define ST_REG_IN_PROGRESS��� 2 > +#define ST_REG_PENDING��� > ��� 3 > +#define ST_WAITING_FOR_RESP��� 4 > + > +/* > + * local data required for ST/KIM/ST-HCI-LL > + */ > +struct st_data_s { > +��� unsigned long st_state; > +/* > + * an instance of tty_struct & ldisc ops to move > around > + */ > +��� struct tty_struct *tty; > +��� struct tty_ldisc_ops *ldisc_ops; > +/* > + * the tx skb - > + * if the skb is already dequeued and the tty failed to > write the same > + * maintain the skb to write in the next transaction > + */ > +��� struct sk_buff *tx_skb; > +#define ST_TX_SENDING��� 1 > +#define ST_TX_WAKEUP��� 2 > +��� unsigned long tx_state; > +/* > + * list of protocol registered > + */ > +��� struct st_proto_s *list[ST_MAX]; > +/* > + * lock > + */ > +��� unsigned long rx_state; > +��� unsigned long rx_count; > +��� struct sk_buff *rx_skb; > +��� struct sk_buff_head txq, tx_waitq; > +��� spinlock_t lock;��� /* > ST LL state lock� */ > +��� unsigned char��� > protos_registered; > +��� unsigned long > ll_state;��� /* ST LL power state */ > +}; > + > +/* point this to tty->driver->write or > tty->ops->write > + * depending upon the kernel version > + */ > +int st_int_write(struct st_data_s*, const unsigned char*, > int); > +/* internal write function, passed onto protocol drivers > + * via the write function ptr of protocol struct > + */ > +long st_write(struct sk_buff *); > +/* function to be called from ST-LL > + */ > +void st_ll_send_frame(enum proto_type, struct sk_buff *); > +/* internal wake up function */ > +void st_tx_wakeup(struct st_data_s *st_data); > + > +int st_core_init(struct st_data_s **); > +void st_core_exit(struct st_data_s *); > +void st_kim_ref(struct st_data_s **); > + > +#define GPS_STUB_TEST > +#ifdef GPS_STUB_TEST > +int gps_chrdrv_stub_write(const unsigned char*, int); > +void gps_chrdrv_stub_init(void); > +#endif > + > +#endif /*ST_CORE_H */ > -- > 1.5.4.3 > > Any comments on these patches ? The INTERNET now has a personality. YOURS! See your Yahoo! Homepage. http://in.yahoo.com/ -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo(a)vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
First
|
Prev
|
Next
|
Last
Pages: 1 2 3 4 5 Prev: [PATCH] rcu: more careful check for the last non-dyntick-idle CPU Next: [PATCH -v10 00/29] use lmb with x86 |